// ==UserScript== // @name RTM Tickler File // @namespace http://blogstyle.seesaa.net/article/26613748.html // @include http://www.rememberthemilk.com/home/* // /==UserScript== // version 0.6 //------------------------------------------------------------------- // タブのデフォルト値 TicklerFile.defaultValue = { monthsPerLine: 3, datesPerLine: 7, monthFormat: "mmm", dateFormat: "DD(ddd)", monthTabWidth: 80, dateTabWidth: 48, contentWidth: 360 }; // ロケール(日付表示に使用) TicklerFile.locale = "ja_JP"; // 日付文字列をキーにその日のタスクリスト(HTML形式)を返すハッシュ TicklerFile.hash = {}; // タスクが存在する日付文字列のリスト TicklerFile.json = ""; // インスタンスアクセス用 TicklerFile.object = null; // ATOM取得インターバル(分) TicklerFile.timer = { timerId: null, interval1: 5, // 操作直後 interval2: 60 // 操作がないとき }; // 各種ユーティリティ関数格納用 TicklerFile.util = {}; // デバッグ用 TicklerFile.debug = false; TicklerFile.debugLocal = false; // 曜日名、月名設定 if (TicklerFile.locale == "ja_JP") { TicklerFile.days = ["日曜日", "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日"]; TicklerFile.shortDays = ["日", "月", "火", "水", "木", "金", "土"]; TicklerFile.months = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月","12月"]; TicklerFile.shortMonths = ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]; } else { TicklerFile.days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; TicklerFile.shortDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; TicklerFile.months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November","December"]; TicklerFile.shortMonths = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; } //------------------------------------------------------------------- function main() { var div = document.getElementById("overviewnews"); if (!div) return; if (TicklerFile.object == null) { // TicklerFile用CSSの適用 TicklerFile.util.addTicklerStyle(); var div = document.createElement('div'); div.id = 'TicklerFile'; TicklerFile.util.drawRTMbox(div, "overviewnews"); // 描画エリアが有効になるのを待って、TicklerFileインスタンス生成 window.setTimeout(function() {CreateTicklerFile()}, 100); } } window.CreateTicklerFile = function() { var div = document.getElementById("TicklerFile"); // ダミーのタグを挿入して表示幅を計測 // 表示幅>0の場合、描画準備OK var tab = document.createElement("a"); tab.appendChild(document.createTextNode("test")); div.appendChild(tab); var show = tab.offsetWidth; div.removeChild(tab); // 描画準備NGの場合、リトライ if (show == 0) window.setTimeout(function() {CreateTicklerFile()}, 100); else { // TicklerFileインスタンス生成 TicklerFile.object = new TicklerFile(TicklerFile.util.showContents); // ATOMを取得して解析、画面設定 if (TicklerFile.debugLocal) TicklerFile.object.getAtomDebug(); else TicklerFile.object.getAtom(); var boxdiv = document.getElementById("overviewTicklerFile").parentNode; document.addEventListener( 'keypress', function(e) { var target = new String(e.target.tagName); if(target == 'INPUT' || target == 'TEXTAREA') return; if(e.charCode == 64) // 「@」キーで表示切替 boxdiv.style.display = boxdiv.style.display ? '' : 'none'; }, false ); } } TicklerFile.util.onOperate = function() { TicklerFile.util.resetTimer(TicklerFile.timer.interval1); } TicklerFile.util.resetTimer = function(min) { if (TicklerFile.timer.timerId != null) window.clearTimeout(TicklerFile.timer.timerId); if (TicklerFile.debugLocal) { TicklerFile.timer.timerId = window.setTimeout(function() {TicklerFile.object.getAtomDebug();}, min * 60 * 1000); } else { TicklerFile.timer.timerId = window.setTimeout(function() {TicklerFile.object.getAtom();}, min * 60 * 1000); } if (TicklerFile.debug) { GM_log("SET " + min + " min" + new Date); } } TicklerFile.util.onResize = function() { var wid = 0; if (window.innerWidth) wid = window.innerWidth; else if (document.documentElement && document.documentElement.clientWidth) wid = document.documentElement.clientWidth; else if(document.body) wid = document.body.clientWidth; var div = document.getElementById("overviewTicklerFile"); if (wid < 850) div.style.width="505px"; else div.style.width="570px"; } window.addEventListener("click", TicklerFile.util.onOperate, false); window.addEventListener("keyup", TicklerFile.util.onOperate, false); window.addEventListener("resize", TicklerFile.util.onResize, false); //------------------------------------------ function TicklerFile(contentsGetterFunction) { // デフォルト値設定 this.monthsPerLine = TicklerFile.defaultValue.monthsPerLine; this.datesPerLine = TicklerFile.defaultValue.datesPerLine; this.monthFormat = TicklerFile.defaultValue.monthFormat; this.dateFormat = TicklerFile.defaultValue.dateFormat; this.monthTabWidth = TicklerFile.defaultValue.monthTabWidth; this.dateTabWidth = TicklerFile.defaultValue.dateTabWidth; this.contentWidth = TicklerFile.defaultValue.contentWidth; this.lines = 0; this.indent = 10; this.margin = 2; TicklerFile.contentsGetter = contentsGetterFunction; this.tabset = document.getElementById("TicklerFile"); // DIVタグの属性から設定値を取得 var s; if (s = this.tabset.getAttribute("months")) this.monthsPerLine = Number(s); if (s = this.tabset.getAttribute("dates")) this.datesPerLine = Number(s); if (s = this.tabset.getAttribute("monthFormat")) this.monthFormat = s; if (s = this.tabset.getAttribute("dateFormat")) this.dateFormat = s; if (s = this.tabset.getAttribute("monthWidth")) this.monthTabWidth = Number(s); if (s = this.tabset.getAttribute("dateWidth")) this.dateTabWidth = Number(s); if (s = this.tabset.getAttribute("contentWidth")) this.contentWidth = Number(s); if (s = this.tabset.getAttribute("indent")) this.indent = Number(s); if (s = this.tabset.getAttribute("margin")) this.margin = Number(s); this.tabset.setAttribute("selectedTab", null); // 描画 this.draw(); this.tabset.setAttribute("lines", this.lines); this.tabset.setAttribute("indent", this.indent); this.tabset.setAttribute("margin", this.margin); this.tabset.setAttribute("contentWidth", this.contentWidth); // 今日のタブを選択 var now = new Date(); this.switchTab(this.tabset, TicklerFile.util.formatString(now, 'YYYY0MM0DD')); } // タブ押下時のコールバック関数から呼ばれるタスク表示関数 TicklerFile.util.showContents = function(datestr) { var content = TicklerFile.hash[datestr]; if (content == null) content = ""; var lines = 3; // 3行に満たない場合タグを付加する var br = content.match(//g); if (br) lines -= br.length; for (var i = 0; i < lines; i++) content += ""; return content; } TicklerFile.prototype.getAtomDebug = function() { var text = '' + '' + '' + 'テストデータ' + '' + '' + '' + '期限: ' + '2006年11月30日木曜日' + '' + '' + '' + '' + ''; var parser = new DOMParser(); var dom = parser.parseFromString(text,"application/xml"); // entryタグ毎に処理 TicklerFile.hash = {}; TicklerFile.json = ""; var entries = dom.getElementsByTagName('entry'); for (var i = 0; i < entries.length; i++) { var title = entries[i].getElementsByTagName('title')[0].textContent; var spans = entries[i].getElementsByTagName('span'); var date = "00000000"; for (var j = 0; j < spans.length; j++) { if (spans[j].className == "rtm_due_value") { date = spans[j].textContent; date = TicklerFile.util.convertDateFormat(date); TicklerFile.util.addContent(date, title); break; } } } // ハッシュからタスクが存在する日付を抽出 for (var key in TicklerFile.hash) TicklerFile.json += '"' + key + '",'; TicklerFile.json = '[' + TicklerFile.json.substr(0, TicklerFile.json.length - 1) + ']'; // 画面を更新 TicklerFile.object.set(TicklerFile.json); // 次回取得時間を設定 TicklerFile.util.resetTimer(TicklerFile.timer.interval2); } TicklerFile.prototype.getAtom = function() { // URLからATOMの場所を生成 var atom = document.URL.replace(/#.*$/, ""); atom = atom.replace(/www\.rememberthemilk\.com\/home\//, "www.rememberthemilk.com/atom/"); // ATOMを取得して解析、hashとjsonに設定 TicklerFile.hash = {}; TicklerFile.json = ""; GM_xmlhttpRequest({ method: 'GET', url: atom, headers: { 'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey/0.3', 'Accept': 'application/atom+xml,application/xml,text/xml', }, onload: function(responseDetails) { var parser = new DOMParser(); var dom = parser.parseFromString(responseDetails.responseText, "application/xml"); // entryタグ毎に処理 var entries = dom.getElementsByTagName('entry'); for (var i = 0; i < entries.length; i++) { var title = entries[i].getElementsByTagName('title')[0].textContent; var spans = entries[i].getElementsByTagName('span'); var date = "00000000"; for (var j = 0; j < spans.length; j++) { if (spans[j].className == "rtm_due_value") { date = spans[j].textContent; date = TicklerFile.util.convertDateFormat(date); TicklerFile.util.addContent(date, title); break; } } } // ハッシュからタスクが存在する日付を抽出 for (var key in TicklerFile.hash) TicklerFile.json += '"' + key + '",'; TicklerFile.json = '[' + TicklerFile.json.substr(0, TicklerFile.json.length - 1) + ']'; // 画面を更新 TicklerFile.object.set(TicklerFile.json); // 次回取得時間を設定 TicklerFile.util.resetTimer(TicklerFile.timer.interval2); } }); } // YYYY年M月D日d曜日 → YYYYMMDD TicklerFile.util.convertDateFormat = function(date) { if (TicklerFile.locale != "ja_JP") { return date; } var ret = "00000000"; if (date.match(/([0-9]+)[^0-9]([0-9]+)[^0-9]([0-9]+)/)) { var year = RegExp.$1; var month = RegExp.$2; if (month.length == 1) month = "0" + month; var day = RegExp.$3; if (day.length == 1) day = "0" + day; ret = year + month + day; } return ret; } TicklerFile.util.addContent = function(date, summary) { var content = ""; if (TicklerFile.hash[date]) content = TicklerFile.hash[date] + ""; content += summary; TicklerFile.hash[date] = content; var month = date.substr(0, 6) + "00"; content = ""; if (TicklerFile.hash[month]) content = TicklerFile.hash[month] + ""; content += date.substr(6, 2) + " " + summary; TicklerFile.hash[month] = content; } TicklerFile.util.addTicklerStyle = function() { var css = '' + '#TicklerFile {' + ' text-align: left;' + '}' + '' + '.ticklerTab {' + ' font-size: 0.9em;' + ' line-height: 1.4em;' + ' margin: 0em 0em 0em 0.25em;' + ' padding: 2px;' + '}' + '' + 'a.ticklerSel {' + ' color: #000000;' + ' text-decoration: none;' + '}' + '' + 'a.ticklerUnsel {' + ' color: #ffffff;' + ' text-decoration: none;' + '}' + '' + '.ticklerTabContents {' + ' font-size: 0.8em;' + ' padding: 0.5em;' + ' background: #ffffff;' + ' border: 1px solid #a8a8a8;' + '}' + '' + '.ticklerSel {' + ' color: #000000;' + ' background: #ffffff;' + ' border-left: 1px solid #a8a8a8;' + ' border-top: 1px solid #a8a8a8;' + ' border-right: 1px solid #a8a8a8;' + '}' + '' + '.ticklerUnsel {' + ' color: #ffffff;' + ' background: #b8b8b8;' + ' border-left: 1px solid #a8a8a8;' + ' border-top: 1px solid #a8a8a8;' + ' border-right: 1px solid #a8a8a8;' + '}' + '' + '.ticklerExist {' + ' font-weight: bold;' + '}' + '' + '.ticklerEmpty {' + ' font-weight: normal;' + '}'; GM_addStyle(css); } TicklerFile.prototype.open = function(datestr) { if (this.tabset.getAttribute("selectedTab") == datestr) return; else this.switchTab(this.tabset, datestr); } TicklerFile.prototype.close = function() { var datestr = this.tabset.getAttribute("selectedTab"); if (datestr) this.switchTab(this.tabset, datestr); } TicklerFile.prototype.add = function(datestr) { this.setData('["' + datestr + '"]', "add"); } TicklerFile.prototype.set = function(json) { this.setData(json, "refresh"); } TicklerFile.prototype.draw = function() { // タブリスト作成 var tablist = this.createTabList(); var tmplist = []; // インデントの深さ計算 this.lines = 0; for (var t in tablist) if (tablist[t] == "") this.lines++; // 逆順にループしてタブ表示 var indentCount = this.lines - 1; while (tablist.length > 0) { var tab = tablist.pop(); if (tab == '') { var margin = this.indent * indentCount + 14; indentCount--; while (tmplist.length > 0) { var label = tmplist.pop(); this.createTab(label, margin, "ticklerTab ticklerEmpty ticklerUnsel"); margin = this.margin; } this.tabset.appendChild(document.createElement("br")); } else { tmplist.push(tab); } } } // flag: "add", "refresh" TicklerFile.prototype.setData = function(json, flag) { var list = eval(json); var hash = new Array(); for (var i = 0; i < list.length; i++) { hash[list[i]] = true; hash[list[i].substr(0,6) + "00"] = true; } var selectedTab = this.tabset.getAttribute("selectedTab"); var nodes = this.tabset.childNodes; for(var t=0; tpush tablist.push(''); // 当日~末日push var theDay; var cnt = 0; for (theDay = new Date(); theDay.getMonth() == now.getMonth(); theDay.setDate(theDay.getDate() + 1)) { // 指定日毎にpush if ((cnt != 0) && (cnt % this.datesPerLine == 0)) tablist.push(''); cnt++; tablist.push(TicklerFile.util.formatString(theDay, 'YYYY0MM0DD')); } // push tablist.push(''); // 当月push label = TicklerFile.util.formatString(now, 'YYYY0MM00'); tablist.push(label); // push tablist.push(''); // 1日~前日push cnt = 0; var nextMonth1st = new Date(theDay); for (; (theDay.getDate() < now.getDate()) && (theDay.getMonth() == nextMonth1st.getMonth()); theDay.setDate(theDay.getDate() + 1)) { // 指定日毎にpush if ((cnt != 0) && (cnt % this.datesPerLine == 0)) tablist.push(''); cnt++; tablist.push(TicklerFile.util.formatString(theDay, 'YYYY0MM0DD')); } // push if (cnt > 0) tablist.push(''); // 翌月push theDay = nextMonth1st; label = TicklerFile.util.formatString(theDay, 'YYYY0MM00'); tablist.push(label); // push tablist.push(''); // 翌々月~前月push cnt = 0; for (theDay.setMonth(theDay.getMonth() + 1); theDay.getMonth() != now.getMonth(); theDay.setMonth(theDay.getMonth() + 1)) { // 指定月毎にpush if ((cnt != 0) && (cnt % this.monthsPerLine == 0)) tablist.push(''); cnt++; label = TicklerFile.util.formatString(theDay, 'YYYY0MM00'); tablist.push(label); } return tablist; } TicklerFile.prototype.onClickTab = function(e) { TicklerFile.prototype.switchTab(this.parentNode,this.getAttribute("date")); return false; } TicklerFile.prototype.switchTab = function(tabset,newtab) { var oldtab = tabset.getAttribute("selectedTab"); var lines = Number(tabset.getAttribute("lines")); var indent = Number(tabset.getAttribute("indent")); var margin = Number(tabset.getAttribute("margin")); var contentWidth = Number(tabset.getAttribute("contentWidth")); var openPos = 0; var linecnt = 0; // タブ親の子ノードリスト取得 var nodes = tabset.childNodes; // 子ノードに対してループ for(var t=0; t 0) && (linecnt == openPos)) { // divエレメント作成 var div = document.createElement("div"); div.id = "ticklerTabContents"; div.className = "ticklerTabContents"; div.innerHTML = TicklerFile.contentsGetter(newtab); div.style.marginLeft = String((lines - linecnt) * indent + 10) + "px"; if (contentWidth > 0) div.style.width = String(contentWidth) + "px"; tabset.insertBefore(div,tab.nextSibling); } } } if (newtab == oldtab) tabset.setAttribute("selectedTab", null); else tabset.setAttribute("selectedTab", newtab); } TicklerFile.util.drawRTMbox = function(content, after_id) { var after_div = document.getElementById(after_id); var next_div = after_div.nextSibling; // 枠線用DIV追加 var div0 = document.createElement('div'); div0.style.margin = '0pt'; div0.style.padding = '0pt'; after_div.parentNode.insertBefore(div0, next_div); var div00 = document.createElement('div'); div00.id = 'overviewTicklerFile'; div00.className = 'white_rbroundbox'; div00.style.width = '570px'; div00.style.paddingBottom = '20px'; div0.appendChild(div00); var div000 = document.createElement('div'); div000.className = 'white_rbtop'; var div0000 = document.createElement('div'); div0000.appendChild(document.createElement('div')); div000.appendChild(div0000); div00.appendChild(div000); var div001 = document.createElement('div'); div001.className = 'white_rbcontentwrap'; var div0010 = document.createElement('div'); div0010.className = 'white_rbcontent'; div001.appendChild(div0010); div00.appendChild(div001); var div002 = document.createElement('div'); div002.className = 'white_rbbot'; var div0020 = document.createElement('div'); div0020.appendChild(document.createElement('div')); div002.appendChild(div0020); div00.appendChild(div002); div0010.appendChild(content); TicklerFile.util.onResize(); } TicklerFile.util.formatString = function(date, template) { template = template.replace(/YYYY/g,date.getFullYear()); template = template.replace(/YY/g,zeroPad(date.getFullYear()-2000)); template = template.replace(/MMM/g,TicklerFile.months[date.getMonth()]); template = template.replace(/mmm/g,TicklerFile.shortMonths[date.getMonth()]); template = template.replace(/0MM/g,zeroPad(date.getMonth()+1)); template = template.replace(/MM/g,date.getMonth()+1); template = template.replace(/DDD/g,TicklerFile.days[date.getDay()]); template = template.replace(/ddd/g,TicklerFile.shortDays[date.getDay()]); template = template.replace(/0DD/g,zeroPad(date.getDate())); template = template.replace(/DD/g,date.getDate()); return template; function zeroPad(n) { var s = n.toString(); if(s.length < 2) s = "0" + s; return(s); } } main();