Created
January 30, 2009 12:11
-
-
Save azu/55041 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name favlist++ | |
| // @namespace http://web.zgo.jp/ | |
| // @include http://www.nicovideo.jp/mylist/* | |
| // @include http://www.nicovideo.jp/user/* | |
| // @include http://www.nicovideo.jp/myvideo/* | |
| // @include http://www.nicovideo.jp/my* | |
| // ==/UserScript== | |
| (function () { | |
| var w = (this.unsafeWindow || window), document = w.document; | |
| var NicoNicoFavlist = { | |
| version: "1.16", | |
| getUserAgent: function () { | |
| return "NicoNicoFavlist/" + NicoNicoFavlist.version + " Greasemonkey"; | |
| } | |
| }; | |
| var Util = { | |
| observe: function (elem, event, func, capture) { | |
| capture = !!capture; | |
| if (elem.attachEvent) { | |
| elem.attachEvent("on" + event, func); | |
| } else if (elem.addEventListener) { | |
| elem.addEventListener(event, func, capture); | |
| } else { | |
| elem["on" + event] = func; | |
| } | |
| } | |
| }; | |
| var Config = { | |
| checkInterval: 30 * 60, | |
| maxNewVideos: 10, | |
| hideCheckedList: false, | |
| showInAllTabs: false, | |
| show: function (elem) { | |
| var ul = document.createElement("ul"); | |
| ul.style.margin = "0px"; | |
| ul.style.padding = "8px"; | |
| ul.style.listStyleType = "none"; | |
| this.addTextInput(ul, "更新チェック間隔", "favlistCheckInterval", Config.checkInterval, "10", "秒"); | |
| this.addTextInput(ul, "新着動画数の上限", "favlistMaxNewVideos", Config.maxNewVideos, "10", "件まで"); | |
| this.addCheckBox(ul, "新着がないマイリストを隠す", "favlistHideCheckedList", Config.hideCheckedList); | |
| this.addCheckBox(ul, "カテゴリをまたいで表示", "favlistShowInAllTabs", Config.showInAllTabs); | |
| elem.appendChild(ul); | |
| }, | |
| addTextInput: function (elem, label, name, value, size, unit) { | |
| var li = document.createElement("li"); | |
| li.className = "TXT12"; | |
| li.style.fontWeight = "bold"; | |
| li.style.marginBottom = "8px"; | |
| li.innerHTML = '<label for="'+name+'">'+label+':</label><br>'; | |
| var input = document.createElement("input"); | |
| input.type = "text"; | |
| input.id = name; | |
| input.name = name; | |
| input.value = value; | |
| input.size = size; | |
| li.appendChild(input); | |
| if (unit) { | |
| var span = document.createElement("span"); | |
| span.innerHTML = unit; | |
| span.style.marginLeft = "4px"; | |
| li.appendChild(span); | |
| } | |
| elem.appendChild(li); | |
| }, | |
| addCheckBox: function (elem, label, name, value) { | |
| var li = document.createElement("li"); | |
| li.className = "TXT12"; | |
| li.style.fontWeight = "bold"; | |
| li.style.marginBottom = "4px"; | |
| li.innerHTML = '<label for="'+name+'">'+label+'</label>'; | |
| var check = document.createElement("input"); | |
| check.type = "checkbox"; | |
| check.id = name; | |
| check.name = name; | |
| check.value = "1"; | |
| check.checked = !!value; | |
| li.insertBefore(check, li.firstChild); | |
| elem.appendChild(li); | |
| }, | |
| load: function () { | |
| var checkInterval = GM_getValue("checkInterval"); | |
| if (checkInterval !== undefined) Config.checkInterval = checkInterval; | |
| var maxNewVideos = GM_getValue("maxNewVideos"); | |
| if (maxNewVideos !== undefined) Config.maxNewVideos = maxNewVideos; | |
| var hideCheckedList = GM_getValue("hideCheckedList"); | |
| if (hideCheckedList !== undefined) Config.hideCheckedList = hideCheckedList; | |
| var showInAllTabs = GM_getValue("showInAllTabs"); | |
| if (showInAllTabs !== undefined) Config.showInAllTabs = showInAllTabs; | |
| }, | |
| save: function () { | |
| var interval = document.getElementById("favlistCheckInterval").value; | |
| try { | |
| interval = w.parseInt(interval); | |
| if (interval < 0) interval = 0; | |
| Config.checkInterval = interval; | |
| GM_setValue("checkInterval", interval); | |
| } catch (e) { | |
| w.alert("更新チェック間隔の値がおかしいです"); | |
| return; | |
| } | |
| var maxnewvideos = document.getElementById("favlistMaxNewVideos").value; | |
| try { | |
| maxnewvideos = w.parseInt(maxnewvideos); | |
| if (maxnewvideos < 0) maxnewvideos = 0; | |
| Config.maxNewVideos = maxnewvideos; | |
| GM_setValue("maxNewVideos", maxnewvideos); | |
| } catch (e) { | |
| w.alert("新着動画数の上限の値がおかしいです"); | |
| return; | |
| } | |
| var hidechedkedlist = document.getElementById("favlistHideCheckedList").checked; | |
| Config.hideCheckedList = hidechedkedlist; | |
| GM_setValue("hideCheckedList", hidechedkedlist); | |
| var showInAllTabs = document.getElementById("favlistShowInAllTabs").checked; | |
| Config.showInAllTabs = showInAllTabs; | |
| GM_setValue("showInAllTabs", showInAllTabs); | |
| favlist.switchTab(0); | |
| } | |
| }; | |
| var Video = function () { this.initialize.apply(this, arguments); }; | |
| Video.prototype = { | |
| initialize: function (mylist, id, title, uri, thumbnail, memo, timestamp) { | |
| this.mylist = mylist; | |
| this.id = id || false; | |
| this.title = title || false; | |
| this.uri = uri || false; | |
| this.thumbnail = thumbnail || false; | |
| this.memo = memo || false; | |
| this.timestamp = timestamp || false; | |
| this.container = false; | |
| }, | |
| serialize: function () { | |
| return [ | |
| this.id ? w.escape(this.id) : "", | |
| this.title ? w.escape(this.title) : "", | |
| this.uri ? w.escape(this.uri) : "", | |
| this.thumbnail ? w.escape(this.thumbnail) : "", | |
| this.memo ? w.escape(this.memo) : "", | |
| this.timestamp ? w.escape(this.timestamp) : "" | |
| ].join("&"); | |
| }, | |
| unserialize: function (data) { | |
| var r = []; | |
| if (data) r = data.split(/&/); | |
| this.id = r[0] ? w.unescape(r[0]) : false; | |
| this.title = r[1] ? w.unescape(r[1]) : false; | |
| this.uri = r[2] ? w.unescape(r[2]) : false; | |
| this.thumbnail = r[3] ? w.unescape(r[3]) : false; | |
| this.memo = r[4] ? w.unescape(r[4]) : false; | |
| this.timestamp = r[5] ? w.unescape(r[5]) : false; | |
| }, | |
| updateByAtomEntry: function (entry) { | |
| var m; | |
| if (m = entry.match(/<title>(.*?)<\/title>/)) this.title = m[1]; | |
| if (m = entry.match(/<link rel="alternate" type="text\/html" href="(.+?)"\/>/)) this.uri = m[1]; | |
| if (this.uri && (m = this.uri.match(/watch\/(.+)/))) this.id = m[1]; | |
| if (m = entry.match(/<img alt=".*?" src="(.+?)"/)) this.thumbnail = m[1]; | |
| if (m = entry.match(/<p class="nico-memo">(.*?)<\/p>/)) this.memo = m[1]; | |
| if (m = entry.match(/<published>(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).*?<\/published>/)) { | |
| this.timestamp = m[1]+"年"+m[2]+"月"+m[3]+"日 "+m[4]+":"+m[5]+":"+m[6]; | |
| } | |
| }, | |
| getCaption: function () { | |
| var caption = this.title || "(無題)"; | |
| if (caption.length > 26) { | |
| caption = caption.substr(0, 13) + "..." + caption.substr(-13); | |
| } | |
| return caption; | |
| }, | |
| show: function (elem) { | |
| var li = document.createElement("li"); | |
| li.style.clear = "both"; | |
| li.style.padding = "4px 0px"; | |
| li.style.borderTop = "1px dotted #CCC"; | |
| var self = this; | |
| var clearance = function () { self.mylist.clear(self); }; | |
| var thumbLink = document.createElement("a"); | |
| thumbLink.href = this.uri; | |
| thumbLink.title = this.title; | |
| try { thumbLink.style.cssFloat = "left"; } | |
| catch (e) { thumbLink.style.styleFloat = "left"; } | |
| thumbLink.style.marginRight = "4px"; | |
| Util.observe(thumbLink, "click", clearance); | |
| var thumb = document.createElement("img"); | |
| thumb.className = "thumb_img"; | |
| thumb.src = this.thumbnail; | |
| thumb.width = "46"; | |
| thumb.height = "34"; | |
| thumbLink.appendChild(thumb); | |
| li.appendChild(thumbLink); | |
| if (this.timestamp) { | |
| var time = document.createElement("p"); | |
| var m = this.timestamp.split(/\D+/); | |
| time.innerHTML = "<strong>" + | |
| m[0].substr(-2) + "/" + m[1] + "/" + m[2] + " " + | |
| m[3] + ":" + m[4] + ":" + m[5] + "</strong> 追加"; | |
| time.className = "TXT10"; | |
| li.appendChild(time); | |
| } | |
| var title = document.createElement("p"); | |
| title.className = "TXT12"; | |
| var titleLink = document.createElement("a"); | |
| titleLink.href = this.uri; | |
| titleLink.title = this.title; | |
| titleLink.className = "video"; | |
| titleLink.innerHTML = this.getCaption(); | |
| Util.observe(titleLink, "click", clearance); | |
| title.appendChild(titleLink); | |
| li.appendChild(title); | |
| var breaker = document.createElement("p"); | |
| breaker.style.clear = "both"; | |
| breaker.innerHTML = '<img src="img/_.gif" width="1" height="1">'; | |
| li.appendChild(breaker); | |
| if (this.memo) { | |
| var memo = document.createElement("p"); | |
| memo.innerHTML = this.memo; | |
| memo.className = "TXT12"; | |
| memo.style.backgroundColor = "#F7F7F7"; | |
| memo.style.border = "1px solid #CCC"; | |
| memo.style.padding = "4px"; | |
| memo.style.marginTop = "4px"; | |
| li.appendChild(memo); | |
| } | |
| elem.appendChild(li); | |
| this.container = li; | |
| }, | |
| remove: function () { | |
| if (this.container) { | |
| this.container.parentNode.removeChild(this.container); | |
| this.container = false; | |
| } | |
| } | |
| }; | |
| var Mylist = function () { this.initialize.apply(this, arguments); }; | |
| Mylist.prototype = { | |
| baseUri: "http://www.nicovideo.jp", | |
| initialize: function (favlist, listId, title) { | |
| this.favlist = favlist; | |
| this.listId = listId || false; | |
| this.title = title || false; | |
| this.checked = { }; | |
| this.newVideos = [ ]; | |
| this.caption = false; | |
| this.container = false; | |
| this.videoList = false; | |
| this.titleBar = false; | |
| this.counter = false; | |
| this.statusBar = false; | |
| this.buttons = false; | |
| this.toRemove = false; | |
| }, | |
| serialize: function () { | |
| var checked = [ ]; | |
| for (var k in this.checked) { checked.push(k); } | |
| var newVideos = [ ]; | |
| for (var i = 0, len = this.newVideos.length; i < len; i++) { | |
| newVideos.push(this.newVideos[i].serialize()); | |
| } | |
| return [ | |
| "0", /* for backward compatibility */ | |
| this.listId ? w.escape(this.listId) : "", | |
| this.title ? w.escape(this.title) : "", | |
| checked.join(":"), | |
| newVideos.join(":"), | |
| this.caption ? w.escape(this.caption) : "" | |
| ].join(";"); | |
| }, | |
| unserialize: function (data) { | |
| var r = []; | |
| if (data) r = data.split(/;/); | |
| // r[0] is userId, but not in use | |
| this.listId = r[1] ? w.unescape(r[1]) : false; | |
| if (/^\d+$/.test(this.listId)) this.listId = "mylist/" + this.listId; | |
| this.title = r[2] ? w.unescape(r[2]) : false; | |
| this.checked = { }; | |
| if (r[3]) { | |
| var checked = r[3].split(/:/); | |
| for (var i = 0; i < checked.length; i++) { | |
| if (checked[i]) { | |
| this.checked[ checked[i] ] = true; | |
| } | |
| } | |
| } | |
| this.newVideos = [ ]; | |
| if (r[4]) { | |
| var vids = r[4].split(/:/); | |
| for (var i = 0; i < vids.length; i++) { | |
| if (vids[i]) { | |
| var v = new Video(this); | |
| v.unserialize(vids[i]); | |
| this.newVideos.push(v); | |
| } | |
| } | |
| } | |
| this.caption = r[5] ? w.unescape(r[5]) : false; | |
| }, | |
| getUri: function () { | |
| return this.baseUri + "/" + this.listId; | |
| }, | |
| getCaption: function () { | |
| var caption = this.caption || this.title || "(無題)"; | |
| if (caption.length > 26) { | |
| caption = caption.substr(0, 13) + "..." + caption.substr(-13); | |
| } | |
| return caption; | |
| }, | |
| update: function () { | |
| this.setStatus("更新中", "#333"); | |
| this.newVideos = [ ]; | |
| this.videoList.innerHTML = ""; | |
| var self = this; | |
| GM_xmlhttpRequest({ | |
| method: "GET", | |
| url: this.getUri() + "?rss=atom&nodescription=1&noinfo=1&sort=1", | |
| headers: { "User-Agent": NicoNicoFavlist.getUserAgent() }, | |
| onload: function (r) { | |
| self.setStatus(false); | |
| if (200 <= r.status && r.status < 300) { | |
| self.updateByAtom(r.responseText); | |
| } else if (r.status == 403) { | |
| self.setStatus("非公開", "#C00", 3000); | |
| } | |
| }, | |
| onerror: function (r) { | |
| self.setStatus("更新失敗", "#C00", 3000); | |
| }, | |
| onreadystatechange: function (r) { | |
| if (r.readyState == 4) { | |
| self.favlist.updateCallback(this); | |
| } | |
| } | |
| }); | |
| }, | |
| updateByAtom: function (xml) { | |
| var m; | |
| if (m = xml.match(/<title>(?:マイリスト )?(.+?)‐ニコニコ動画.*?<\/title>/)) { | |
| this.title = m[1]; | |
| } | |
| var oldChecked = this.checked; | |
| this.checked = { }; | |
| var re_entry = /<entry>([\S\s]*?)<\/entry>/g; | |
| while (m = re_entry.exec(xml)) { | |
| var v = new Video(this); | |
| v.updateByAtomEntry(m[1]); | |
| if (v.id in oldChecked) { | |
| this.checked[v.id] = true; | |
| } else { | |
| this.newVideos.push(v); | |
| } | |
| } | |
| this.updateTitleBar(); | |
| this.updateVideoList(); | |
| this.favlist.save(); | |
| }, | |
| updateTitleBar: function () { | |
| if (this.titleBar && this.titleBar.tagName.toUpperCase() == "A") { | |
| this.titleBar.title = this.title; | |
| this.titleBar.innerHTML = this.getCaption(); | |
| if (this.newVideos.length > 0) { | |
| this.titleBar.style.fontWeight = "bold"; | |
| this.counter.innerHTML = "(" + this.newVideos.length + ")"; | |
| this.counter.style.display = ""; | |
| this.container.style.display = ""; | |
| } else { | |
| this.titleBar.style.fontWeight = ""; | |
| this.counter.style.display = "none"; | |
| if (Config.hideCheckedList && this.statusBar.style.display == "none") { | |
| this.container.style.display = "none"; | |
| } | |
| } | |
| } | |
| }, | |
| updateVideoList: function () { | |
| if (!this.videoList) return; | |
| this.videoList.innerHTML = ""; | |
| var len = this.newVideos.length;//新着のサムネ数 | |
| if (0 < Config.maxNewVideos && Config.maxNewVideos < len) { | |
| len = Config.maxNewVideos; | |
| } | |
| for (var i = 0; i < len; i++) { | |
| this.newVideos[i].show(this.videoList); | |
| } | |
| }, | |
| show: function (elem, editting) { | |
| var div = document.createElement("div"); | |
| div.style.clear = "both"; | |
| div.style.padding = "4px 0px"; | |
| div.style.borderBottom = "1px solid #999"; | |
| this.container = div; | |
| var buttons = document.createElement("p"); | |
| try { buttons.style.cssFloat = "right"; } | |
| catch (e) { buttons.style.styleFloat = "right"; } | |
| div.appendChild(buttons); | |
| this.buttons = buttons; | |
| var self = this; | |
| this.toRemove = false; | |
| this.addButton(buttons, "削除", function () { | |
| self.toRemove = true; | |
| self.container.parentNode.removeChild(self.container); | |
| self.container = false; | |
| }); | |
| if (!editting) { | |
| this.addButton(buttons, "クリア", function () { self.clearAll(); }); | |
| } | |
| var status = document.createElement("p"); | |
| status.className = "TXT12"; | |
| try { status.style.cssFloat = "right"; } | |
| catch (e) { status.style.styleFloat = "right"; } | |
| status.style.color = "#FFF"; | |
| status.style.backgroundColor = "#666"; | |
| status.style.fontWeight = "bold"; | |
| status.style.padding = "0px 4px"; | |
| status.style.lineHeight = "22px"; | |
| status.style.display = "none"; | |
| div.appendChild(status); | |
| this.statusBar = status; | |
| var h = document.createElement("p"); | |
| h.className = "TXT12"; | |
| h.style.padding = "2px 0px"; | |
| div.appendChild(h); | |
| if (editting) { | |
| var input = document.createElement("input"); | |
| input.type = "text"; | |
| input.value = this.caption || this.title; | |
| input.style.width = "170px"; | |
| h.appendChild(input); | |
| this.titleBar = input; | |
| this.counter = null; | |
| } else { | |
| var a = document.createElement("a"); | |
| a.href = this.getUri(); | |
| a.title = this.title; | |
| a.innerHTML = this.getCaption(); | |
| h.appendChild(a); | |
| this.titleBar = a; | |
| var counter = document.createElement("span"); | |
| counter.style.color = "#F33"; | |
| counter.style.fontWeight = "bold"; | |
| counter.style.marginLeft = "4px"; | |
| counter.style.display = "none"; | |
| h.appendChild(counter); | |
| this.counter = counter; | |
| } | |
| var breaker = document.createElement("p"); | |
| breaker.style.clear = "both"; | |
| breaker.innerHTML = '<img src="img/_.gif" width="1" height="1">'; | |
| div.appendChild(breaker); | |
| if (editting) { | |
| this.videoList = null; | |
| } else { | |
| var ul = document.createElement("ul"); | |
| ul.style.clear = "both"; | |
| ul.style.listStyleType = "none"; | |
| ul.style.margin = "0px"; | |
| ul.style.padding = "0px"; | |
| div.appendChild(ul); | |
| this.videoList = ul; | |
| } | |
| this.updateTitleBar(); | |
| this.updateVideoList(); | |
| elem.appendChild(div); | |
| }, | |
| showConfig: function (elem) { | |
| this.show(elem, true); | |
| }, | |
| addButton: function (elem, caption, func) { | |
| var btn = document.createElement("input"); | |
| btn.type = "button"; | |
| btn.value = caption; | |
| btn.className = "submit"; | |
| btn.style.marginLeft = "4px"; | |
| Util.observe(btn, "click", func); | |
| elem.appendChild(btn); | |
| return btn; | |
| }, | |
| saveConfig: function () { | |
| if (this.toRemove) { | |
| this.remove(); | |
| return; | |
| } | |
| if (!this.titleBar.value || this.titleBar.value == this.title) { | |
| this.caption = false; | |
| } else { | |
| this.caption = this.titleBar.value; | |
| } | |
| if (this.caption != this.titleBar.value) { | |
| this.caption = this.titleBar.value; | |
| } | |
| }, | |
| remove: function () { | |
| this.favlist.remove(this.listId); | |
| if (this.container) { | |
| this.container.parentNode.removeChild(this.container); | |
| this.container = false; | |
| } | |
| }, | |
| clear: function (video) { | |
| if (!video) return; | |
| for (var i = 0, len = this.newVideos.length; i < len; i++) { | |
| if (this.newVideos[i] == video) { | |
| this.newVideos.splice(i, 1); | |
| } | |
| } | |
| video.remove(); | |
| this.checked[video.id] = true; | |
| this.updateTitleBar(); | |
| this.favlist.save(); | |
| }, | |
| clearAll: function () { | |
| for (var i = 0, len = this.newVideos.length; i < len; i++) { | |
| var v = this.newVideos[i]; | |
| v.remove(); | |
| this.checked[v.id] = true; | |
| } | |
| this.newVideos = [ ]; | |
| this.updateTitleBar(); | |
| this.favlist.save(); | |
| }, | |
| setStatus: function (status, color, timeout) { | |
| if (status) { | |
| this.statusBar.innerHTML = status; | |
| this.statusBar.style.backgroundColor = color; | |
| this.statusBar.style.display = ""; | |
| this.buttons.style.display = "none"; | |
| this.container.style.display = ""; | |
| if (timeout) { | |
| var self = this; | |
| w.setTimeout(function () { self.setStatus(false); }, timeout); | |
| } | |
| } else { | |
| this.statusBar.style.display = "none"; | |
| this.buttons.style.display = ""; | |
| if (Config.hideCheckedList && this.newVideos.length == 0) { | |
| this.container.style.display = "none"; | |
| } | |
| } | |
| } | |
| }; | |
| var Favlist = function () { this.initialize.apply(this, arguments); }; | |
| Favlist.prototype = { | |
| initialize: function () { | |
| this.id = arguments[0] || "favlist"; | |
| this.list = { }; | |
| this.tabs = [ ]; | |
| this.container = false; | |
| this.updateAllButton = false; | |
| this.updateQueue = [ ]; | |
| }, | |
| save: function () { | |
| GM_setValue(this.id, this.serialize()); | |
| }, | |
| load: function () { | |
| this.unserialize(GM_getValue(this.id)); | |
| }, | |
| serialize: function () { | |
| var data = []; | |
| for (var k in this.list) { | |
| data.push(this.list[k].serialize()); | |
| } | |
| return data.join("#"); | |
| }, | |
| unserialize: function (data) { | |
| this.list = { }; | |
| if (data) { | |
| data = data.split(/#/); | |
| for (var i = 0; i < data.length; i ++) { | |
| var ml = new Mylist(this); | |
| ml.unserialize(data[i]); | |
| this.list[ml.listId] = ml; | |
| } | |
| } | |
| }, | |
| get: function (listId) { | |
| return this.list[listId]; | |
| }, | |
| add: function (listId, title) { | |
| var ml = new Mylist(this, listId, title); | |
| this.list[listId] = ml; | |
| this.save(); | |
| }, | |
| remove: function (listId) { | |
| var ml = this.list[listId]; | |
| if (ml) { | |
| delete this.list[listId]; | |
| this.save(); | |
| } | |
| }, | |
| updateAll: function () { | |
| var first = false; | |
| for (var k in this.list) { | |
| if (!first) { | |
| first = this.list[k]; | |
| } else { | |
| this.updateQueue.push(this.list[k]); | |
| this.list[k].setStatus("待機中", "#CCC"); | |
| } | |
| } | |
| if (first) { | |
| if (this.updateAllButton) { | |
| this.updateAllButton.disabled = true; | |
| } | |
| first.update(); | |
| } | |
| }, | |
| updateCallback: function (ml) { | |
| if (this.updateQueue.length > 0) { | |
| var ml = this.updateQueue.shift(); | |
| ml.update(); | |
| } else { | |
| if (this.updateAllButton) { | |
| this.updateAllButton.disabled = false; | |
| } | |
| } | |
| }, | |
| clearByVideoId: function (videoId) { | |
| var toRemove; | |
| if (videoId instanceof Array) { | |
| var ids = { }; | |
| for (var i = 0, len = videoId.length; i < len; i++) { | |
| ids[videoId[i]] = true; | |
| } | |
| toRemove = function (id) { | |
| return ids[id]; | |
| } | |
| } else { | |
| toRemove = function (id) { | |
| return (videoId == id); | |
| } | |
| } | |
| var changed = false; | |
| for (var k in this.list) { | |
| var mylist = this.list[k], videos = mylist.newVideos; | |
| var mylist_changed = false; | |
| for (var i = videos.length - 1; i >= 0; i--) { | |
| var video = videos[i]; | |
| if (toRemove(video.id)) { | |
| video.remove(); | |
| mylist.checked[video.id] = true; | |
| videos.splice(i, 1); | |
| changed = true; | |
| mylist_changed = true; | |
| } | |
| } | |
| if (mylist_changed) mylist.updateTitleBar(); | |
| } | |
| if (changed) favlist.save(); | |
| }, | |
| addToPlaylist: function () { | |
| if (typeof w.gm_playlistController == "undefined") return; | |
| var addVideos = []; | |
| for (var k in this.list) { | |
| var mylist = this.list[k], videos = mylist.newVideos; | |
| for (var i = 0, len = videos.length; i < len; i++) { | |
| addVideos.push(videos[i]); | |
| } | |
| mylist.clearAll(); | |
| } | |
| if (addVideos.length > 0) { | |
| w.gm_playlistController.pushVideos(addVideos); | |
| } | |
| }, | |
| show: function (elem) { | |
| var div = document.createElement("div"); | |
| div.className = "mb16p4"; | |
| div.style.position = "relative"; | |
| var h = document.createElement("h2"); | |
| div.appendChild(h); | |
| var self = this; | |
| this.tabs = []; | |
| this.addTab(h, "一覧", function () { self.showList(); }); | |
| this.addTab(h, "設定", function () { self.showConfig(); }); | |
| var span = document.createElement("span"); | |
| span.innerHTML = "favlist"; | |
| span.style.display = "block"; | |
| span.style.borderWidth = "2px"; | |
| span.style.borderColor = "#FFF #FFF #333 #FFF"; | |
| span.style.borderStyle = "solid"; | |
| h.appendChild(span); | |
| var container = document.createElement("div"); | |
| container.style.clear = "both"; | |
| container.style.position = "relative"; | |
| container.style.paddingTop = "4px"; | |
| div.appendChild(container); | |
| this.container = container; | |
| elem.insertBefore(div, elem.firstChild); | |
| this.switchTab(0); | |
| }, | |
| addTab: function (elem, caption, func) { | |
| var tab = document.createElement("a"); | |
| tab.href = "javascript:void(0);"; | |
| tab.innerHTML = caption; | |
| tab.style.display = "block"; | |
| tab.style.textAlign = "center"; | |
| tab.style.textDecoration = "none"; | |
| try { | |
| tab.style.cssFloat = "right"; | |
| } catch (e) { | |
| tab.style.styleFloat = "right"; | |
| } | |
| tab.style.width = "3em"; | |
| tab.style.color = "#333"; | |
| tab.style.backgroundColor = "#FFF"; | |
| tab.style.borderWidth = "2px"; | |
| tab.style.borderColor = "#FFF #FFF #333 #FFF"; | |
| tab.style.borderStyle = "solid"; | |
| elem.insertBefore(tab, elem.firstChild); | |
| this.tabs.push({ | |
| tab: tab, | |
| func: func | |
| }); | |
| var self = this; | |
| Util.observe(tab, "click", function () { self.switchTab(tab); }); | |
| return tab; | |
| }, | |
| switchTab: function (selectTab) { | |
| if (this.updateQueue.length > 0) return; | |
| if (typeof selectTab == "number") selectTab = this.tabs[selectTab].tab; | |
| var func = false; | |
| for (var i = 0, len = this.tabs.length; i < len; i++) { | |
| var tab = this.tabs[i].tab; | |
| if (tab == selectTab) { | |
| tab.style.borderColor = "#333 #333 #FFF #333"; | |
| func = this.tabs[i].func; | |
| } else { | |
| tab.style.borderColor = "#FFF #FFF #333 #FFF"; | |
| } | |
| } | |
| if (func) func(); | |
| }, | |
| showList: function () { | |
| this.container.innerHTML = ""; | |
| for (var k in this.list) { | |
| this.list[k].show(this.container); | |
| } | |
| var buttons = document.createElement("p"); | |
| buttons.style.clear = "both"; | |
| buttons.style.paddingTop = "4px"; | |
| var self = this; | |
| this.updateAllButton = this.addButton(buttons, "いますぐ更新", function () { self.updateAll(); }); | |
| var checkPlaylist = function () { | |
| if (typeof w.gm_playlistController != "undefined") { | |
| var b = self.addButton(buttons, "プレイリストに移動", function () { self.addToPlaylist(); }); | |
| b.style.marginLeft = "5px"; | |
| return true; | |
| } | |
| } | |
| var saveButton = document.createElement("input"); | |
| saveButton.className = "submit"; | |
| saveButton.type = "button"; | |
| saveButton.value = "保存"; | |
| saveButton.style.width = "5em"; | |
| saveButton.style.fontWeight = "bold"; | |
| var self = this; | |
| Util.observe(saveButton, "click", function () { | |
| for (var k in self.list) { | |
| self.list[k].saveConfig(); | |
| } | |
| self.save(); | |
| Config.save(); | |
| }); | |
| checkPlaylist() || setTimeout(checkPlaylist, 1); | |
| this.container.appendChild(buttons); | |
| this.container.appendChild(saveButton); | |
| }, | |
| addButton: function (elem, caption, func) { | |
| var btn = document.createElement("input"); | |
| btn.type = "button"; | |
| btn.value = caption; | |
| btn.className = "submit"; | |
| Util.observe(btn, "click", func); | |
| elem.appendChild(btn); | |
| return btn; | |
| }, | |
| showConfig: function () { | |
| this.container.innerHTML = ""; | |
| for (var k in this.list) { | |
| this.list[k].showConfig(this.container); | |
| } | |
| Config.show(this.container); | |
| var p = document.createElement("p"); | |
| p.style.borderTop = "1px solid #999"; | |
| p.style.padding = "8px"; | |
| var saveButton = document.createElement("input"); | |
| saveButton.className = "submit"; | |
| saveButton.type = "button"; | |
| saveButton.value = "保存"; | |
| saveButton.style.width = "5em"; | |
| saveButton.style.fontWeight = "bold"; | |
| var self = this; | |
| Util.observe(saveButton, "click", function () { | |
| for (var k in self.list) { | |
| self.list[k].saveConfig(); | |
| } | |
| self.save(); | |
| Config.save(); | |
| }); | |
| p.appendChild(saveButton); | |
| this.container.appendChild(p); | |
| }, | |
| }; | |
| var NicoHistory = { | |
| videoIds: [ ], | |
| loadFromCookie: function () { | |
| NicoHistory.videoIds = [ ]; | |
| if (w.document.cookie && /\bnicohistory\s*=\s*([^;]+)/.test(w.document.cookie)) { | |
| var hist = RegExp.$1; | |
| var m, re = /(?:^|%2C)(.+?)(?=%3A|$)/ig; | |
| while (m = re.exec(hist)) { | |
| this.videoIds.push(w.unescape(m[1])); | |
| } | |
| } | |
| } | |
| }; | |
| var RegisterButton = function () { this.initialize.apply(this, arguments); }; | |
| RegisterButton.prototype = { | |
| initialize: function (favlist, listId, title) { | |
| this.favlist = favlist; | |
| this.listId = listId; | |
| this.title = title; | |
| this.button = false; | |
| }, | |
| show: function (elem) { | |
| if (this.button) return; | |
| var a = document.createElement("a"); | |
| a.id = this.buttonId; | |
| a.href = "javascript:void(0);"; | |
| a.className = "TXT12"; | |
| a.style.textDecoration = "none"; | |
| a.style.padding = "2px 4px"; | |
| a.style.margin = "3px 4px"; | |
| a.style.border = "1px solid #CCC"; | |
| a.style.backgroundColor = "#EEE"; | |
| var self = this; | |
| Util.observe(a, "click", function () { self.execute(); }); | |
| elem.style.position = "relative"; | |
| elem.appendChild(a); | |
| this.button = a; | |
| this.update(); | |
| }, | |
| update: function () { | |
| if (!this.button) return; | |
| if (this.favlist.get(this.listId)) { | |
| this.button.innerHTML = '<span style="color:#F00;">×</span>'; | |
| } else { | |
| this.button.innerHTML = '<span style="color:#FC3;">★</span>'; | |
| } | |
| }, | |
| execute: function () { | |
| if (this.favlist.get(this.listId)) { | |
| this.favlist.remove(this.listId); | |
| } else { | |
| this.favlist.add(this.listId, this.title); | |
| GM_setValue("lastUpdate", 0); | |
| } | |
| this.update(); | |
| } | |
| }; | |
| Config.load(); | |
| var favlist = new Favlist(); | |
| favlist.load(); | |
| var m; | |
| if (m = w.location.href.match(/nicovideo\.jp\/mylist\/(?:\d+\/)?(\d+)(?!.*rss=)/)) { | |
| var h1 = document.getElementsByTagName("h1"); | |
| if (h1 && h1.length > 0) { | |
| var button = new RegisterButton(favlist, "mylist/" + m[1], h1[0].innerHTML); | |
| button.show(h1[0]); | |
| } | |
| } else if (m = w.location.href.match(/nicovideo\.jp\/(?:myvideo|user)\/(\d+)(?!.*rss=)/)) { | |
| var h1 = document.getElementsByTagName("h1"); | |
| if (h1 && h1.length > 0) { | |
| var title = document.title.match(/^(.+)さんの/) ? RegExp.$1 + "さんの投稿動画" : "投稿動画(" + m[1] + ")"; | |
| var button = new RegisterButton(favlist, "myvideo/" + m[1], title); | |
| button.show(h1[0]); | |
| } | |
| } else if (m = w.location.href.match(/nicovideo\.jp\/my?/)){ | |
| NicoHistory.loadFromCookie(); | |
| if (NicoHistory.videoIds.length > 0) { | |
| favlist.clearByVideoId(NicoHistory.videoIds); | |
| } | |
| var showFavlist = function () { | |
| var parentContainer; | |
| var xp = "//div[@class='content_312']"; | |
| var r = document.evaluate(xp, document, null, | |
| XPathResult.FIRST_ORDERED_NODE_TYPE, null); | |
| if (r) parentContainer = r.singleNodeValue; | |
| if (parentContainer) { | |
| favlist.show(parentContainer); | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| if (showFavlist()) { | |
| if (w.Category) { | |
| var oldCategoryUpdate = w.Category.update; | |
| if (oldCategoryUpdate) { | |
| w.Category.update = function () { | |
| oldCategoryUpdate.apply(w.Category, arguments); | |
| if (Config.showInAllTabs) { | |
| showFavlist(); | |
| } else { | |
| w.Category.update = oldCategoryUpdate; | |
| } | |
| } | |
| } | |
| } | |
| if (Config.checkInterval > 0) { | |
| var now = w.Math.floor((new Date()).getTime() / 1000); | |
| var last = GM_getValue("lastUpdate") || 0; | |
| if (last + Config.checkInterval < now) { | |
| GM_setValue("lastUpdate", now); | |
| favlist.updateAll(); | |
| } | |
| } | |
| } | |
| } | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment