Last active
November 7, 2015 08:48
-
-
Save sifisifi/4cdd04f6b8bda07fc38c to your computer and use it in GitHub Desktop.
This file contains 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 Convert One Character to Stamp in LLL | |
// @namespace https://github.com/mosaicer | |
// @author mosaicer | |
// @description LLLにおける入力補助機能を提供する | |
// @match http://*.reddit.com/r/lowlevelaware/comments/* | |
// @match https://*.reddit.com/r/lowlevelaware/comments/* | |
// @match https://*.reddit.com/r/lowlevelaware/submit* | |
// @version 1.7 | |
// @grant GM_xmlhttpRequest | |
// @grant GM_addStyle | |
// @grant GM_getValue | |
// @grant GM_setValue | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
/** | |
スタンプ一覧(2015/06/18) | |
対応が怪しいもの:3点リーダー,音符マーク,読点,句読点,波線,ばつ印, | |
その他の複数文字で一つのスタンプを表すもの | |
複数文字について:前後に半角空白を付けることで判定するようにした | |
更新(2015/08/29) | |
数字を追加.全角と半角の両方に対応 | |
*/ | |
var global = new Function("return window;")(); | |
var lll = global.LLL = new function () { | |
this.STAMP_MAP = { | |
'あ': '[](#a)', 'い': '[](#i)', 'う': '[](#u)', 'え': '[](#e)', | |
'お': '[](#o)', 'か': '[](#ka)', 'き': '[](#ki)', 'く': '[](#ku)', | |
'け': '[](#ke)', 'こ': '[](#ko)', 'さ': '[](#sa)', 'し': '[](#si)', | |
'す': '[](#su)', 'せ': '[](#se)', 'そ': '[](#so)', 'た': '[](#ta)', | |
'ち': '[](#ti)', 'つ': '[](#tu)', 'て': '[](#te)', 'と': '[](#to)', | |
'な': '[](#na)', 'に': '[](#ni)', 'ぬ': '[](#nu)', 'ね': '[](#ne)', | |
'の': '[](#no)', 'は': '[](#ha)', 'ひ': '[](#hi)', 'ふ': '[](#hu)', | |
'へ': '[](#he)', 'ほ': '[](#ho)', 'ま': '[](#ma)', 'み': '[](#mi)', | |
'む': '[](#mu)', 'め': '[](#me)', 'も': '[](#mo)', 'や': '[](#ya)', | |
'ゆ': '[](#yu)', 'よ': '[](#yo)', 'ら': '[](#ra)', 'り': '[](#ri)', | |
'る': '[](#ru)', 'れ': '[](#re)', 'ろ': '[](#ro)', 'わ': '[](#wa)', | |
'を': '[](#wo)', 'ん': '[](#n)', '…': '[](#santen)', | |
'♪': '[](#onpumark)', '「': '[](#kakko)', '」': '[](#kakkotoji)', | |
'!': '[](#exmark)', '!': '[](#exmark)', '?': '[](#hatenamark)', | |
'?': '[](#hatenamark)', '、': '[](#touten)', '。': '[](#kutouten)', | |
',': '[](#touten)', '.': '[](#kutouten)', 'が': '[](#ka)[](#dakuten)', | |
'ぎ': '[](#ki)[](#dakuten)', 'ぐ': '[](#ku)[](#dakuten)', | |
'げ': '[](#ke)[](#dakuten)', 'ご': '[](#ko)[](#dakuten)', | |
'ざ': '[](#sa)[](#dakuten)', 'じ': '[](#si)[](#dakuten)', | |
'ず': '[](#su)[](#dakuten)', 'ぜ': '[](#se)[](#dakuten)', | |
'ぞ': '[](#so)[](#dakuten)', 'だ': '[](#ta)[](#dakuten)', | |
'ぢ': '[](#ti)[](#dakuten)', 'づ': '[](#tu)[](#dakuten)', | |
'で': '[](#te)[](#dakuten)', 'ど': '[](#to)[](#dakuten)', | |
'ば': '[](#ha)[](#dakuten)', 'び': '[](#hi)[](#dakuten)', | |
'ぶ': '[](#hu)[](#dakuten)', 'べ': '[](#he)[](#dakuten)', | |
'ぼ': '[](#ho)[](#dakuten)', 'ぱ': '[](#ha)[](#handakuten)', | |
'ぴ': '[](#hi)[](#handakuten)', 'ぷ': '[](#hu)[](#handakuten)', | |
'ぺ': '[](#he)[](#handakuten)', 'ぽ': '[](#ho)[](#handakuten)', | |
'ぁ': '[](#as)', 'ぃ': '[](#is)', 'ぅ': '[](#us)', 'ぇ': '[](#es)', | |
'ぉ': '[](#os)', 'っ': '[](#tus)', 'ゃ': '[](#yas)', 'ゅ': '[](#yus)', | |
'ょ': '[](#yos)', '〜': '[](#nyoro)', 'ー': '[](#bou)', | |
'×': '[](#batu)', 'L': '[](#elu)', 'L': '[](#elu)', | |
' ちんこ ': '[](#chin)', ' はつでん ': '[](#hatuden)', | |
' すのー ': '[](#snoo)', ' うんこ ': '[](#unkoji)', | |
' はーと ': '[](#heartmark)', '0': '[](#su0)', '1': '[](#su1)', | |
'2': '[](#su2)', '3': '[](#su3)', '4': '[](#su4)', '5': '[](#su5)', | |
'6': '[](#su6)', '7': '[](#su7)', '8': '[](#su8)', '9': '[](#su9)', | |
'0': '[](#su0)', '1': '[](#su1)', '2': '[](#su2)', '3': '[](#su3)', | |
'4': '[](#su4)', '5': '[](#su5)', '6': '[](#su6)', '7': '[](#su7)', | |
'8': '[](#su8)', '9': '[](#su9)' | |
}; | |
// ドットスタンプのコマンド+独自コマンドの配列 | |
this.DOT_COMMAND_ARRAY = [ | |
'#dt-01', '#dt-02', '#dt-03', '#dt-04', '#dt-05', '#dt-06', '#dt-07', | |
'#dt-08', '#dt-09', '#dt-10', '#dt-11', '#dt-12', '#dt-13', '#dt-14', | |
'#dt-15', '#dt-16', '#dt-17', '#dt-18', '#dt-19', '#dt-20', '#dt-21', | |
'#dt-22', '#dt-23', '#dt-24', '#dt-25', '#dt-26', '#dt-27', '#dt-28', | |
'#dt-29', '#dt-30', '#dt-31', '#dt-32', '#script-clear-class', | |
'#script-smallp-class', '#script-bigp-class' | |
]; | |
// 独自コマンドに対応するオブジェクト | |
this.SUPPORT_STAMP_MAP = { | |
'#script-clear-class': ['[透明ドット]', '[ ](#dt-00)'], | |
'#script-smallp-class': ['[小改行]', ' \n'], | |
'#script-bigp-class': ['[大改行]', '\n\n'] | |
}; | |
// ボタンのクラス名とか | |
this.STAMP_CONVERT_CLASS = 'lll-stamp-converter'; | |
this.TREMBLER_CLASS = 'lll-trembler'; | |
this.STAMP_BTN_CLASS = 'lll-stamp-displayer'; | |
this.STAMP_SHADOW_CLASS = 'lll-stamp-shadow'; | |
this.DOT_STAMP_CLASS = 'lll-dot-stamp'; | |
this.STAMP_CLASS = 'lll-stamp'; | |
this.FAV_STAMP_CLASS = 'lll-fav-stamp'; | |
this.STAMP_DIALOG_ID = 'lll-stamp-dialog'; | |
this.PROGRESS_DIALOG_CLASS = 'lll-progress-dialog'; | |
this.TAB_BUTTON_CLASS = 'lll-tab-button'; | |
this.TABS_AREA_ID = 'lll-tab-area'; | |
this.TAB_CLASS = 'lll-tab'; | |
// 震わせるコマンド | |
this.TREMBLER_COMMAND = '/gakuburu'; | |
// 保存するデータのキー | |
this.DATA_KEY = 'lll_fav_stamp_key'; | |
// 保存する際の区切り文字 | |
this.SPLIT_WORD = ',/'; | |
// 面倒なのでpreg_quote関数をphpjsから拝借 | |
this.preg_quote = function (str, delimiter) { | |
// discuss at: http://phpjs.org/functions/preg_quote/ | |
// original by: booeyOH | |
// improved by: Ates Goral (http://magnetiq.com) | |
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) | |
// improved by: Brett Zamir (http://brett-zamir.me) | |
// bugfixed by: Onno Marsman | |
return String(str).replace(new RegExp('[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + (delimiter || '') + '-]', 'g'), '\\$&'); | |
}; | |
// 各ボタンを生成する関数 | |
this.generateButtons = function (targetNode) { | |
var convertButton = document.createElement('button'), | |
trembleButton = document.createElement('button'), | |
stampButton = document.createElement('button'); | |
// 変換ボタン | |
convertButton.textContent = 'convert'; | |
convertButton.setAttribute('class', this.STAMP_CONVERT_CLASS); | |
// テキストを送信しないようにリターンする | |
convertButton.setAttribute('onclick', 'return false;'); | |
// 保存ボタンの右に変換を挿入 | |
targetNode.insertBefore(convertButton, targetNode.childNodes[1]); | |
// 震わせるボタン | |
trembleButton.textContent = 'tremble'; | |
trembleButton.setAttribute('class', this.TREMBLER_CLASS); | |
trembleButton.setAttribute('onclick', 'return false;'); | |
targetNode.insertBefore(trembleButton, convertButton.nextSibling); | |
// スタンプボタン | |
stampButton.textContent = 'stamp'; | |
stampButton.setAttribute('class', this.STAMP_BTN_CLASS); | |
stampButton.setAttribute('onclick', 'return false;'); | |
targetNode.insertBefore(stampButton, trembleButton.nextSibling); | |
}; | |
// パレットを生成する関数 | |
this.generatePalettes = function (targetNode) { | |
var paletteDiv; | |
// textareaがない場合は処理しない | |
if (!!targetNode.getElementsByTagName('textarea').length) { | |
paletteDiv = document.createElement('p'); | |
// ドットスタンプコマンドの配列でループを回して挿入していく | |
this.DOT_COMMAND_ARRAY.forEach((function (_this) { | |
return function (command) { | |
var dotStamp = document.createElement('a'); | |
dotStamp.setAttribute('href', command); | |
dotStamp.setAttribute('class', _this.DOT_STAMP_CLASS); | |
// 特に影響はないけど,リンク飛んだ扱いになるのが面倒なので対策 | |
dotStamp.setAttribute('onclick', 'return false;'); | |
// テキストがあるやつだったらそれを入れる | |
if (/^(#s).+/.test(command)) { | |
dotStamp.textContent = _this.SUPPORT_STAMP_MAP[command][0]; | |
} | |
// パレットにドンドン追加していく | |
paletteDiv.appendChild(dotStamp); | |
}; | |
})(this)); | |
// 一番初めに挿入.スタンプ入力支援スクリプトとの競合を回避 | |
targetNode.insertBefore(paletteDiv, targetNode.firstChild); | |
} | |
}; | |
// 一文字系スタンプに変換する関数 | |
this.convertStrToOneStamp = function (targetNode) { | |
var bottomArea = targetNode.parentNode.parentNode, | |
textBox = bottomArea.previousSibling.getElementsByTagName('textarea')[0], | |
livePreview = bottomArea.nextSibling.nextSibling.childNodes[1], | |
text = textBox.value, | |
textLength = text.length, | |
// 複数文字をチェックするオブジェクト | |
checkPluralObj = { | |
'single': '', | |
'plural': '' | |
}, | |
// 最後にテキストボックスに出力する文字列 | |
output = '', | |
// 一時的に文字列を保存しておく変数 | |
subStr, | |
i; | |
// 一文字ずつチェックしていく | |
for (i = 0; i < textLength; i++) { | |
subStr = text.substring(i, i + 1); | |
/** | |
半角空白だったら複数文字の可能性があるのでチェック | |
2つ目の条件は複数文字のチェック中は常にここを通過するため | |
*/ | |
if (subStr === ' ' || !!checkPluralObj.plural) { | |
// 半角空白が次に来るまで記録する | |
checkPluralObj.plural += subStr; | |
} | |
// 対応するスタンプがあれば格納しなおす | |
if (!!STAMP_MAP[subStr]) { | |
subStr = STAMP_MAP[subStr]; | |
} | |
// 複数文字のチェック中の場合 | |
if (!!checkPluralObj.plural) { | |
// 出力に加える用の文字を別の値に記憶していく | |
checkPluralObj.single += subStr; | |
} | |
// 出力に加える | |
output += subStr; | |
// 前後に半角空白がある場合=複数文字のチェックが終わった場合 | |
if (/.+ $/.test(checkPluralObj.plural)) { | |
// 対応するスタンプを取得 | |
subStr = this.STAMP_MAP[checkPluralObj.plural]; | |
// スタンプがあれば,出力用の文字列と入れ替える | |
if (!!subStr) { | |
output = output.replace( | |
new RegExp(preg_quote(checkPluralObj.single)), subStr | |
); | |
} | |
// チェック用のオブジェクトの値をリセットして次に備える | |
checkPluralObj.plural = checkPluralObj.single = ''; | |
} | |
} | |
// できた文字列をテキストボックスにセット | |
textBox.value = output; | |
// プレビューがあればそこにもセット | |
if (!!livePreview && !!livePreview.childNodes[0]) { | |
if (/<("[^"]*"|'[^']*'|[^'">])*>/g.test(output)) { | |
alert('HTMLタグが使われている可能性があるため,ライブプレビューには反映しません'); | |
} else { | |
livePreview.childNodes[0].innerHTML = output.replace( | |
/\[\]\((#[a-z0-9]+)\)/g, '<a href="$1"></a>' | |
); | |
} | |
} | |
}; | |
// 選択した(してなくてもいい)文字列を震わせる関数 | |
this.tremleString = function (targetNode) { | |
var bottomArea = targetNode.parentNode.parentNode, | |
textBox = bottomArea.previousSibling.getElementsByTagName('textarea')[0], | |
// livePreview = bottomArea.nextSibling.nextSibling.childNodes[1], | |
str = textBox.value, | |
strStart = textBox.selectionStart, | |
strEnd = textBox.selectionEnd, | |
rangeStr = str.slice(strStart, strEnd); | |
// 選択文字列あり,もしくは選択の始まりと終わりが違う場合 | |
if (!!rangeStr || strStart !== strEnd) { | |
textBox.value = str.slice(0, strStart) + '[' + rangeStr + '](' + | |
this.TREMBLER_COMMAND + ')' + str.slice(strEnd); | |
} | |
// 選択の始まりと終わりが同じ場合 | |
else if (strStart === strEnd) { | |
textBox.value = '[' + str + '](' + this.TREMBLER_COMMAND + ')'; | |
} | |
// TODO: プレビューがあればそこにもセット | |
// if (!!livePreview && !!livePreview.childNodes[0]) { | |
// if (/<("[^"]*"|'[^']*'|[^'">])*>/g.test(str)) { | |
// alert('HTMLタグが使われている可能性があるため,ライブプレビューには反映しません'); | |
// } else { | |
// } | |
// } | |
}; | |
// ドットスタンプを加える関数 | |
this.insertDotStamp = function (targetNode) { | |
var command = targetNode.getAttribute('href'), | |
mdDiv = targetNode.parentNode.parentNode, | |
textBox = mdDiv.getElementsByTagName('textarea')[0], | |
output = textBox.value, | |
strStart = textBox.selectionStart, | |
strEnd = textBox.selectionEnd, | |
rangeStr = output.slice(strStart, strEnd), | |
rangeStrSize = rangeStr.length, | |
subStr = '', | |
livePreview = mdDiv.nextSibling.nextSibling.nextSibling, | |
i; | |
// 一番最初の入力だったら引用をつける | |
if (!!!output) { | |
output = '>'; | |
} | |
// 選択文字列あり,もしくは選択の始まりと終わりが違う場合 | |
if (!!rangeStr || strStart !== strEnd) { | |
// テキストがある場合は意味がないのでスルーさせる | |
if (!!targetNode.textContent) { | |
alert('そのスタンプを適用することはできません'); | |
return; | |
} | |
// 一文字ずつ指定されたコマンドで囲む | |
for (i = 0; i < rangeStrSize; i++) { | |
subStr += '[' + rangeStr.substring(i, i + 1) + '](' + command + ')'; | |
} | |
// 文字列の前後を足して出力を生成 | |
output = output.slice(0, strStart) + subStr + output.slice(strEnd); | |
} | |
// 選択の始まりと終わりが同じ場合 | |
else if (strStart === strEnd) { | |
// テキストで場合分け | |
output += !!targetNode.textContent ? this.SUPPORT_STAMP_MAP[command][1] : | |
'[ ](' + command + ')'; | |
} | |
// テキストボックスに貼り付け | |
textBox.value = output; | |
// プレビューがあればそこにもセット | |
if (!!livePreview) { | |
// livePreviewにstyle(display:none;)がついている場合はそれを消す | |
if (!!livePreview.getAttribute('style')) { | |
livePreview.removeAttribute('style'); | |
} | |
if (/<("[^"]*"|'[^']*'|[^'">])*>/g.test(output)) { | |
alert('HTMLタグが使われている可能性があるため,ライブプレビューには反映しません'); | |
} else { | |
// スタンプ変換&小改行&大改行に対応するため置換する | |
output = '<p>' + output.replace( | |
/\[(.)\]\((#[a-z0-9\-]+)\)/g, '<a href="$2">$1</a>' | |
).replace(/ \n/g, '<br>\n').replace(/\n{2}/g, '</p><p>') + '</p>'; | |
// >で始まっていたら引用をつける | |
if (/^(<p>>).+/.test(output)) { | |
output = '<blockquote>\n' + output.replace(/<p>>/, '<p>') + | |
'\n</blockquote>'; | |
} | |
// ライブプレビューにセット | |
livePreview.childNodes[1].innerHTML = output; | |
} | |
} | |
}; | |
// ダイアログを表示する関数 | |
this.displayStamps = function () { | |
// スタンプがあればスタンプ一覧を表示 | |
if (!!Object.keys(this.stampListMap).length) { | |
this.stampDialog.style.display = 'block'; | |
} | |
// スタンプをまだ取得していな場合 | |
else { | |
// プログレスダイアログに変更 | |
this.stampDialog.textContent = '情報を取得しています…'; | |
this.stampDialog.setAttribute('class', this.PROGRESS_DIALOG_CLASS); | |
// ダイアログを表示 | |
this.stampDialog.style.display = 'block'; | |
// スタンプを取りに行く | |
this.fetchStamps(); | |
} | |
}; | |
// スタンプを取得する | |
this.fetchStamps = function () { | |
GM_xmlhttpRequest({ | |
method: 'GET', | |
url: document.querySelector( | |
'link[title="applied_subreddit_stylesheet"]' | |
).getAttribute('href'), | |
onload: (function (_this) { | |
return function (responseDetails) { | |
// スタンプ一覧を生成する | |
_this.generateStamps(responseDetails.responseText); | |
} | |
})(this), | |
onerror: (function (_this) { | |
return function (responseDetails) { | |
// ダイアログを消す | |
_this.stampDialog.style.display = 'none'; | |
alert('スタイルシートの取得に失敗しました.詳しくはJSコンソールでログをご確認ください.'); | |
console.error('An error occurred.\nresponseText: ' + | |
responseDetails.responseText + '\nreadyState: ' + | |
responseDetails.readyState + '\nresponseHeaders: ' + | |
responseDetails.responseHeaders + '\nstatus: ' + | |
responseDetails.status + '\nstatusText: ' + | |
responseDetails.statusText + '\nfinalUrl: ' + | |
responseDetails.finalUrl); | |
} | |
})(this) | |
}); | |
}; | |
// スタンプ一覧を生成 | |
this.generateStamps = function (stylesheetText) { | |
// 普通の画像スタンプのポジション番号を配列で格納 | |
var normalPosArray = stylesheetText.match(/a\[href\$="\-\d+"\]/g) | |
.map(function (stamp) { | |
return stamp.match(/\d+/)[0]; | |
}), | |
// アニメーションスタンプの番号を多次元配列で格納 | |
animPosArray = [], | |
// 位置を指定する | |
index = -1; | |
// アニメーションスタンプの番号を処理 | |
stylesheetText.match(/a\[href\$="#stmp\-ani\d+\-[a-z]\d+"\]/g) | |
.map(function (stamp) { | |
// a01, a02, ... | |
return stamp.match(/[a-z]\d+/g)[1]; | |
}) | |
// 同じものは除去 | |
.filter(function (element, index, array) { | |
return array.indexOf(element) === index; | |
}) | |
.forEach(function (pos) { | |
// a01とかb01とか各最初の番号が来た場合 | |
if (/[a-z]01/.test(pos)) { | |
// 位置を進める | |
index++; | |
// 新たに配列を入れる | |
animPosArray.push([]); | |
} | |
// 指定した位置の配列に番号を格納 | |
animPosArray[index].push(pos); | |
}); | |
// 値を元に戻す | |
index = 0; | |
// 普通のスタンプ&アニメーションスタンプを処理 | |
stylesheetText.match(/a\[href\*="#stmp\-[^\-]+\-"\]/g) | |
.forEach((function (_this) { | |
return function (command) { | |
// #stmp-01-, ... | |
var stamp = command.split('"')[1]; | |
// アニメーションのスタンプだった場合(aniだけが頼り) | |
if (/a\[href\*="#stmp\-ani\d+\-"\]/.test(command)) { | |
// キーはそれっぽいやつ,値はその全スタンプをくっつけたもの | |
_this.stampListMap[command.split('-')[1]] = | |
animPosArray[index].map(function (pos) { | |
return '<a href="' + stamp + pos + '" class="' + | |
_this.STAMP_CLASS + '" onclick="return false;"></a>'; | |
}).join(''); | |
// 位置を進める | |
index++; | |
} | |
// 普通のスタンプだった場合 | |
else { | |
// キーはそれっぽいやつ,値はその全スタンプをくっつけたもの | |
_this.stampListMap[command.split('-')[1]] = | |
normalPosArray.map(function (pos) { | |
return '<a href="' + stamp + pos + '" class="' + | |
_this.STAMP_CLASS + '" onclick="return false;"></a>'; | |
}).join(''); | |
} | |
}; | |
})(this)); | |
// 普通の処理で取れるスタンプを処理 | |
// ドット,一文字,おみくじ,じゃんけん | |
this.stampListMap['その他'] = stylesheetText | |
.match(/a\[href="#.*?"\]/g) | |
.filter(function (element, index, array) { | |
return array.indexOf(element) === index; | |
}) | |
.map((function (_this) { | |
return function (stamp) { | |
return '<a href="' + stamp.split('"')[1] + '" class="' + | |
_this.STAMP_CLASS + '" onclick="return false;"></a>'; | |
}; | |
})(this)).join(''); | |
// タブメニューのセット | |
this.setTabMenu(); | |
}; | |
// タブのメニューをセットする | |
this.setTabMenu = function () { | |
var tabs = document.createElement('p'), | |
tabButton = document.createElement('a'), | |
tab = document.createElement('div'), | |
index = 2, | |
key; | |
// ダイアログをリセット | |
this.stampDialog.innerHTML = ''; | |
// お気に入りのタブのボタンを追加 | |
tabButton.setAttribute('href', '#tab1'); | |
tabButton.setAttribute('class', this.TAB_BUTTON_CLASS); | |
tabButton.setAttribute('onclick', 'return false;'); | |
tabButton.textContent = 'favorite'; | |
tabs.appendChild(tabButton); | |
// お気に入りタブの本体を追加 | |
tab.setAttribute('id', 'tab1'); | |
tab.setAttribute('class', this.TAB_CLASS); | |
tab.innerHTML = this.favoriteStamps.map((function (_this) { | |
return function (stamp) { | |
return '<a href="' + stamp + '" class="' + _this.FAV_STAMP_CLASS + | |
'" onclick="return false;"></a>'; | |
} | |
})(this)).join(''); | |
// お気に入りのタブを追加 | |
this.stampDialog.appendChild(tab); | |
// 各スタンプ群でタブを構成し追加する | |
for (key in this.stampListMap) { | |
if (this.stampListMap.hasOwnProperty(key)) { | |
// タブのボタンを追加 | |
tabButton = document.createElement('a'); | |
tabButton.setAttribute('href', '#tab' + index); | |
tabButton.setAttribute('class', this.TAB_BUTTON_CLASS); | |
tabButton.setAttribute('onclick', 'window.LLL.changeTab(this.getAttribute("href").substring(1)); return false;'); | |
tabButton.textContent = key; | |
tabs.appendChild(tabButton); | |
// タブの本体を追加 | |
tab = document.createElement('div'); | |
tab.setAttribute('id', 'tab' + index); | |
tab.setAttribute('class', this.TAB_CLASS); | |
tab.innerHTML = this.stampListMap[key]; | |
this.stampDialog.appendChild(tab); | |
index++; | |
} | |
} | |
// タブのボタンエリアをダイアログの最初に追加 | |
tabs.setAttribute('id', this.TABS_AREA_ID); | |
this.stampDialog.insertBefore(tabs, this.stampDialog.firstChild); | |
// プログレス用のクラスを削除 | |
this.stampDialog.removeAttribute('class'); | |
// 初期のタブをセット | |
this.changeTab('tab1'); | |
}; | |
// タブを切り替える | |
this.changeTab = function (tabName) { | |
// 全タブを消す | |
[].forEach.call( | |
document.querySelectorAll('.' + this.TAB_CLASS), function (targetNode) { | |
targetNode.style.display = 'none'; | |
} | |
); | |
// 全ボタンのテキストカラーを元に戻す | |
[].forEach.call(document.querySelectorAll('.' + this.TAB_BUTTON_CLASS), | |
function (targetNode) { | |
targetNode.style.color = '#666'; | |
} | |
); | |
// 押したタブを表示する | |
document.getElementById(tabName).style.display = 'block'; | |
// 押したタブボタンのテキストカラーを変える | |
document.querySelector('[href="#' + tabName + '"]').style.color = | |
'goldenrod'; | |
}; | |
// 指定されたスタンプを挿入する関数 | |
this.insertStamp = function (command) { | |
// テキストボックスを取得 | |
var textBox = this.targetStampButton.parentNode.parentNode.previousSibling | |
.getElementsByTagName('textarea')[0]; | |
// テキストボックスにスタンプを追加 | |
textBox.value = textBox.value + '[](' + command + ')'; | |
}; | |
this.addCustomStyle = function() { | |
// ドットスタンプクラスのスタイルを定義 | |
GM_addStyle('.' + this.DOT_STAMP_CLASS + ' { margin-right: 2px; } '); | |
// スタンプを押した時の影 | |
GM_addStyle('.' + this.STAMP_SHADOW_CLASS + '{ box-shadow: 0px 0px 20px -5px rgba(0, 0, 0, 0.8); }'); | |
// ダイアログ用のスタイルを定義 | |
GM_addStyle('#' + this.STAMP_DIALOG_ID + ' { position: fixed; top: 10%; left: 10%; width: 80%; height: 80%; text-align: center; line-height: 25px; border: 1px solid gray; border-radius: 5px; box-shadow: 4px 4px 1px rgba(55, 55, 55, 0.3); z-index: 1; background-color: #fefefe; } '); | |
// プログレスダイアログ用のスタイルを定義 | |
GM_addStyle('.' + this.PROGRESS_DIALOG_CLASS + ' { top: 50% !important; left: 35% !important; width: 15% !important; height: 50px !important; line-height: 50px !important; overflow: hidden !important; } '); | |
/* --- ↓http://fukafuka295.jp/hp/hp_no8.html を改変↓ --- */ | |
/* ▼ タブ部分のマージンとパディング領域を設定 */ | |
GM_addStyle('#' + this.TABS_AREA_ID + ' { margin-left: 4px; margin-top: 4px; }'); | |
/* ▼ リンクをタブのように見せる */ | |
GM_addStyle('.' + this.TAB_BUTTON_CLASS + ' { display: block; width: 100px; float: left; margin: 0px 4px 4px 0px; padding: 3px; text-align: center; font-size:12px; text-decoration: none; background-color: #fafafa; border: 1px solid #dcdcdc; color: #666; font-weight: 600; }'); | |
GM_addStyle('#' + this.TABS_AREA_ID + ' a:hover { opacity: 0.7; filter: alpha(opacity=70); }'); | |
/* ▼ タブ中身のボックス */ | |
GM_addStyle('.' + this.TAB_CLASS + ' { height: 400px; clear: left; border-top: 2px solid #DDD; border-bottom: 2px solid #DDD; background-color: #fbfbfb; padding-top: 8px; overflow: auto; }'); | |
/* --- ↑http://fukafuka295.jp/hp/hp_no8.html を改変↑ --- */ | |
// ダイアログを最前面にするため,他の高さを0にする | |
GM_addStyle('.RESDialogSmall > h3 { z-index: 0 !important; } #header { z-index: 0 !important; } #sr-autocomplete-area { z-index: 0 !important; } '); | |
// この要素群はスタイル変更が効かなかったので動的に変更 | |
[].forEach.call(document.querySelectorAll('[data-res-css]'), | |
function (targetNode) { | |
targetNode.style.zIndex = 0; | |
}); | |
}; | |
// スタンプダイアログ | |
this.stampDialog = document.createElement('div'); | |
// スタンプ一覧 | |
this.stampListMap = {}; | |
// お気に入りのスタンプ | |
this.favoriteStamps = []; | |
// 長押し用のタイマー | |
this.longClickTimer; | |
// 直近のスタンプ表示ボタン | |
this.targetStampButton; | |
}; | |
// 保存したデータを取得 | |
if (!!GM_getValue(lll.DATA_KEY)) { | |
lll.favoriteStamps = GM_getValue(lll.DATA_KEY).split(lll.SPLIT_WORD); | |
} | |
// 独自のスタイルを適用 | |
lll.addCustomStyle(); | |
// 全テキストボックス下部に各ボタンを挿入 | |
[].forEach.call( | |
document.querySelectorAll('[class="usertext-buttons"]'), function (node) { lll.generateButtons(node); } | |
); | |
// 全テキストボックス上部にドットスタンプのパレットを挿入 | |
[].forEach.call(document.querySelectorAll('div[class="md"]'), function (node) { lll.generatePalettes(node); }); | |
// ダイアログを生成し非表示にしておく | |
lll.stampDialog.setAttribute('id', lll.STAMP_DIALOG_ID); | |
lll.stampDialog.style.display = 'none'; | |
document.body.appendChild(lll.stampDialog); | |
/** | |
各ボタンが押されたら押されたノードを渡して関数を実行 | |
ドキュメント全体にリスナをセットすることで後からの追加にも対応 | |
*/ | |
document.addEventListener('click', function (e) { | |
var targetNode = e.target, | |
targetClass = targetNode.getAttribute('class'); | |
// 各ケースに引っかかったらダイアログを閉じる処理をスルーさせるためにreturnする | |
switch (targetClass) { | |
case lll.STAMP_CONVERT_CLASS: | |
lll.convertStrToOneStamp(targetNode); | |
return; | |
case lll.TREMBLER_CLASS: | |
lll.tremleString(targetNode); | |
return; | |
case lll.DOT_STAMP_CLASS: | |
lll.insertDotStamp(targetNode); | |
return; | |
case lll.TAB_BUTTON_CLASS: | |
return; // ここでreturnしないとタブをクリックした時にスタンプダイアログがこの後の処理で消える。 | |
case lll.STAMP_BTN_CLASS: | |
// 直近のスタンプ表示ボタンを格納 | |
lll.targetStampButton = targetNode; | |
lll.displayStamps(); | |
return; | |
} | |
if ( | |
// ダイアログが出ていて,押したものがスタンプでもお気に入りのスタンプでもタブの中身でもない場合 | |
// スタンプとお気に入りのスタンプは後述のリスナーに任せる | |
lll.stampDialog.style.display === 'block' && | |
targetClass !== lll.STAMP_CLASS && | |
targetClass !== lll.FAV_STAMP_CLASS && | |
targetClass !== lll.TAB_CLASS && | |
// ダイアログの中では消えてほしくないのでここでチェック | |
( | |
!!!targetNode.getAttribute('id') || | |
targetNode.getAttribute('id') !== lll.STAMP_DIALOG_ID | |
) | |
) { | |
// ダイアログを消す | |
lll.stampDialog.style.display = 'none'; | |
} | |
}, false); | |
// 長押し判別用:押されたのを感知するリスナー | |
document.addEventListener('mousedown', function (e) { | |
var targetNode = e.target, | |
targetClass = targetNode.getAttribute('class'), | |
command = targetNode.getAttribute('href'), | |
targetClassList = targetNode.classList; | |
// スタンプ一覧のスタンプかお気に入りスタンプだった場合 | |
if (targetClass === lll.STAMP_CLASS || targetClass === lll.FAV_STAMP_CLASS) { | |
// 対象のスタンプに影を付ける | |
targetClassList.add(lll.STAMP_SHADOW_CLASS); | |
// タイマーをセット(500ミリ秒後に実行) | |
lll.longClickTimer = setTimeout(function() { | |
// スタンプの影を消す | |
targetClassList.remove(lll.STAMP_SHADOW_CLASS); | |
// スタンプ一覧のスタンプだったらそれを処理 | |
if (targetClass === lll.STAMP_CLASS) { | |
// 同じものだったら何もしない | |
if (lll.favoriteStamps.indexOf(command) > -1) { | |
return; | |
} | |
lll.favoriteStamps.push(command); | |
// 一度出したダイアログの中身は変えないのでここで動的に追加 | |
document.getElementById('tab1').innerHTML += '<a href="' + command + | |
'" class="' + lll.FAV_STAMP_CLASS + '" onclick="return false;"></a>'; | |
} | |
// お気に入りのスタンプだったらそれを削除 | |
else { | |
lll.favoriteStamps = lll.favoriteStamps.filter(function (stamp) { | |
return (stamp !== command); | |
}); | |
// ダイアログからも削除 | |
targetNode.parentNode.removeChild(targetNode); | |
} | |
// 最終的な配列を区切り文字で区切って保存する | |
GM_setValue(lll.DATA_KEY, lll.favoriteStamps.join(lll.SPLIT_WORD)); | |
}, 500); | |
} | |
}, false); | |
// 長押し判別用:離されたのを感知するリスナー | |
document.addEventListener('mouseup', function (e) { | |
var targetNode = e.target; | |
// スタンプ一覧のスタンプかお気に入りスタンプだった場合 | |
if (targetNode.classList.contains(lll.STAMP_SHADOW_CLASS)) { | |
// スタンプの影を消す | |
targetNode.classList.remove(lll.STAMP_SHADOW_CLASS); | |
// タイマーをクリアして長押しのイベントが実行されないようにする | |
clearTimeout(lll.longClickTimer); | |
// ダイアログを消す | |
lll.stampDialog.style.display = 'none'; | |
// コマンドを渡す | |
lll.insertStamp(targetNode.getAttribute('href')); | |
} | |
}, false); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment