•  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
r2069 vs r2070
1
[[분류:집단창작]][[분류:끝말잇기]]
2
[include(틀:토론 중, 토론번호=Y8ytFAdFzFRa4cGDark1E5)]
3
{{{+1 {{{#fc0000 주의: 번에 1~2개씩만 적어주세요! 한번 적은 5 다시 적을 있습니다.}}}}}}
4
[include(틀:집단창작)]
1
// ==UserScript==
2
// @name NamuFix
3
// @namespace http://litehell.info/
4
// @description 나무위키 등 더시드 사용 위키의 편 인터페이스 등을 개선합니다.
5
// @include https://namu.wiki/*
6
// @include https://alphawiki.org/*
7
// @include https://www.alphawiki.org/*
8
// @include https://theseed.io/*
9
// @include https://board.namu.wiki/*
10
// @version 180630.0
11
// @author LiteHell
12
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
13
// @require https://cdn.rawgit.com/LiteHell/NamuFix/5326c9aada134f65bba171d12f5ca5d042fd4fca/korCountryNames.js
14
// @require https://cdn.rawgit.com/LiteHell/NamuFix/0ea78119c377402a10bbdfc33365c5195ce7fccc/FlexiColorPicker.js
15
// @require https://cdn.rawgit.com/Caligatio/jsSHA/v2.3.1/src/sha.js
16
// @require https://cdn.rawgit.com/zenozeng/color-hash/v1.0.3/dist/color-hash.js
17
// @require https://cdn.rawgit.com/ben-liang/pnglib/91a91b7f840fdf19ef34a32df8051f2178957293/pnglib.js
18
// @require https://cdn.rawgit.com/stewartlord/identicon.js/7c4b4efdb7e2aba458eba14b24ba14e8e2bcdb2a/identicon.js
19
// @require https://cdn.jsdelivr.net/npm/[email protected]
20
// @require https://cdn.rawgit.com/LiteHell/TooSimplePopupLib/7f2a8a81f11f980c1dfa6b5b2213cd38b8bbde3c/TooSimplePopupLib.js
21
// @require https://cdn.rawgit.com/wkpark/jsdifflib/dc19d085db5ae71cdff990aac8351607fee4fd01/difflib.js
22
// @require https://cdn.rawgit.com/wkpark/jsdifflib/dc19d085db5ae71cdff990aac8351607fee4fd01/diffview.js
23
// @require https://cdn.rawgit.com/LiteHell/NamuFix/d6bcc377c563c2745af0b8078ce5dc97f2c19910/namuapi.js
24
// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.3/moment-with-locales.min.js
25
// @require https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.14/moment-timezone-with-data.min.js
26
// @require https://cdnjs.cloudflare.com/ajax/libs/async/2.6.0/async.min.js
27
// @connect cdn.rawgit.com
28
// @connect cdnjs.cloudflare.com
29
// @connect jsdelivr.net
30
// @connect api.github.com
31
// @connect ipinfo.io
32
// @connect wtfismyip.com
33
// @connect www.googleapis.com
34
// @connect web.archive.org
35
// @connect archive.is
36
// @connect www.vpngate.net
37
// @connect namufix.wikimasonry.org
38
// @connect phpgongbu.ga
39
// @connect twitter.com
40
// @connect facebook.com
41
// @connect www.facebook.com
42
// @grant GM_addStyle
43
// @grant GM_openInTab
44
// @grant GM_xmlhttpRequest
45
// @grant GM_getValue
46
// @grant GM_setValue
47
// @grant GM_deleteValue
48
// @grant GM_listValues
49
// @grant GM_info
50
// @grant GM_getResourceURL
51
// @grant GM.openInTab
52
// @grant GM.xmlHttpRequest
53
// @grant GM.getValue
54
// @grant GM.setValue
55
// @grant GM.deleteValue
56
// @grant GM.listValues
57
// @grant GM.info
58
// @grant GM.getResourceUrl
59
// @run-at document-end
60
// @noframes
61
// ==/UserScript==
62
/* jshint ignore:start */
63
/*
64
Copyright (c) 2015 LiteHell
565
6
[목차]
66
Permission is hereby granted, free of charge, to any person
67
obtaining a copy of this software and associated documentation
68
files (the "Software"), to deal in the Software without
69
restriction, including without limitation the rights to use,
70
copy, modify, merge, publish, distribute, sublicense, and/or sell
71
copies of the Software, and to permit persons to whom the
72
Software is furnished to do so, subject to the following
73
conditions:
774
8
== 설명 ==
9
끝말잇기를 이용한 알파위키 항목명의 특정한 방식으로의 정리 프로젝트.
75
The above copyright notice and this permission notice shall be
76
included in all copies or substantial portions of the Software.
1077
11
== 항목의 (존재) 의의 ==
12
* '''알파위키 문서 [PageCount(문서)]개(실시간 기준)를 거의 다 쓸 때까지 얼마큼 끝말잇기가 가능한지 도전하는 근성 프로젝트.'''
13
* 끝말잇기하다 안 되면 문서를 만들어 버리거나, 어떤 글자로 시작하는 문서를 찾다가 '어, 이런 문서가 있네!' 할 수 있으므로 문서 기여 촉진 효과.
14
* 알파위키 홍보 효과. 5천개 5만개만 되어도 대단하다. 20만개쯤 가면 한국기네스에도 올라갈 수도 있다. [[나비 효과|한 사람의 참여가 큰 파도를 일으킵니다.]]
15
* '''심심하면 오는 곳.'''
16
== 규칙 ==
17
* 이 끝말잇기는 계속되어야 합니다. 알파위키의 문서를 다 쓸 때까지.
18
* [[한방단어|이어질 게 없는 단어]] 금지. 만약 한방 단어가 적혀있다고 생각되실 경우 이어질 게 없는지를 본인의 생각으로만 판단하지 말고 반드시 검색창을 통해 해당 글자로 시작하는 문서가 없는 것을 확인 후 삭제하시기 바랍니다.
19
* 끝말잇기에는 알파위키에 문서가 있는 것들만 사용할 수 있습니다. 없는것은 직접 만들어주세요.(단어가 아닌 문장이나 다어절도 가능.)
20
* '''한 번 나온 단어는 또 나올 수 없습니다.''' 그렇다고 진행이 된 모든 항목을 지우지는 말아주시기 바랍니다. 웬만하면 그 단어를 수습하는 방향으로 해 주시는 게 더 좋습니다. 본인이 적고자 하는 단어가 이미 있는지 먼저 Ctrl + F 눌러 먼저 확인 적어주시기 바랍니다.
21
* 단어 추가 시엔 기존 문서의 틀에 맞춰서 추가해주세요.
22
* 위의 기준에 맞지 않는 단어라면 지울 수 있습니다.[br](예) 이어질 수 없는 단어.
23
* 괄호 안의 글자로는 잇지 않습니다.(예) [[신의 한 수(영화)]], [[신의 한 수(JTBC)]]의 경우 '''영화의 '화'나 JTBC의 'C'가 아닌 '수'로 시작하는 단어로 이어주세요'''. 괄호 속의 글자는 포함하지 않습니다.
24
* 편집시 1~2개의 문서만 적어주시기 바랍니다. 또 연속적으로 계속 혼자서 이어적지 마시기 바랍니다. 여러개를 적었다가 [[동시 수정]]으로 인해 로그가 꼬일 수도 있습니다.
25
* 단, 본인이 하나의 문서를 적었는데 5분 동안 그 누구도 추가하지 않는다면 하나 더 적을 수 있습니다.
26
* [[http://korean.go.kr/front/page/pageView.do?page_id=P000066&mn_id=30|국립국어원의 한글 맞춤법 3장 5절에 따른 두음법칙]]을 허용합니다.
78
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
79
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
80
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
81
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
82
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
83
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
84
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
85
OTHER DEALINGS IN THE SOFTWARE.
86
*/
2787
28
== 끝말잇기 템플릿 생성기 ==
29
[[https://ideone.com/ZNHyfF|알파위키 끝말잇기 템플릿 생성기]]에서 fork를 누른 stdin "(시작 숫자) (문단 개수)" 형식으로 입력한 ''Run'' 클릭
30
템플릿은 칸이 100개쯤 남았을 500개씩 추가하도록 하자. 템플릿 생성기가 공개된 이상 굳이 미리 많이 만들어 놓을 필요는 없다.
88
try {
89
if (typeof GM_addStyle !== 'undefined') {
90
var GM_addStyle = function (text) {
91
var style = document.createElement("style");
92
style.innerHTML = text;
93
document.head.appendChild(style);
94
}
95
}
3196
32
== ==
33
* '''단어가 나온 건지 나온 건지 확인하기 힘들다면 Ctrl + F키로 단어를 확인해 보시면 편리합니다. 모바일의 경우에는 일부 브라우저에서 기능이 메뉴를 통해 지원되며 그렇지 않은 경우는 해당 문서의 역링크에 들어가십시오.'''(이게 가장 중요합니다.)
34
* 작성한 문서에 빨간 불이 켜져 있으면 알파위키에서 삭제되었거나 존재하지 않는 문서를 말합니다. 미리보기를 통해 미리 확인해 보시기 바랍니다.
35
== 끝말잇기 ==
36
=== 1 ~ 100(완료) ===
37
|| 1 || [[알파위키]] || 2 || [[키보드]] ||
38
|| 3 || [[드론 쉽]] || 4 || [[쉽게 씌어진 시]] ||
39
|| 5 || [[시루떡]] || 6 || [[떡칠]] ||
40
|| 7 || [[칠판]] || 8 || [[판교]] ||
41
|| 9 || [[교장]] || 10 || [[장성]] ||
42
|| 11 || [[성 호르몬]] || 12 || [[몬스터]] ||
43
|| 13 || [[터빈]] || 14 || [[빈 국제공항]] ||
44
|| 15 || [[항구구역]] || 16 || [[역사]] ||
45
|| 17 || [[사당]] || 18 || [[당백전]] ||
46
|| 19 || [[전시정]] || 20 || [[정부]] ||
47
|| 21 || [[부정맥]] || 22 || [[맥주]] ||
48
|| 23 || [[주량]] || 24 || [[양식]] ||
49
|| 25 || [[식도]] || 26 || [[도시]] ||
50
|| 27 || [[시자리오]] || 28 || [[오송]] ||
51
|| 29 || [[송신탑]] || 30 || [[탑골공원]] ||
52
|| 31 || [[원주민]] || 32 || [[민선]] ||
53
|| 33 || [[선사시대]] || 34 || [[대통령]] ||
54
|| 35 || [[영부인]] || 36 || [[인천]] ||
55
|| 37 || [[천리마]] || 38 || [[마산]] ||
56
|| 39 || [[산사나무]] || 40 || [[무산]] ||
57
|| 41 || [[산삼]] || 42 || [[삼각관계]] ||
58
|| 43 || [[계산]] || 44 || [[산화]] ||
59
|| 45 || [[화염]] || 46 || [[염산]] ||
60
|| 47 || [[산와머니]] || 48 || [[이산화탄소]] ||
61
|| 49 || [[소사원시선]] || 50 || [[선바위역]] ||
62
|| 51 || [[역주행]] || 52 || [[행주대첩]] ||
63
|| 53 || [[첩보원]] || 54 || [[원장]] ||
64
|| 55 || [[장님]] || 56 || [[님다의 난]] ||
65
|| 57 || [[난쟁이]] || 58 || [[이빨]] ||
66
|| 59 || [[빨래판]] || 60 || [[판장온천]] ||
67
|| 61 || [[천사]] || 62 || [[사탕]] ||
68
|| 63 || [[탕정역]] || 64 || [[역발상]] ||
69
|| 65 || [[상일동역]] || 66 || [[역삼동]] ||
70
|| 67 || [[동구릉]] || 68 || [[능곡역]] ||
71
|| 69 || [[역마살]] || 70 || [[살생부]] ||
72
|| 71 || [[부전]] || 72 || [[전수]] ||
73
|| 73 || [[수영]] || 74 || [[영자]] ||
74
|| 75 || [[자갈치]] || 76 || [[치약]] ||
75
|| 77 || [[약수]] || 78 || [[수소]] ||
76
|| 79 || [[소요산]] || 80 || [[산돼지]] ||
77
|| 81 || [[지구온난화]] || 82 || [[화명]] ||
78
|| 83 || [[명장]] || 84 || [[장자연]] ||
79
|| 85 || [[연날리기]] || 86 || [[기린]] ||
80
|| 87 || [[인간]] || 88 || [[간디]] ||
81
|| 89 || [[디스코]] || 90 || [[코디]] ||
82
|| 91 || [[디지털미디어시티]] || 92 || [[티본]] ||
83
|| 93 || [[본인서명사실 확인 등에 관한 법률]] || 94 || [[율리우스 벨키스크 하웨이]] ||
84
|| 95 || [[이적죄]] || 96 || [[죄는 미워하되 사람은 미워하지 말라]] ||
85
|| 97 || [[라볶이]] || 98 || [[이 xx에는 슬픈 전설이 있어]] ||
86
|| 99 || [[어떤 마술의 금서목록 포터블]] || 100 || [[블레스레이터]] ||
97
function NFStorage() {
98
function jsonParsable(str) {
99
try {
100
JSON.parse(str);
101
return true;
102
} catch (err) {
103
return false;
104
}
105
}
106
var discards = ['save', 'load', 'delete', 'export', 'import'];
107
this.save = async function () {
108
for (var i in this) {
109
if (discards.indexOf(i) != -1) continue;
110
await GM.setValue('SET_' + i, JSON.stringify(this[i]));
111
}
112
};
113
this.load = async function () {
114
var sets = await GM.listValues();
115
for (var i = 0; i < sets.length; i++) {
116
var now = sets[i];
117
if (now.indexOf('SET_') != 0) continue;
118
if (discards.indexOf(now.substring(4)) != -1) continue;
119
let sval = await GM.getValue(now);
120
this[now.substring(4)] = jsonParsable(sval) ? JSON.parse(sval) : sval;
121
}
122
};
123
this.delete = async function (key) {
124
if (discards.indexOf(key) != -1) return;
125
await GM.deleteValue('SET_' + key);
126
delete this[key];
127
};
128
this.export = async function (excludes) {
129
let sets = await GM.listValues();
130
let result = {};
131
for (let i of sets) {
132
if (i.indexOf('SET_') != 0) continue;
133
let setname = i.substring(4)
134
if (discards.includes(setname) || excludes.includes(setname)) continue;
135
result[setname] = await GM.getValue(i);
136
}
137
return result;
138
};
139
this.import = async function (data) {
140
for (let i in data) {
141
await GM.setValue('SET_' + i, data[i]);
142
}
143
}
144
}
145
if (location.host === 'board.namu.wiki') {
146
async function runBoardFix() {
147
let SET = new NFStorage();
148
await SET.load();
149
// 시간대 자동 변경
150
if (SET.noLocaltimeOnNamuBoard !== false) {
151
let times = document.querySelectorAll('.read_header > .meta > .time, .fbMeta .time');
152
let origTimezone = "UTC" // America/Asuncion 아님.
153
for (let i = 0; i < times.length; i++) {
154
let t = moment.tz(times[i].textContent.trim(), "YYYY.MM.DD HH:mm", origTimezone);
155
times[i].textContent = t.tz(moment.tz.guess()).format("YYYY.MM.DD HH:mm z")
156
}
157
if (times.length !== 0)
158
console.log(`[NamuFix] 게시판 시간대 변경 완료. ${origTimezone} > ${moment.tz.guess()}`);
159
}
160
// 아카이브 버튼
161
if (document.querySelector('.viewDocument .read_footer .btnArea a[onclick]') !== null) {
162
let extraMenuLink = document.querySelector('.viewDocument .read_footer .btnArea a[onclick]');
163
let documentId = /document_([0-9]+)/.exec(extraMenuLink.className)[1];
164
let archiveLink = document.createElement("a");
165
archiveLink.href = "#";
166
archiveLink.target = "_blank";
167
archiveLink.textContent = "아카이브";
168
let clickHandler = (evt) => {
169
evt.preventDefault();
170
archiveLink.textContent = "(진중)";
171
archiveLink.removeAttribute("href");
172
archiveLink.removeEventListener("click", clickHandler);
173
GM.xmlHttpRequest({
174
method: 'POST',
175
url: 'https://phpgongbu.ga/archive/',
176
headers: {
177
"Content-Type": "application/x-www-form-urlencoded",
178
"User-Agent": `Mozilla/5.0 (compatible; NamuFix/${GM.info.script.version}`
179
},
180
data: `board_num=${documentId}`,
181
onload: (res) => {
182
GM.openInTab(res.finalUrl);
183
archiveLink.textContent = "아카이브됨";
184
archiveLink.href = res.finalUrl;
185
}
186
});
187
};
188
archiveLink.addEventListener("click", clickHandler);
189
extraMenuLink.parentNode.insertBefore(archiveLink, extraMenuLink.nextSibling);
190
}
191
// 댓글 상용구
192
if (document.querySelector('.write_comment') && SET.commentMacros) {
193
console.log('[NamuFix] 댓글창 감지됨.')
194
let writeAuthorDiv = document.querySelector('.write_author');
195
for(let i of SET.commentMacros.split(',')) {
196
let macroName = i.split(':')[0];
197
let macroContentParts = i.split(':');
198
macroContentParts.shift();
199
let macroContent = macroContentParts.join(':');
200
let macroBtn = document.createElement("button");
201
console.log(`[NamuFix] 매크로 버튼 추가중 (이름: ${macroName}, 내용: ${macroContent})`)
202
macroBtn.setAttribute('type', 'button');
203
macroBtn.innerHTML = '상용구 (' + macroName + ')';
204
macroBtn.addEventListener('click', (evt) => {
205
evt.preventDefault();
206
let xeEditor = document.querySelector('.xpress-editor'),
207
hiddenContentInput = document.querySelector('.write_comment > input[name="content"]'),
208
writeForm = document.querySelector('.write_comment > .write_form');
209
// XE에디터 제거
210
xeEditor.parentNode.removeChild(xeEditor);
211
// 기존 content input 제거
212
hiddenContentInput.parentNode.removeChild(hiddenContentInput);
213
// content input 추가
214
let newContentInput = document.createElement('input');
215
newContentInput.setAttribute('type', 'hidden');
216
newContentInput.name = "content";
217
newContentInput.value = macroContent;
218
writeForm.appendChild(newContentInput);
219
// 댓글 작성
220
document.querySelector('.write_author button[type="submit"]').click();
221
})
222
writeAuthorDiv.appendChild(macroBtn,writeAuthorDiv.lastChild);
223
}
224
}
225
}
226
runBoardFix();
227
} else
228
(async function () {
229
console.log(`[NamuFix] 현재 버전 : ${GM.info.script.version}`);
230
if (location.hostname == 'no-ssl.namu.wiki')
231
location.hostname = 'namu.wiki';
87232
88
=== 101 ~ 200(완료) ===
89
|| 101 || [[터널]] || 102 || [[널빤지]] ||
90
|| 103 || [[지금 남편이 무슨 말을 하는 거지?]] || 104 || [[지리]] ||
91
|| 105 || [[이리]] || 106 || [[이기야]] ||
92
|| 107 || [[야기]] || 108 || [[기동신선조 불타라 검]] ||
93
|| 109 || [[검투사]] || 110 || [[사쿠라코 씨의 발밑에는 시체가 묻혀 있다]] ||
94
|| 111 || [[다대포항]] || 112 || [[항문]] ||
95
|| 113 || [[문산]] || 114 || [[산맥]] ||
96
|| 115 || [[맥웰 인터내셔널]] || 116 || [[널뛰기]] ||
97
|| 117 || [[기성품]] || 118 || [[품질관리]] ||
98
|| 119 || [[이월]] || 120 || [[월곶 분기점]] ||
99
|| 121 || [[점순이]] || 122 || [[이천]] ||
100
|| 123 || [[천송이]] || 124 || [[이하]] ||
101
|| 125 || [[하리수]] || 126 || [[수원]] ||
102
|| 127 || [[원인에 있어서 자유로운 행]] || 128 || [[위례성]] ||
103
|| 129 || [[성범죄]] || 130 || [[죄수]] ||
104
|| 131 || [[수]] || 132 || [[서산 공기총 난사 사건]] ||
105
|| 133 || [[건천]] || 134 || [[천 천안문 법륜공 이홍지 프리 티베트 유효파]] ||
106
|| 135 || [[파주]] || 136 || [[주장]] ||
107
|| 137 || [[장물죄]] || 138 || [[죄는 씻을 있습니다]] ||
108
|| 139 || [[다대포해수욕장]] || 140 || [[장신구]] ||
109
|| 141 || [[구리]] || 142 || [[이 세상은 이미 내가 구해 부와 권력을 손에 넣었고 여기사나 여마왕과 성에서 즐겁게 살고있으니 나 이외의 용사는 이제 더이상 이세계로 오지 마시길 바랍니다]] ||
110
|| 143 || [[다람쥐]] || 144 || [[쥐 이야기 조지와 제럴드의 모험]] ||
111
|| 145 || [[험멜]] || 146 || [[멜ㄴㅇㄴ]] ||
112
|| 147 || [[ㄴㄴ]] || 148 || [[ㄴㄱㄴㄱㄴㄱ]] ||
113
|| 149 || [[ㄱ-]] || 150 || [[-4CA]] ||
114
|| 151 || [[A-train]] || 152 || [[namu]] ||
115
|| 153 || [[UN 사무총장]] || 154 || [[장흥]] ||
116
|| 155 || [[흥부]] || 156 || [[부식성 장갑]] ||
117
|| 157 || [[갑천]] || 158 || [[천지]] ||
118
|| 159 || [[지상]] || 160 || [[상계]] ||
119
|| 161 || [[계란]] || 162 || [[난카이 전기철도]] ||
120
|| 163 || [[도돌이표]] || 164 || [[표범]] ||
121
|| 165 || [[범인은닉죄]] || 166 || [[죄]] ||
122
|| 167 || [[죄와벌]] || 168 || [[벌교]] ||
123
|| 169 || [[교과서]] || 170 || [[서아시아 축구 선수권 대회]] ||
124
|| 171 || [[회기역]] || 172 || [[역도산]] ||
125
|| 173 || [[산본역]] || 174 || [[역북동]] ||
126
|| 175 || [[동두천시]] || 176 || [[시청역]] ||
127
|| 177 || [[역장]] || 178 || [[장미]] ||
128
|| 179 || [[미분]] || 180 || [[분당신도시]] ||
129
|| 181 || [[시상]] || 182 || [[상어]] ||
130
|| 183 || [[어천삼각선]] || 184 || [[선적분]] ||
131
|| 185 || [[분당구]] || 186 || [[구원]] ||
132
|| 187 || [[원주]] || 188 || [[주사위]] ||
133
|| 189 || [[위병소]] || 190 || [[소라]] ||
134
|| 191 || [[라이]] || 192 || [[이매]] ||
135
|| 193 || [[매개변수]] || 194 || [[수학]] ||
136
|| 195 || [[학자]] || 196 || [[자라투스트라는 이렇게 말했다]] ||
137
|| 197 || [[다다익선]] || 198 || [[선암신호장]] ||
138
|| 199 || [[장티푸스]] || 200 || [[스리랑카민주사회주의공화국]] ||
233
function insertCSS(url) {
234
// 나무 CSP 빡세getResourceUrl 쓰면 오류남.
235
GM.xmlHttpRequest({
236
method: 'GET',
237
url: url,
238
onload: (res) => {
239
let styleTag = document.createElement("style");
240
styleTag.innerHTML = res.responseText;
241
document.head.appendChild(styleTag);
242
}
243
});
244
}
139245
140
=== 201 ~ 300(완료) ===
141
|| 201 || [[국회 국방위원회 회식 난투극 사건]] || 202 || [[건국 이래 최고로 자랑스런 대한민국 전설의 미남 개그맨 정명훈 나가신다 길을 비키고 머리를 조아리고 만세를 외치고 풍악을 울려 어깨춤을 추고 콧노래를 부르며 스텝을 밟고 전 재산을 바치고 큰 절을 하여라 하지만 두 번은 안 된다~ 들은 사람 손! 내리지 말고 흔드세요~ 안녕~]] ||
142
|| 203 || [[~~설]] || 204 || [[설화명곡역]] ||
143
|| 205 || [[역수]] || 206 || [[수능시험]] ||
144
|| 207 || [[험버트 험버트]] || 208 || [[트겐위한]] ||
145
|| 209 || [[한일 월드컵]] || 210 || [[컵스]] ||
146
|| 211 || [[스카우트]] || 212 || [[트리 오브 라이프]] ||
147
|| 213 || [[프랑스]] || 214 || [[스카웃]] ||
148
|| 215 || [[웃찾사]] || 216 || [[사이]] ||
149
|| 217 || [[이사 시소코]] || 218 || [[코렁탕]] ||
150
|| 219 || [[탕평책]] || 220 || [[책바다]] ||
151
|| 221 || [[다리미]] || 222 || [[미나 페이셰스]] ||
152
|| 223 || [[스핑크스]] || 224 || [[스타일]] ||
153
|| 225 || [[일제 강점기]] || 226 || [[기가바이트]] ||
154
|| 227 || [[트집 잡을 수 없는 러브 코미디]] || 228 || [[디스크]] ||
155
|| 229 || [[크레용 신짱]] || 230 || [[짱짱맨]] ||
156
|| 231 || [[맨해튼]] || 232 || [[튼튼하게 만들었습니다]] ||
157
|| 233 || [[다비치]] || 234 || [[치질]] ||
158
|| 235 || [[질내사정]] || 236 || [[정력]] ||
159
|| 237 || [[역곡]] || 238 || [[곡반정동차고지]] ||
160
|| 239 || [[지랄병]] || 240 || [[병점]] ||
161
|| 241 || [[점박이: 한반도의 공룡 3D]] || 242 || [[D-WAR]] ||
162
|| 243 || [[Railjet]] || 244 || [[TWICE]] ||
163
|| 245 || [[EXO]] || 246 || [[O형 주계열성]] ||
164
|| 247 || [[성장판]] || 248 || [[판데모니엄]] ||
165
|| 249 || [[엄궁역]] || 250 || [[역사 갤러리]] ||
166
|| 251 || [[이곡]] || 252 || [[곡사포]] ||
167
|| 253 || [[포항]] || 254 || [[항성]] ||
168
|| 255 || [[성공]] || 256 || [[공기수송]] ||
169
|| 257 || [[송장]] || 258 || [[장충체육관]] ||
170
|| 259 || [[관객모독]] || 260 || [[독]] ||
171
|| 261 || [[독극물]] || 262 || [[물방개]] ||
172
|| 263 || [[개새끼]] || 264 || [[끼어들기]] ||
173
|| 265 || [[기 드 뤼지냥]] || 266 || [[냥코대전쟁]] ||
174
|| 267 || [[쟁글리시]] || 268 || [[시월드]] ||
175
|| 269 || [[드라이아이스]] || 270 || [[스파르타]] ||
176
|| 271 || [[타지마할]] || 272 || [[할라피뇨]] ||
177
|| 273 || [[요로결석]] || 274 || [[석유]] ||
178
|| 275 || [[유교]] || 276 || [[교황]] ||
179
|| 277 || [[황금]] || 278 || [[금광]] ||
180
|| 279 || [[광주송정역]] || 280 || [[역린]] ||
181
|| 281 || [[인하대]] || 282 || [[대박]] ||
182
|| 283 || [[박수칠 때 떠나라]] || 284 || [[나무위키]] ||
183
|| 285 || [[키타이 고로드]] || 286 || [[드래곤]] ||
184
|| 287 || [[곤명]] || 288 || [[명작]] ||
185
|| 289 || [[작전]] || 290 || [[전자레인지]] ||
186
|| 291 || [[지랄하고 자빠졌네]] || 292 || [[네브라스카]] ||
187
|| 293 || [[카 2]] || 294 || [[2½톤 트럭]] ||
188
|| 295 || [[럭키]] || 296 || [[키위]] ||
189
|| 297 || [[위키 갤러리]] || 298 || [[리코쳇]] ||
190
|| 299 || [[쳇바퀴]] || 300 || [[퀴즈]] ||
246
insertCSS("https://cdn.rawgit.com/LiteHell/NamuFix/284db44ac1d89ff0cbd1155c3372db38be3bc140/NamuFix.css");
247
insertCSS("https://cdn.rawgit.com/LiteHell/TooSimplePopupLib/edad912e28eeacdc3fd8b6e6b7ac5cafc46d95b6/TooSimplePopupLib.css");
248
insertCSS("https://cdn.rawgit.com/wkpark/jsdifflib/dc19d085db5ae71cdff990aac8351607fee4fd01/diffview.css");
249
console.log('[NamuFix] CSS 삽입됨.');
191250
192
=== 301 ~ 400(완료) ===
193
|| 301 || [[즈지스와프 벡신스키]] || 302 || [[키위무나]] ||
194
|| 303 || [[나 꿍꼬도 기싱 꿍꼬또]] || 304 || [[또 속냐]] ||
195
|| 305 || [[냐옹]] || 306 || [[옹호극]] ||
196
|| 307 || [[극락강]] || 308 || [[강시라]] ||
197
|| 309 || [[나전역]] || 310 || [[역관광]] ||
198
|| 311 || [[광부]] || 312 || [[부산]] ||
199
|| 313 || [[산다라박]] || 314 || [[박근혜]] ||
200
|| 315 || [[혜구]] || 316 || [[구성]] ||
201
|| 317 || [[성찬례]] || 318 || [[예천]] ||
202
|| 319 || [[천보성]] || 320 || [[성균관대]] ||
203
|| 321 || [[대장]] || 322 || [[장전]] ||
204
|| 323 || [[전쟁]] || 324 || [[쟁반]] ||
205
|| 325 || [[반전]] || 326 || [[전포]] ||
206
|| 327 || [[포르쉐]] || 328 || [[쉐도우]] ||
207
|| 329 || [[우보]] || 330 || [[보쓴꼬]] ||
208
|| 331 || [[꼬깔콘]] || 332 || [[콘 에어]] ||
209
|| 333 || [[어린이회관]] || 334 || [[관리자]] ||
210
|| 335 || [[자군야포]] || 336 || [[포클랜드-말비나스 제도]] ||
211
|| 337 || [[도라산역]] || 338 || [[역법]] ||
212
|| 339 || [[법전]] || 340 || [[전국동시지방선거]] ||
213
|| 341 || [[거제]] || 342 || [[제주도]] ||
214
|| 343 || [[도포]] || 344 || [[포천]] ||
215
|| 345 || [[천리행군]] || 346 || [[군북역]] ||
216
|| 347 || [[역내포괄적경제동반자협정]] || 348 || [[정평]] ||
217
|| 349 || [[평신도]] || 350 || [[도계역]] ||
218
|| 351 || [[역도]] || 352 || [[도봉산]] ||
219
|| 353 || [[산융산]] || 354 || [[업동신호장]] ||
220
|| 355 || [[장 마티유 필리베르 세뤼리]] || 356 || [[에미야 물좀다]] ||
221
|| 357 || [[오바마]] || 358 || [[마천]] ||
222
|| 359 || [[천방지추마골피]] || 360 || [[피자]] ||
223
|| 361 || [[자궁]] || 362 || [[궁륭산병성배 세계 여자바둑 선수권 대회]] ||
224
|| 363 || [[회덕역]] || 364 || [[역사채널e]] ||
225
|| 365 || [[e편한세상]] || 366 || [[상하이잉숑진비창 유한공사]] ||
226
|| 367 || [[사쿠라장의 애완 그녀]] || 368 || [[여천역]] ||
227
|| 369 || [[역학계몽요해]] || 370 || [[해운대]] ||
228
|| 371 || [[대한민국]] || 372 || [[국제금융센터부산은행역]] ||
229
|| 373 || [[역대 콘스탄티노플 총대주교]] || 374 || [[교!교!교!]] ||
230
|| 375 || [[교대]] || 376 || [[대선]] ||
231
|| 377 || [[선유도]] || 378 || [[도선여객]] ||
232
|| 379 || [[객관식]] || 380 || [[식사]] ||
233
|| 381 || [[사하라 아랍 민주 공화국]] || 382 || [[국뽕]] ||
234
|| 383 || [[뽕나무]] || 384 || [[무스 가득한 게이]] ||
235
|| 385 || [[이프로]] || 386 || [[로스앤젤레스 스타디움 헐리우드 파크]] ||
236
|| 387 || [[크리스탈]] || 388 || [[탈라스]] ||
237
|| 389 || [[스씨]] || 390 || [[씨발]] ||
238
|| 391 || [[발리언트 하츠 : 더 그레이트 워]] || 392 || [[워싱턴 D.C.]] ||
239
|| 393 || [[C나]] || 394 || [[나무위키 민선 운영진 폐지 사태]] ||
240
|| 395 || [[태진아]] || 396 || [[아처]] ||
241
|| 397 || [[처녀]] || 398 || [[여주역]] ||
242
|| 399 || [[역대 보이그룹 음반 초동 순위]] || 400 || [[위안]] ||
251
// 업데이트 확인
252
GM.xmlHttpRequest({
253
method: "GET",
254
url: "https://api.github.com/repos/LiteHell/NamuFix/releases/latest",
255
onload: function (res) {
256
var obj = JSON.parse(res.responseText);
257
if (typeof obj.message !== 'undefined' && obj.message.indexOf('API rate limit') != -1) {
258
console.log('[NamuFix] NamuFix데이트 연기! (GitHub API 제한따른류)');
259
return; // GitHub API 오류
260
}
261
var currentVersion = GM.info.script.version;
262
if(!obj.tag_name) {
263
console.log('[NamuFix] NamuFix 업데이트 연기! (최신 버전을 읽을 없음)');
264
return;
265
}
266
var latestVersion = obj.tag_name;
267
}
268
});
243269
244
=== 401 ~ 500(완료) ===
245
|| 401 || [[안아줘 HOLD ON ME]] || 402 || [[EA SPORTS ™ FIFA ONLINE 3 adidas CHAMPIONSHIP 2015 Season 1]] ||
246
|| 403 || [[123]] || 404 || [[3+1 합체]] ||
247
|| 405 || [[체력장]] || 406 || [[장수]] ||
248
|| 407 || [[수간]] || 408 || [[간장 치킨]] ||
249
|| 409 || [[킨샤사]] || 410 || [[사마천]] ||
250
|| 411 || [[천국]] || 412 || [[국가비]] ||
251
|| 413 || [[비수]] || 414 || [[수리고등학교]] ||
252
|| 415 || [[교정직공무원]] || 416 || [[원숭이 섬의 저주]] ||
253
|| 417 || [[주 이란 미국 대사관 인질 사건]] || 418 || [[건달]] ||
254
|| 419 || [[달서구]] || 420 || [[구미역]] ||
255
|| 421 || [[역관]] || 422 || [[관광객]] ||
256
|| 423 || [[객주]] || 424 || [[주사위 놀이장]] ||
257
|| 425 || [[장관]] || 426 || [[관제탑]] ||
258
|| 427 || [[탑승동역]] || 428 || [[역시 내 청춘 러브코메디는 잘못됐다.]] ||
259
|| 429 || [[다중 계정 검사]] || 430 || [[사재혁]] ||
260
|| 431 || [[혁거세거서간]] || 432 || [[간장]] ||
261
|| 433 || [[장나라]] || 434 || [[라다 그란타]] ||
262
|| 435 || [[타라 덩컨 시리즈]] || 436 || [[즈곡크]] ||
263
|| 437 || [[크레이지 아케이드]] || 438 || [[드레스 차림의 내가 높으신 분들의 가정교사가 된 사건]] ||
264
|| 439 || [[건들바위역]] || 440 || [[역장 우리]] ||
265
|| 441 || [[리듬체조]] || 442 || [[조금만 메리 크리스마스]] ||
266
|| 443 || [[스즈미야 하루히의 우울]] || 444 || [[울리야놉스크급 항공모함]] ||
267
|| 445 || [[함장]] || 446 || [[장삐쭈]] ||
268
|| 447 || [[쭈쭈바]] || 448 || [[바람]] ||
269
|| 449 || [[람보르기니]] || 450 || [[니킥]] ||
270
|| 451 || [[킥오프]] || 452 || [[프론트 미션 2089 : 보더 오브 매드니스]] ||
271
|| 453 || [[스트라이크]] || 454 || [[크레파스]] ||
272
|| 455 || [[스테인리스 스틸]] || 456 || [[틸다 스윈튼]] ||
273
|| 457 || [[튼살]] || 458 || [[살사]] ||
274
|| 459 || [[사제]] || 460 || [[제4회 전국동시지방선거/기초단체장/서울특별시]] ||
275
|| 461 || [[시온]] || 462 || [[온 유어 마크]] ||
276
|| 463 || [[크림슨 발록]] || 464 || [[록 음악]] ||
277
|| 465 || [[악바르 대제]] || 466 || [[제천역]] ||
278
|| 467 || [[역삼역]] || 468 || [[역촌역]] ||
279
|| 469 || [[역대 MSL 일람]] || 470 || [[람세스]] ||
280
|| 471 || [[스바루 360]] || 472 || [[007]] ||
281
|| 473 || [[700]] || 474 || [[0.999…=1]] ||
282
|| 475 || [[1!2!3!4! ヨロシク!]] || 476 || [[クルクル☆ラブ 〜Opioid Peptide MIX〜]] ||
283
|| 477 || [[~로서와 ~로써의 구분]] || 478 || [[분기점]] ||
284
|| 479 || [[점촌역]] || 480 || [[역전재판 4]] ||
285
|| 481 || [[4.19민주묘지역]] || 482 || [[역병]] ||
286
|| 483 || [[병점차량사업소]] || 484 || [[소꿉놀이]] ||
287
|| 485 || [[이수]] || 486 || [[수험생]] ||
288
|| 487 || [[생리대]] || 488 || [[대곡]] ||
289
|| 489 || [[곡성군]] || 490 || [[군사]] ||
290
|| 491 || [[사천시]] || 492 || [[시민공원역]] ||
291
|| 493 || [[역경]] || 494 || [[경복궁역]] ||
292
|| 495 || [[역류성 식도염]] || 496 || [[염수정]] ||
293
|| 497 || [[정글은 언제나 맑음 뒤 흐림]] || 498 || [[임수경]] ||
294
|| 499 || [[경찰]] || 500 || [[찰떡]] ||
270
function nOu(a) {
271
return typeof a === 'undefined' || a == null;
272
}
295273
296
=== 501 ~ 600(완료) ===
297
|| 501 || [[떡검]] || 502 || [[검사]] ||
298
|| 503 || [[사곡]] || 504 || [[곡성(영화)|곡성]] ||
299
|| 505 || [[성궁]] || 506 || [[궁둥]] ||
300
|| 507 || [[녀석도 사실은 불쌍한 녀석이었어]] || 508 || [[어룡역]] ||
301
|| 509 || [[역도요정 김복주]] || 510 || [[주예]] ||
302
|| 511 || [[수리 크루즈]] || 512 || [[즈 고오마 구]] ||
303
|| 513 || [[구강]] || 514 || [[강기갑]] ||
304
|| 515 || [[갑종회송]] || 516 || [[송탄시]] ||
305
|| 517 || [[시 브엉]] || 518 || [[엉덩국/등장인물]] ||
306
|| 519 || [[물레방아]] || 520 || [[아저씨]] ||
307
|| 521 || [[씨 노 이블]] || 522 || [[블라고예 마르야노비치]] ||
308
|| 523 || [[치가 사츠키]] || 524 || [[키리사키 치토게]] ||
309
|| 525 || [[게이큐 전철]] || 526 || [[철스퍼거]] ||
310
|| 527 || [[거창군]] || 528 || [[군수]] ||
311
|| 529 || [[수정 전쟁]] || 530 || [[쟁탈전]] ||
312
|| 531 || [[전사]] || 532 || [[사포]] ||
313
|| 533 || [[포장]] || 534 || [[장발장]] ||
314
|| 535 || [[장독대]] || 536 || [[대진대학교]] ||
315
|| 537 || [[교정]] || 538 || [[정유라]] ||
316
|| 539 || [[라이베리아]] || 540 || [[아마테라스]] ||
317
|| 541 || [[스에노하라역]] || 542 || [[역지사지]] ||
318
|| 543 || [[지축역]] || 544 || [[역전! 야매요리]] ||
319
|| 545 || [[리투아니아]] || 546 || [[아누비스]] ||
320
|| 547 || [[스누피 커피우유]] || 548 || [[유동닉]] ||
321
|| 549 || [[익산역]] || 550 || [[역사를 잊은 민족에게 미래는 없다]] ||
322
|| 551 || [[다슬기국]] || 552 || [[국방부 직할 부대]] ||
323
|| 553 || [[대방역]] || 554 || [[역주문]] ||
324
|| 555 || [[문재인 암살 예글 작성 사건]] || 556 || [[건차자도 사건]] ||
325
|| 557 || [[건전지]] || 558 || [[지문]] ||
326
|| 559 || [[문재인]] || 560 || [[인가차트]] ||
327
|| 561 || [[트포대]] || 562 || [[대포]] ||
328
|| 563 || [[포괄보조금]] || 564 || [[금의]] ||
329
|| 565 || [[의존재원]] || 566 || [[원자]] ||
330
|| 567 || [[자주재원]] || 568 || [[원심분리기]] ||
331
|| 569 || [[기체빨]] || 570 || [[빨래건조대 살인사건]] ||
332
|| 571 || [[건양사이버대학교]] || 572 || [[교육칙어]] ||
333
|| 573 || [[어 딜도 망가]] || 574 || [[가마고리 경정장앞역]] ||
334
|| 575 || [[역전앞]] || 576 || [[앞치마]] ||
335
|| 577 || [[마 제왕 앙그마르]] || 578 || [[르 줄라호 침몰사고]] ||
336
|| 579 || [[고구마]] || 580 || [[마함바]] ||
337
|| 581 || [[바하 칼리포르니아]] || 582 || [[아모르파티]] ||
338
|| 583 || [[티 익스프레스]] || 584 || [[스무 살의 봄]] ||
339
|| 585 || [[봄에 가장 가까운 거리]] || 586 || [[리 316식 시화]] ||
340
|| 587 || [[화염방사기]] || 588 || [[기 드 모파상]] ||
341
|| 589 || [[상하이 조]] || 590 || [[조선의 궁궐에 당도한걸 환영하오 낯선이여]] ||
342
|| 591 || [[여러분 이거 거짓말인 아시죠]] || 592 || [[죠 & 맥 시리즈]] ||
343
|| 593 || [[즈믄누리]] || 594 || [[리 108식 대사치]] ||
344
|| 595 || [[치가네마루]] || 596 || [[루바체 상회]] ||
345
|| 597 || [[회사국가시대]] || 598 || [[대동아전쟁해군의 노래]] ||
346
|| 599 || [[래더가스 드라코 미니스타렐]] || 600 || [[렐릭 엔터테인먼트]] ||
274
// HTML스케
275
function encodeHTMLComponent(text) {
276
var result = text;
277
// http://www.w3schools.com/php/func_string_htmlspecialchars.asp 함.
278
result = result.replace(/&/gmi, "&amp;");
279
result = result.replace(/</gmi, "&lt;");
280
result = result.replace(/>/gmi, "&gt;");
281
result = result.replace(/'/gmi, "&#039;");
282
result = result.replace(/"/gmi, "&quot;");
283
return result;
284
}
347285
348
=== 601 ~ 700(완료) ===
349
|| 601 || [[트로이목마]] || 602 || [[마무]] ||
350
|| 603 || [[무스비]] || 604 || [[비호]] ||
351
|| 605 || [[호러]] || 606 || [[러브라이브]] ||
352
|| 607 || [[브이 플라워]] || 608 || [[워털루 전투]] ||
353
|| 609 || [[투 러브 트러블]] || 610 || [[블리자드 노스]] ||
354
|| 611 || [[스무스 드로우]] || 612 || [[우만레]] ||
355
|| 613 || [[레터박스]] || 614 || [[스키 유키 마지 매직]] ||
356
|| 615 || [[직쏘]] || 616 || [[쏘우]] ||
357
|| 617 || [[우즈베키스탄]] || 618 || [[탄자니아]] ||
358
|| 619 || [[아재개그]] || 620 || [[그렘린(어떤 마술의 금서목록)]] ||
359
|| 621 || [[린스]] || 622 || [[스까듭밥]] ||
360
|| 623 || [[밥 딜런]] || 624 || [[런던]] ||
361
|| 625 || [[던전 디펜더스]] || 626 || [[스타크래프트]] ||
362
|| 627 || [[트집]] || 628 || [[집적회로]] ||
363
|| 629 || [[로동당]] || 630 || [[당태종]] ||
364
|| 631 || [[종갓집]] || 632 || [[집단강간]] ||
365
|| 633 || [[간경화]] || 634 || [[화요일]] ||
366
|| 635 || [[일기예보]] || 636 || [[보지노프]] ||
367
|| 637 || [[프랑스 100mm 함포]] || 638 || [[포름알데히드]] ||
368
|| 639 || [[드럼]] || 640 || [[럼 피즈]] ||
369
|| 641 || [[즈가리야]] || 642 || [[야당역]] ||
370
|| 643 || [[역삼각함수]] || 644 || [[수색역]] ||
371
|| 645 || [[역무원]] || 646 || [[원주각]] ||
372
|| 647 || [[각도기]] || 648 || [[기가 모듈]] ||
373
|| 649 || [[듈라한]] || 650 || [[한대앞역]] ||
374
|| 651 || [[역전 학원]] || 652 || [[원폭]] ||
375
|| 653 || [[폭염의 검사 바로미데스]] || 654 || [[스트라이크 위치스]] ||
376
|| 655 || [[스나가와]] || 656 || [[와우]] ||
377
|| 657 || [[우박]] || 658 || [[박그네를 감옥으로]] ||
378
|| 659 || [[로각좁]] || 660 || [[좁쌀]] ||
379
|| 661 || [[쌀벌레]] || 662 || [[레이저]] ||
380
|| 663 || [[저그]] || 664 || [[그라바타]] ||
381
|| 665 || [[타치바나 카나데]] || 666 || [[데몬]] ||
382
|| 667 || [[몬데그린]] || 668 || [[린저씨]] ||
383
|| 669 || [[씨앗]] || 670 || [[앗! 시리즈]] ||
384
|| 671 || [[즈 바즈 바]] || 672 || [[바트당]] ||
385
|| 673 || [[당나귀]] || 674 || [[귀신]] ||
386
|| 675 || [[신생대]] || 676 || [[대나무]] ||
387
|| 677 || [[무재배]] || 678 || [[배추]] ||
388
|| 679 || [[추석]] || 680 || [[석고]] ||
389
|| 681 || [[고고학자]] || 682 || [[자석]] ||
390
|| 683 || [[석공]] || 684 || [[공사]] ||
391
|| 685 || [[사신]] || 686 || [[신발]] ||
392
|| 687 || [[발가락]] || 688 || [[락앤락]] ||
393
|| 689 || [[락스]] || 690 || [[스타 무한도전]] ||
394
|| 691 || [[전자제품]] || 692 || [[품절]] ||
395
|| 693 || [[절연 테이프]] || 694 || [[프라가]] ||
396
|| 695 || [[가위]] || 696 || [[위키니트]] ||
397
|| 697 || [[트위터]] || 698 || [[터크스 케이커스 제도 축구 국가대표팀]] ||
398
|| 699 || [[팀 포트리스 2/수레 밀기]] || 700 || [[기갑]] ||
286
function validateIP(ip) {
287
return /^(?:\d{1,3}\.){3}\d{1,3}$/.test(ip) || /^([0-9a-f]){1,4}(:([0-9a-f]){1,4}){7}$/i.test(ip);
288
}
399289
400
=== 701 ~ 800(완료) ===
401
|| 701 || [[갑상선암]] || 702 || [[암사]] ||
402
|| 703 || [[사당역]] || 704 || [[역사학자]] ||
403
|| 705 || [[자전거]] || 706 || [[거가대교]] ||
404
|| 707 || [[교수]] || 708 || [[수리수리 마하수리]] ||
405
|| 709 || [[리엔필드]] || 710 || [[드래곤볼 Z : 부활의 F]] ||
406
|| 711 || [[Fuck You]] || 712 || [[umanle]] ||
407
|| 713 || [[e지원]] || 714 || [[원자로]] ||
408
|| 715 || [[로마 제국]] || 716 || [[국제업무지구역]] ||
409
|| 717 || [[역차별]] || 718 || [[별에서 온 그대]] ||
410
|| 719 || [[대 IS 군사 개입]] || 720 || [[입막음]] ||
411
|| 721 || [[음성]] || 722 || [[성선설]] ||
412
|| 723 || [[설악산]] || 724 || [[산지기]] ||
413
|| 725 || [[기구]] || 726 || [[구경]] ||
414
|| 727 || [[경상북도]] || 728 || [[도라지]] ||
415
|| 729 || [[지평역]] || 730 || [[역전검사]] ||
416
|| 731 || [[사지절단 당한 여자]] || 732 || [[자동차]] ||
417
|| 733 || [[차지]] || 734 || [[지구본]] ||
418
|| 735 || [[본능]] || 736 || [[능력]] ||
419
|| 737 || [[역적 : 백성을 훔친 도적]] || 738 || [[적폐청산]] ||
420
|| 739 || [[산군]] || 740 || [[군자]] ||
421
|| 741 || [[자뻑]] || 742 || [[뻑유]] ||
422
|| 743 || [[유승민]] || 744 || [[민영환]] ||
423
|| 745 || [[환갑]] || 746 || [[갑질]] ||
424
|| 747 || [[질산]] || 748 || [[산성]] ||
425
|| 749 || [[성수역]] || 750 || [[역곡역]] ||
426
|| 751 || [[역하렘]] || 752 || [[렘피라]] ||
427
|| 753 || [[라면]] || 754 || [[면발이 억수로 부드럽네]] ||
428
|| 755 || [[네이버 주식회사]] || 756 || [[사화]] ||
429
|| 757 || [[화약]] || 758 || [[약사]] ||
430
|| 759 || [[사이다]] || 760 || [[다리]] ||
431
|| 761 || [[이 개그맨들이 사는 세상]] || 762 || [[상큼 발랄 DAY!DAY!DAY!]] ||
432
|| 763 || [[Yee]] || 764 || [[E(셜록)]] ||
433
|| 765 || [[ET]] || 766 || [[TE 엔진]] ||
434
|| 767 || [[진보네트워크센터]] || 768 || [[터검니]] ||
435
|| 769 || [[니코니코니]] || 770 || [[니더작센]] ||
436
|| 771 || [[센과 치히로의 행방불명]] || 772 || [[명언]] ||
437
|| 773 || [[언사이클로피디아 드라마티카]] || 774 || [[카스]] ||
438
|| 775 || [[스타]] || 776 || [[타란토 공습]] ||
439
|| 777 || [[습도]] || 778 || [[도토리]] ||
440
|| 779 || [[이 멋진 세계에 축복을]] || 780 || [[을지문덕]] ||
441
|| 781 || [[덕후]] || 782 || [[후 시앙]] ||
442
|| 783 || [[앙그라 마이뉴]] || 784 || [[뉴질랜드]] ||
443
|| 785 || [[드라군]] || 786 || [[군국주의]] ||
444
|| 787 || [[의사양반]] || 788 || [[반정]] ||
445
|| 789 || [[정지]] || 790 || [[지느러미]] ||
446
|| 791 || [[미츠하]] || 792 || [[하츠네미쿠]] ||
447
|| 793 || [[쿠란]] || 794 || [[란다]] ||
448
|| 795 || [[다이나믹 노동]] || 796 || [[동네]] ||
449
|| 797 || [[네이버]] || 798 || [[버러지]] ||
450
|| 799 || [[지 아이 조]] || 800 || [[조선민주주의인민공화국]] ||
290
function formatDateTime(t) {
291
var d = new Date(t);
292
return `${d.getFullYear()}년 ${d.getMonth() + 1}월 ${d.getDate()}일 ${(['일', '월', '화', '수', '목', '금', '토'])[d.getDay()]}요일 ${ d.getHours() > 12 ? '오후' : '오전'} ${d.getHours() - (d.getHours() > 12 ? 12 : 0)}시 ${d.getMinutes()}분 ${d.getSeconds()}초`;
293
}
451294
452
=== 801 ~ 900(완료) ===
453
|| 801 || [[국자]] || 802 || [[자위대]] ||
454
|| 803 || [[대만]] || 804 || [[만득이]] ||
455
|| 805 || [[이태석]] || 806 || [[석굴암]] ||
456
|| 807 || [[암세포]] || 808 || [[포기]] ||
457
|| 809 || [[기 조작]] || 810 || [[작화]] ||
458
|| 811 || [[화타]] || 812 || [[타치바나 타키]] ||
459
|| 813 || [[키스]] || 814 || [[스파클]] ||
460
|| 815 || [[클래시 로얄]] || 816 || [[얄루 강]] ||
461
|| 817 || [[강냉이]] || 818 || [[이석]] ||
462
|| 819 || [[석기시대]] || 820 || [[대저차량기지]] ||
463
|| 821 || [[지진]] || 822 || [[진박]] ||
464
|| 823 || [[박지나]] || 824 || [[나나]] ||
465
|| 825 || [[나이테]] || 826 || [[테란]] ||
466
|| 827 || [[란드그리즈]] || 828 || [[즈고크]] ||
467
|| 829 || [[크리티컬]] || 830 || [[컬러]] ||
468
|| 831 || [[러셀]] || 832 || [[셀룰로스]] ||
469
|| 833 || [[스카이프]] || 834 || [[프랭크]] ||
470
|| 835 || [[크라운제과]] || 836 || [[과자]] ||
471
|| 837 || [[자가 면역 질환]] || 838 || [[환자복]] ||
472
|| 839 || [[복수]] || 840 || [[수수깡]] ||
473
|| 841 || [[깡패]] || 842 || [[패싸움]] ||
474
|| 843 || [[움직이는 물체의 전기역학에 관하여]] || 844 || [[여수시]] ||
475
|| 845 || [[시청률]] || 846 || [[율곡 이이]] ||
476
|| 847 || [[이승환]] || 848 || [[환전]] ||
477
|| 849 || [[전장의 안개]] || 850 || [[개살구]] ||
478
|| 851 || [[구두쇠]] || 852 || [[쇠고기]] ||
479
|| 853 || [[기보배]] || 854 || [[배포중단]] ||
480
|| 855 || [[단종]] || 856 || [[종단(조선)]] ||
481
|| 857 || [[단조]] || 858 || [[조리]] ||
482
|| 859 || [[리어카]] || 860 || [[카레]] ||
483
|| 861 || [[레 미제라블]] || 862 || [[블리치]] ||
484
|| 863 || [[치어]] || 864 || [[어린이]] ||
485
|| 865 || [[이두환]] || 866 || [[환공포증]] ||
486
|| 867 || [[증여세]] || 868 || [[세금]] ||
487
|| 869 || [[금강불괴]] || 870 || [[괴수]] ||
488
|| 871 || [[수정구]] || 872 || [[구치소]] ||
489
|| 873 || [[소찬휘]] || 874 || [[휘파람 행진곡]] ||
490
|| 875 || [[곡선]] || 876 || [[선거]] ||
491
|| 877 || [[거산역]] || 878 || [[역전]] ||
492
|| 879 || [[전소미]] || 880 || [[미 파워 뱅크]] ||
493
|| 881 || [[크레용팝]] || 882 || [[팝콘]] ||
494
|| 883 || [[콘솔]] || 884 || [[솔라]] ||
495
|| 885 || [[라트비아]] || 886 || [[아이슬란드]] ||
496
|| 887 || [[드라이버]] || 888 || [[버스커버스커]] ||
497
|| 889 || [[커클랜드 블렌디드 스카치 위스키]] || 890 || [[키 재보기]] ||
498
|| 891 || [[기라티나]] || 892 || [[나의 시체를 넘어서 가라]] ||
499
|| 893 || [[라스푸틴]] || 894 || [[틴트]] ||
500
|| 895 || [[트라이어택]] || 896 || [[택티컬 트루퍼]] ||
501
|| 897 || [[퍼스트 클래스]] || 898 || [[스리자야와르데네푸라코테]] ||
502
|| 899 || [[테라스]] || 900 || [[스위스]] ||
295
function formatTimespan(timespan) {
296
var units = [{
297
name: "주",
298
unit: 60 * 60 * 24 * 7,
299
value: 0
300
},
301
{
302
name: "일",
303
unit: 60 * 60 * 24,
304
value: 0
305
},
306
{
307
name: "시간",
308
unit: 60 * 60,
309
value: 0
310
},
311
{
312
name: "분",
313
unit: 60,
314
value: 0
315
},
316
{
317
name: "초",
318
unit: 1,
319
value: 0
320
}
321
];
322
for (var i = 0; i < units.length; i++) {
323
while (timespan >= units[i].unit) {
324
timespan -= units[i].unit;
325
units[i].value++;
326
}
327
}
328
return units.filter(function (x) {
329
return x.value != 0;
330
}).map(function (x) {
331
return x.value + x.name
332
}).join(' ');
333
}
503334
504
=== 901 ~ 1000(완료) ===
505
|| 901 || [[스케치북]] || 902 || [[북이면]] ||
506
|| 903 || [[면제]] || 904 || [[제나라]] ||
507
|| 905 || [[라디오]] || 906 || [[오수]] ||
508
|| 907 || [[수요일]] || 908 || [[일상]] ||
509
|| 909 || [[상장]] || 910 || [[장가]] ||
510
|| 911 || [[가독성]] || 912 || [[성지]] ||
511
|| 913 || [[지식인]] || 914 || [[인내]] ||
512
|| 915 || [[내면]] || 916 || [[면상]] ||
513
|| 917 || [[상수도]] || 918 || [[도살]] ||
514
|| 919 || [[살인]] || 920 || [[인수]] ||
515
|| 921 || [[수호]] || 922 || [[호수비]] ||
516
|| 923 || [[비밀번호 486]] || 924 || [[6668587667]] ||
517
|| 925 || [[778]] || 926 || [[8888577]] ||
518
|| 927 || [[795]] || 928 || [[5886899678]] ||
519
|| 929 || [[856]] || 930 || [[636]] ||
520
|| 931 || [[635]] || 932 || [[527]] ||
521
|| 933 || [[777]] || 934 || [[7N21/7N31]] ||
522
|| 935 || [[101]] || 936 || [[104]] ||
523
|| 937 || [[486]] || 938 || [[655]] ||
524
|| 939 || [[556]] || 940 || [[675]] ||
525
|| 941 || [[588]] || 942 || [[899]] ||
526
|| 943 || [[943]] || 944 || [[369]] ||
527
|| 945 || [[945]] || 946 || [[51]] ||
528
|| 947 || [[13일의 금요일 파트3]] || 948 || [[307]] ||
529
|| 949 || [[767]] || 950 || [[756]] ||
530
|| 951 || [[666]] || 952 || [[613]] ||
531
|| 953 || [[301]] || 954 || [[102]] ||
532
|| 955 || [[201]] || 956 || [[103]] ||
533
|| 957 || [[36]] || 958 || [[686]] ||
534
|| 959 || [[6호선]] || 960 || [[선행]] ||
535
|| 961 || [[행정]] || 962 || [[정치]] ||
536
|| 963 || [[치안]] || 964 || [[안습]] ||
537
|| 965 || [[습기]] || 966 || [[기지개]] ||
538
|| 967 || [[개떡]] || 968 || [[떡고물]] ||
539
|| 969 || [[물병]] || 970 || [[병신]] ||
540
|| 971 || [[신교]] || 972 || [[교실]] ||
541
|| 973 || [[실과]] || 974 || [[과거]] ||
542
|| 975 || [[거꾸로 말해요 아하]] || 976 || [[하얀색]] ||
543
|| 977 || [[색소]] || 978 || [[소금]] ||
544
|| 979 || [[금괴]] || 980 || [[괴물]] ||
545
|| 981 || [[물총]] || 982 || [[총기]] ||
546
|| 983 || [[기계]] || 984 || [[계산기]] ||
547
|| 985 || [[기본]] || 986 || [[본드]] ||
548
|| 987 || [[드라이브]] || 988 || [[브라운]] ||
549
|| 989 || [[운지]] || 990 || [[지석진]] ||
550
|| 991 || [[진수]] || 992 || [[수직]] ||
551
|| 993 || [[직통]] || 994 || [[통장]] ||
552
|| 995 || [[장기]] || 996 || [[기지]] ||
553
|| 997 || [[지구]] || 998 || [[구스]] ||
554
|| 999 || [[스타크래프트 2: 군단의 심장]] || 1000 || [[장위안]](1000개 돌파!) ||
335
var hashDictionary512 = {};
336
var hashDictionary1 = {};
337
var hashDictionary256 = {};
338
var ipDictionary = {};
555339
556
=== 1001 ~ 1100(완료) ===
557
|| 1001 || [[안구]] || 1002 || [[구주]] ||
558
|| 1003 || [[주화]] || 1004 || [[화염방사]] ||
559
|| 1005 || [[사칭]] || 1006 || [[칭따오]] ||
560
|| 1007 || [[오스트랄로피테쿠스]] || 1008 || [[스파게티]] ||
561
|| 1009 || [[티파니]] || 1010 || [[니스]] ||
562
|| 1011 || [[스키장]] || 1012 || [[장비(삼국지)]] ||
563
|| 1013 || [[비장]] || 1014 || [[장지]] ||
564
|| 1015 || [[지나]] || 1016 || [[나무]] ||
565
|| 1017 || [[무스]] || 1018 || [[스승의 은혜]] ||
566
|| 1019 || [[혜리]] || 1020 || [[리스]] ||
567
|| 1021 || [[스키 장비]] || 1022 || [[비긴 어게인]] ||
568
|| 1023 || [[인사동]] || 1024 || [[동대문]] ||
569
|| 1025 || [[문지기]] || 1026 || [[기안]] ||
570
|| 1027 || [[안티]] || 1028 || [[티비]] ||
571
|| 1029 || [[비룡]] || 1030 || [[용수]] ||
572
|| 1031 || [[수치]] || 1032 || [[치수]] ||
573
|| 1033 || [[수영장]] || 1034 || [[장 르노]] ||
574
|| 1035 || [[노무현]] || 1036 || [[현대 유니콘스]] ||
575
|| 1037 || [[스테이크]] || 1038 || [[크고 아름다운]] ||
576
|| 1039 || [[운석 금메달]] || 1040 || [[달력]] ||
577
|| 1041 || [[역사지도]] || 1042 || [[도나]] ||
578
|| 1043 || [[나프탈렌]] || 1044 || [[렌터카]] ||
579
|| 1045 || [[카를루스 에두아르두 카스트루 다 시우바]] || 1046 || [[바이오하자드 더 스테이지]] ||
580
|| 1047 || [[지가르데]] || 1048 || [[데벤져]] ||
581
|| 1049 || [[져주기 게임]] || 1050 || [[임신 엔딩]] ||
582
|| 1051 || [[딩고]] || 1052 || [[고리]] ||
583
|| 1053 || [[리쌍]] || 1054 || [[쌍방울 레이더스]] ||
584
|| 1055 || [[스튜]] || 1056 || [[튜브]] ||
585
|| 1057 || [[브라질]] || 1058 || [[질소를 샀더니 과자를 덤으로]] ||
586
|| 1059 || [[로보트 태권 V]] || 1060 || [[VDT 증후군]] ||
587
|| 1061 || [[군장]] || 1062 || [[장갑기병 보톰즈 외로운 그림자 다시]] ||
588
|| 1063 || [[시로타니 타다오미]] || 1064 || [[미장원]] ||
589
|| 1065 || [[원두]] || 1066 || [[두부]] ||
590
|| 1067 || [[부산광역시]] || 1068 || [[시리우스]] ||
591
|| 1069 || [[스2갤]] || 1070 || [[갤러그]] ||
592
|| 1071 || [[그 고집, 내게도 주세요]] || 1072 || [[요통]] ||
593
|| 1073 || [[통수]] || 1074 || [[수사기록 Pilot]] ||
594
|| 1075 || [[TACC]] || 1076 || [[C 언어]] ||
595
|| 1077 || [[어디에다 대고 신성한 우리 영해에 불질이야]] || 1078 || [[야리스기 도시전설]] ||
596
|| 1079 || [[설정오류]] || 1080 || [[류정옥]] ||
597
|| 1081 || [[옥정IC]] || 1082 || [[CF]] ||
598
|| 1083 || [[FIFA]] || 1084 || [[AEK-999(소녀전선)]] ||
340
function SHA512(text) {
341
if (typeof hashDictionary512[text] === 'undefined') {
342
var shaObj = new jsSHA("SHA-512", "TEXT");
343
shaObj.update(text);
344
hashDictionary512[text] = shaObj.getHash("HEX");
345
}
346
return hashDictionary512[text];
347
}
348
349
function SHA1(text) {
350
if (typeof hashDictionary1[text] === 'undefined') {
351
var shaObj = new jsSHA("SHA-1", "TEXT");
352
shaObj.update(text);
353
hashDictionary1[text] = shaObj.getHash("HEX");
354
}
355
return hashDictionary1[text];
356
}
357
358
function SHA256(text) {
359
if (typeof hashDictionary256[text] === 'undefined') {
360
var shaObj = new jsSHA("SHA-256", "TEXT");
361
shaObj.update(text);
362
hashDictionary256[text] = shaObj.getHash("HEX");
363
}
364
return hashDictionary256[text];
365
}
366
367
function getIpInfo(ip, cb) {
368
if (ipDictionary[ip])
369
return cb(ipDictionary[ip]);
370
GM.xmlHttpRequest({
371
method: "GET",
372
url: `http://ipinfo.io/${ip}/json`,
373
onload: function (res) {
374
var resObj = JSON.parse(res.responseText);
375
if (res.status === 200 || res.status === 304) {
376
if (/^AS[0-9]+ /.test(resObj.org)) {
377
resObj.org = resObj.org.replace(/^AS[0-9]+ /, '');
378
}
379
if (SET.ipInfoDefaultOrg != 'ipinfo.io') {
380
getIpWhois(ip, function (whoisRes) {
381
if (!whoisRes.success || whoisRes.raw) {
382
ipDictionary[ip] = resObj;
383
cb(resObj);
384
return;
385
}
386
var koreanISP = null,
387
koreanUser = null;
388
if (whoisRes.result.korean && whoisRes.result.korean.ISP && whoisRes.result.korean.ISP.netinfo && whoisRes.result.korean.ISP.netinfo.orgName) {
389
koreanISP = whoisRes.result.korean.ISP.netinfo.orgName;
390
} else if (whoisRes.result.korean && whoisRes.result.korean.user && whoisRes.result.korean.user.netinfo && whoisRes.result.korean.user.netinfo.orgName) {
391
koreanUser = whoisRes.result.korean.user.netinfo.orgName;
392
}
393
if (SET.ipInfoDefaultOrg === 'KISAuser' && koreanUser !== null) {
394
resObj.org = koreanUser;
395
} else if (SET.ipInfoDefaultOrg === 'KISAISP' && koreanISP !== null) {
396
resObj.org = koreanISP;
397
} else if (SET.ipInfoDefaultOrg === 'KISAuserOrISP' && (koreanUser !== null || koreanISP !== null)) {
398
resObj.org = koreanUser !== null ? koreanUser : koreanISP;
399
}
400
ipDictionary[ip] = resObj;
401
cb(resObj);
402
return;
403
});
404
} else {
405
ipDictionary[ip] = resObj;
406
cb(resObj);
407
}
408
} else {
409
cb(null);
410
}
411
}
412
});
413
}
414
415
var whoisDictionary = {};
416
417
function getIpWhois(ip, cb) {
418
if (whoisDictionary[ip])
419
return cb(whoisDictionary[ip]);
420
GM.xmlHttpRequest({
421
method: "GET",
422
url: `http://namufix.wikimasonry.org/whois/ip/${ip}`,
423
onload: function (res) {
424
var resObj = JSON.parse(res.responseText);
425
whoisDictionary[ip] = resObj;
426
cb(resObj);
427
}
428
});
429
}
430
431
function enterTimespanPopup(title, callback) {
432
var win = TooSimplePopup();
433
win.title(title);
434
win.content(function (winContainer) {
435
var units = {
436
second: 1,
437
minute: 60,
438
hour: 60 * 60,
439
day: 60 * 60 * 24,
440
week: 60 * 60 * 24 * 7,
441
month: 60 * 60 * 24 * 7 * 4, // 4 주
442
year: 60 * 60 * 24 * 7 * 48 // 48주
443
}
444
winContainer.innerHTML = '<style>.timespan-container input.timespan-input {width: 60px;}</style><div class="timespan-container">' +
445
' <input type="number" data-unit="year" class="timespan-input" value="0">년' +
446
' <input type="number" data-unit="month" class="timespan-input" value="0">개월' +
447
' <input type="number" data-unit="week" class="timespan-input" value="0">주' +
448
' <input type="number" data-unit="day" class="timespan-input" value="0">일' +
449
' <input type="number" data-unit="hour" class="timespan-input" value="0">시간' +
450
' <input type="number" data-unit="minute" class="timespan-input" value="0">분' +
451
' <input type="number" data-unit="second" class="timespan-input" value="0">초' +
452
'</div>';
453
win.button('닫기', function () {
454
win.close();
455
callback(null);
456
});
457
win.button('입력', function () {
458
var result = 0;
459
var isNumberic = function (v) {
460
return !isNaN(parseFloat(v)) && isFinite(v);
461
} // https://stackoverflow.com/a/9716488
462
var timespanInputs = winContainer.querySelectorAll('input.timespan-input')
463
for (var i = 0; i < timespanInputs.length; i++) {
464
var timespanInput = timespanInputs[i];
465
if (isNumberic(timespanInput.value)) result += timespanInput.value * units[timespanInput.dataset.unit];
466
}
467
win.close();
468
callback(result);
469
})
470
});
471
}
472
473
function whoisPopup(ip) {
474
if (ip === null || ip === "")
475
return alert('ip주소를 입력해주세요');
476
var win = TooSimplePopup();
477
win.title('IP WHOIS 조회');
478
win.button('다른 IP 조회', function () {
479
var newip = prompt('IP 주소를 입력하세요.');
480
win.close();
481
whoisPopup(newip);
482
})
483
win.button('닫기', win.close);
484
win.content(function (container) {
485
container.innerHTML = '조회중입니다. 잠시만 기다려주세요...';
486
getIpWhois(ip, function (result) {
487
if (result.success && !result.raw) {
488
var whoisObj = result.result;
489
var currentView = 'table';
490
container.innerHTML = '<p>NamuFix 서버를 통해 KISA WHOIS를 조회했습니다. 결과는 다음과 같습니다.(<a href="#" class="switchViewType">JSON 형식으로 보기</a>)</p><div class="whois-content"><table></table></div>';
491
var resultContainer = container.querySelector('.whois-content');
492
var switchViewLink = container.querySelector('a.switchViewType');
493
494
function useTableView() {
495
function writeSubInfoTable(subInfoObj, caption) {
496
var table = document.createElement('table');
497
table.className = "whois";
498
table.innerHTML += '<caption>' + encodeHTMLComponent(caption) + '</caption>';
499
table.innerHTML += '<thead><tr><th>이름</th><th>내용</th></tr></thead>';
500
var tableHtml = '<tbody>';
501
var koreanContactNames = {
502
techContact: '네트워크 담당자 정보',
503
adminContact: 'IP주소 관리자 정보',
504
abuseContact: '네트워크 abuse 담당자 정보'
505
};
506
var koreanNetInfoNames = {
507
// 주소범위(range), 프리픽스(prefix), 네트워크이름(servName), 기관명(orgname), 주소(addr), 우편번호(zipCode), 할당내역 등록일(regDate) 필드 포함
508
addr: '주소',
509
netType: '네트워크 구분',
510
orgName: '기관명',
511
orgID: '기관 ID',
512
prefix: 'IP prefix',
513
range: 'IP 범위',
514
regDate: '등록일',
515
servName: '네트워크 이름',
516
zipCode: '우편번호'
517
};
518
for (var i in subInfoObj) {
519
if (i == 'techContact' || i == 'adminContact' || i == 'abuseContact') {
520
tableHtml += '<tr><td colspan="2" class="whois-subheader">' + koreanContactNames[i] + '</td></tr>';
521
if (subInfoObj[i].name)
522
tableHtml += '<tr><td>이름</td><td>' + encodeHTMLComponent(subInfoObj[i].name) + '</td></tr>';
523
if (subInfoObj[i].phone)
524
tableHtml += '<tr><td>전화번호</td><td>' + encodeHTMLComponent(subInfoObj[i].phone) + '</td></tr>';
525
if (subInfoObj[i].email)
526
tableHtml += '<tr><td>연락처</td><td>' + encodeHTMLComponent(subInfoObj[i].email) + '</td></tr>';
527
} else if (i == 'netinfo') {
528
// KISA 씨발놈들.... API 설명서에 다 적어두지...
529
tableHtml += '<tr><td colspan="2" class="whois-subheader">네트워크 정보</td></tr>';
530
for (var na in subInfoObj[i]) {
531
tableHtml += '<tr><td>' + (koreanNetInfoNames[na] ? koreanNetInfoNames[na] : na) + '</td><td>' + encodeHTMLComponent(subInfoObj[i][na]) + '</td></tr>';
532
}
533
}
534
}
535
tableHtml += '</tbody>';
536
table.innerHTML += tableHtml;
537
return table;
538
}
539
switchViewLink.innerText = '(진행중입니다)';
540
currentView = 'progress';
541
resultContainer.innerHTML = '<p>(진행중입니다.)</p>'
542
543
function writeRest() {
544
if (whoisObj.korean && whoisObj.korean.ISP)
545
resultContainer.appendChild(writeSubInfoTable(whoisObj.korean.ISP, "IP 주소 보유기관 정보 (국문)"));
546
if (whoisObj.korean && whoisObj.korean.user)
547
resultContainer.appendChild(writeSubInfoTable(whoisObj.korean.user, "IP 주소 이용기관 정보 (국문)"));
548
if (whoisObj.english && whoisObj.english.ISP)
549
resultContainer.appendChild(writeSubInfoTable(whoisObj.english.ISP, "IP 주소 보유기관 정보 (영문)"));
550
if (whoisObj.english && whoisObj.english.user)
551
resultContainer.appendChild(writeSubInfoTable(whoisObj.english.user, "IP 주소 이용기관 정보 (영문)"));
552
switchViewLink.innerText = 'JSON 형식으로 보기';
553
currentView = 'table';
554
}
555
if (whoisObj.countryCode == 'none') {
556
resultContainer.innerHTML = '<style>.whois-subheader {background: #9D75D9; color: white; text-align: center;} table.whois tr, table.whois td{border: 1px #9D75D9 solid; border-collapse: collapse;} table.whois td {padding: 5px;} table.whois caption { caption-side: top; }</style>' +
557
'<table class="whois"><caption>쿼리 정보</caption>' +
558
'<thead><th>이름</th><th>내용</th></tr></thead>' +
559
'<tbody>' +
560
'<tr><td>쿼리</td><td>' + whoisObj.query + '</td></tr>' +
561
'<tr><td>쿼리 유형</td><td>' + whoisObj.queryType + '</td></tr>' +
562
'<tr><td>레지스트리</td><td>' + whoisObj.registry + '</td></tr>' +
563
'<tr><td>등록 국가</td><td><span class="icon ion-android-star"></span> 없음 (루프백같이 특수한 경우)</td></tr>' +
564
'</tbody></table>';
565
writeRest();
566
} else {
567
getFlagIcon(whoisObj.countryCode.toLowerCase(), function (flagIconData) {
568
resultContainer.innerHTML = '<style>.whois-subheader {background: #9D75D9; color: white; text-align: center;} table.whois tr, table.whois td{border: 1px #9D75D9 solid; border-collapse: collapse;} table.whois td {padding: 5px;} table.whois caption { caption-side: top; }</style>' +
569
'<table class="whois"><caption>쿼리 정보</caption>' +
570
'<thead><th>이름</th><th>내용</th></tr></thead>' +
571
'<tbody>' +
572
'<tr><td>쿼리</td><td>' + whoisObj.query + '</td></tr>' +
573
'<tr><td>쿼리 유형</td><td>' + whoisObj.queryType + '</td></tr>' +
574
'<tr><td>레지스트리</td><td>' + whoisObj.registry + '</td></tr>' +
575
'<tr><td>등록 국가</td><td><img style="height: 1em;" src="' + flagIconData + '">' + korCountryNames[whoisObj.countryCode.toUpperCase()] + '</td></tr>' +
576
'</tbody></table>';
577
writeRest();
578
});
579
}
580
}
581
582
function useJsonView() {
583
switchViewLink.innerText = '표 형식으로 보기';
584
currentView = 'json';
585
resultContainer.innerHTML = '<textarea readonly style="width: 50vw; height: 600px; max-height: 90vh;"></textarea>';
586
resultContainer.querySelector('textarea').value = JSON.stringify(whoisObj);
587
}
588
switchViewLink.addEventListener('click', function (evt) {
589
evt.preventDefault();
590
if (currentView == 'json') {
591
useTableView();
592
} else if (currentView == 'table') {
593
useJsonView();
594
} else if (currentView == 'progress') {
595
alert('잠시만 기다려주세요.');
596
}
597
})
598
useTableView();
599
} else if (result.success && result.raw) {
600
container.innerHTML = '<p>NamuFix 서버에서 다음과 같은 WHOIS 결과를 얻었습니다.</p><textarea readonly style="width: 50vw; height: 600px; max-height: 80vh;"></textarea>';
601
container.querySelector('textarea').value = result.result;
602
} else if (result.error.namufix) {
603
alert('NamuFix 서버측에서 오류가 발생했습니다.\n\n메세지 : ' + result.error.message + '\n오류 코드 : ' + result.error.code);
604
win.close();
605
} else if (result.error.kisa) {
606
container.innerHTML = '<p>KISA WHOIS 조회결과 다음과 같은 오류가 반환됐습니다.</p><p><em>' + result.error.message + '</em></p><p>오류 코드는 ' + result.error.code + '입니다.</p>';
607
}
608
});
609
});
610
}
611
612
// To bypass CSP
613
var flagIconDictionary = {};
614
615
function getFlagIcon(countryCode, cb) {
616
if (flagIconDictionary[countryCode])
617
return cb(flagIconDictionary[countryCode]);
618
GM.xmlHttpRequest({
619
method: 'GET',
620
url: `https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.9.0/flags/4x3/${countryCode}.svg`,
621
onload: function (res) {
622
flagIconDictionary[countryCode] = "data:image/svg+xml;base64," + btoa(res.responseText);
623
return cb(flagIconDictionary[countryCode]);
624
}
625
});
626
}
627
628
// 문서/역사/편집 페이지 등에서 버튼 추가 함수
629
function addArticleButton(text, onclick) {
630
var aTag = document.createElement("a");
631
aTag.className = "btn btn-secondary";
632
aTag.setAttribute("role", "button");
633
aTag.innerHTML = text;
634
aTag.href = "#";
635
aTag.addEventListener('click', (evt) => {
636
evt.preventDefault();
637
onclick(evt);
638
});
639
var buttonGroup = document.querySelector('body.Liberty .liberty-content .content-tools .btn-group , body.senkawa .wiki-article-menu > div.btn-group');
640
buttonGroup.insertBefore(aTag, buttonGroup.firstChild);
641
};
642
643
function uniqueID() {
644
var dt = Date.now();
645
var url = location.href;
646
var randomized = Math.floor(Math.random() * 48158964189489678525869410);
647
return SHA512(String(dt).concat(dt, '\n', url, '\n', String(randomized)));
648
}
649
650
function listenPJAX(callback) {
651
// create elements
652
var pjaxButton = document.createElement("button");
653
var scriptElement = document.createElement("script");
654
655
// configure button
656
pjaxButton.style.display = "none";
657
pjaxButton.id = "nfFuckingPJAX"
658
pjaxButton.addEventListener("click", callback);
659
660
// configure script
661
scriptElement.setAttribute("type", "text/javascript");
662
scriptElement.innerHTML = '$(document).bind("pjax:end", function(){document.querySelector("button#nfFuckingPJAX").click();})';
663
664
// add elements
665
document.body.appendChild(pjaxButton);
666
document.head.appendChild(scriptElement);
667
}
668
669
var SET = new NFStorage();
670
671
async function INITSET() { // Storage INIT
672
await SET.load();
673
if (nOu(SET.tempsaves))
674
SET.tempsaves = {};
675
if (nOu(SET.recentlyUsedTemplates))
676
SET.recentlyUsedTemplates = [];
677
if (nOu(SET.imgurDeletionLinks))
678
SET.imgurDeletionLinks = [];
679
if (nOu(SET.discussIdenti))
680
SET.discussIdenti = 'icon'; // icon, headBg, none
681
if (nOu(SET.discussIdentiLightness))
682
SET.discussIdentiLightness = 0.7;
683
if (nOu(SET.discussIdentiSaturation))
684
SET.discussIdentiSaturation = 0.5;
685
if (nOu(SET.favorites))
686
SET.favorites = [];
687
if (nOu(SET.customIdenticons))
688
SET.customIdenticons = {};
689
if (nOu(SET.hideDeletedWhenDiscussing))
690
SET.hideDeletedWhenDiscussing = 0;
691
else if (typeof SET.hideDeletedWhenDiscussing !== "Number")
692
SET.hideDeletedWhenDiscussing = Number(SET.hideDeletedWhenDiscussing);
693
if (nOu(SET.discussAnchorPreviewType))
694
SET.discussAnchorPreviewType = 1; // 0 : None, 1 : mouseover, 2 : quote
695
else
696
SET.discussAnchorPreviewType = Number(SET.discussAnchorPreviewType);
697
if (nOu(SET.removeNFQuotesInAnchorPreview))
698
SET.removeNFQuotesInAnchorPreview = false;
699
if (nOu(SET.lookupIPonDiscuss))
700
SET.lookupIPonDiscuss = true;
701
if (nOu(SET.ignoreNonSenkawaWarning))
702
SET.ignoreNonSenkawaWarning = false;
703
if (nOu(SET.loadUnvisibleReses))
704
SET.loadUnvisibleReses = false;
705
if (nOu(SET.ipInfoDefaultOrg))
706
SET.ipInfoDefaultOrg = "ipinfo.io"; //ipinfo.io, KISAISP, KISAuser, KISAuserOrISP
707
if (nOu(SET.addAdminLinksForLiberty))
708
SET.addAdminLinksForLiberty = false;
709
if (nOu(SET.autoTempsaveSpan))
710
SET.autoTempsaveSpan = 1000 * 60 * 5; // 5분
711
if (nOu(SET.addBatchBlockMenu))
712
SET.addBatchBlockMenu = false;
713
if (nOu(SET.noLocaltimeOnNamuBoard))
714
SET.noLocaltimeOnNamuBoard = true;
715
if (nOu(SET.fileUploadReqLimit))
716
SET.fileUploadReqLimit = 3;
717
if (nOu(SET.adminReqLimit))
718
SET.adminReqLimit = 3;
719
if (nOu(SET.quickBlockReasonTemplate_discuss))
720
SET.quickBlockReasonTemplate_discuss = '긴급조치 https://${host}/thread/${threadNo} #${messageNo}' // ${host}, ${threadNo}, ${messageNo}
721
if (nOu(SET.quickBlockReasonTemplate_history))
722
SET.quickBlockReasonTemplate_history = '긴급조치 [[${docName}]] ${revisionNo}' // ${host}, ${docName}, ${revisionNo}
723
if (nOu(SET.quickBlockDefaultDuration))
724
SET.quickBlockDefaultDuration = 0;
725
if (nOu(SET.addQuickBlockLink))
726
SET.addQuickBlockLink = false;
727
if (nOu(SET.notifyForUnvisibleThreads))
728
SET.notifyForUnvisibleThreads = false;
729
if (nOu(SET.checkWhoisNetTypeOnDiscuss))
730
SET.checkWhoisNetTypeOnDiscuss = false;
731
if (nOu(SET.checkedServerNotices))
732
SET.checkedServerNotices = [];
733
if (nOu(SET.additionalScript))
734
SET.additionalScript = "";
735
if (nOu(SET.umiCookie))
736
SET.umiCookie = "";
737
if (nOu(SET.unprefixedFilename))
738
SET.unprefixedFilename= false;
739
if (nOu(SET.addSnsShareButton))
740
SET.addSnsShareButton = false;
741
if (nOu(SET.commentMacros))
742
SET.commentMacros = '';
743
if (nOu(SET.ipBlockHistoryCheckDelay))
744
SET.ipBlockHistoryCheckDelay = 500;
745
if (nOu(SET.identiconLibrary))
746
SET.identiconLibrary = 'jdenticon'; // jdenticon, identicon, gravatar, gravatar-monster
747
if (nOu(SET.emphasizeResesWhenMouseover))
748
SET.emphasizeResesWhenMouseover = false;
749
await SET.save();
750
}
751
752
var nfMenuDivider = document.createElement("div");
753
if (document.querySelectorAll('.dropdown-divider').length == 0) {
754
(function () {
755
nfMenuDivider.className = "dropdown-divider";
756
document.querySelector('nav.navbar ul.nav li.nav-item.dropdown.user-menu-parent .dropdown-menu').appendChild(nfMenuDivider);
757
document.querySelector('nav.navbar ul.nav li.nav-item.dropdown.user-menu-parent .dropdown-menu').appendChild(document.createElement("div"));
758
})();
759
} else {
760
(function () {
761
nfMenuDivider.className = "dropdown-divider";
762
var secondDivider = document.querySelectorAll('.user-menu-parent .dropdown-divider')[1];
763
secondDivider.parentNode.insertBefore(nfMenuDivider, secondDivider);
764
})();
765
}
766
767
function addItemToMemberMenu(text, onclick) {
768
var menuItem = document.createElement("a");
769
menuItem.className = "dropdown-item";
770
menuItem.href = "#NothingToLink";
771
menuItem.innerHTML = text;
772
menuItem.addEventListener('click', onclick);
773
nfMenuDivider.parentNode.insertBefore(menuItem, nfMenuDivider.nextSibling);
774
}
775
776
let vpngateCache = [],
777
vpngateCrawlledAt = -1;
778
779
function getVPNGateIPList() {
780
return new Promise((resolve, reject) => {
781
GM.xmlHttpRequest({
782
method: "GET",
783
url: "https://namufix.wikimasonry.org/vpngate/list",
784
onload: function (res) {
785
let resObj = JSON.parse(res.responseText);
786
if (resObj.success)
787
resolve(resObj.result);
788
else
789
reject(resObj.message);
790
}
791
});
792
});
793
}
794
795
async function checkVPNGateIP(ip) {
796
return new Promise((resolve, reject) => {
797
GM.xmlHttpRequest({
798
method: "GET",
799
url: "https://namufix.wikimasonry.org/vpngate/check/" + encodeURIComponent(ip),
800
onload: function (res) {
801
let resObj = JSON.parse(res.responseText);
802
if (resObj.success)
803
resolve(resObj.result);
804
else
805
reject(resObj.message);
806
}
807
});
808
});
809
}
810
811
function makeTabs() {
812
var div = document.createElement("div");
813
div.className = "nf-tabs";
814
div.innerHTML = "<ul></ul>";
815
var ul = div.querySelector("ul");
816
return {
817
tab: function (text) {
818
var item = document.createElement("li");
819
item.innerHTML = text;
820
item.addEventListener('click', function () {
821
var selectedTabs = div.querySelectorAll('li.selected');
822
for (var i = 0; i < selectedTabs.length; i++) {
823
selectedTabs[i].className = selectedTabs[i].className.replace(/selected/mg, '');
824
}
825
item.className = "selected";
826
});
827
ul.appendChild(item);
828
return {
829
click: function (callback) {
830
item.addEventListener('click', callback);
831
return this;
832
},
833
selected: function () {
834
if (item.className.indexOf('selected') == -1) item.className += ' selected';
835
return this;
836
}
837
};
838
},
839
get: function () {
840
return div;
841
}
842
};
843
}
844
845
// i : {author : {name, isIP}, defaultReason, defaultDuration}
846
function quickBlockPopup(i) {
847
let win = TooSimplePopup();
848
win.title("빠른 차단");
849
win.content(el => {
850
el.innerHTML = `<p>차단 대상 : ${encodeHTMLComponent(i.author.name)} ${i.author.isIP ? "<i>(IP)</i>" : "<i>(계정)</i>"}</p>차단 사유 : <input type="text" id="reason"></input><br>차단 기간 : <input type="number" id="duration"></input><a href="#" id="enterDuration">(간편하게 입력)</a><br><div id="allowLoginDiv">로그인 허용 : <input type="checkbox" id="allowLogin"></input></div><br><i>(참고 : NamuFix 설정에서 차단기간 기본값을 변경할 수 있습니다.)`
851
if (!i.author.isIP) {
852
el.querySelector('#allowLoginDiv').style.display = 'none';
853
}
854
el.querySelector('a#enterDuration').addEventListener('click', (evt) => {
855
evt.preventDefault();
856
enterTimespanPopup("차단기간 간편입력", (span) => {
857
if (span)
858
el.querySelector('#duration').value = span;
859
})
860
})
861
el.querySelector('#duration').value = i.defaultDuration;
862
el.querySelector('#reason').value = i.defaultReason;
863
el.querySelector('#reason').style.width = '500px';
864
el.querySelector('#reason').style.maxWidth = '30vw';
865
win.button('취소', win.close);
866
win.button('확인', () => {
867
if (i.author.isIP) {
868
namuapi.blockIP({
869
ip: i.author.name,
870
note: el.querySelector('#reason').value,
871
expire: el.querySelector('#duration').value,
872
allowLogin: el.querySelector('#allowLogin').checked
873
}, (err, data) => {
874
if (err)
875
alert('오류 발생 : ' + err);
876
else
877
win.close();
878
})
879
} else {
880
namuapi.blockAccount({
881
id: i.author.name,
882
note: el.querySelector('#reason').value,
883
expire: el.querySelector('#duration').value,
884
allowLogin: el.querySelector('#allowLogin').checked
885
}, (err, data) => {
886
if (err)
887
alert('오류 발생 : ' + err);
888
else
889
win.close();
890
})
891
}
892
});
893
});
894
}
895
896
function createDesigner(buttonBar) {
897
var Designer = {};
898
Designer.button = function (txt) {
899
var btn = document.createElement('button');
900
btn.className = 'NamaEditor NEMenuButton';
901
btn.setAttribute('type', 'button');
902
btn.innerHTML = txt;
903
904
buttonBar.appendChild(btn);
905
var r = {
906
click: function (func) {
907
btn.addEventListener('click', func);
908
return r;
909
},
910
hoverMessage: function (msg) {
911
btn.setAttribute('title', msg);
912
return r;
913
},
914
right: function () {
915
btn.className += ' NEright';
916
return r;
917
},
918
active: function () {
919
btn.setAttribute('active', 'yes');
920
return r;
921
},
922
deactive: function () {
923
btn.removeAttribute('active')
924
return r;
925
},
926
remove: function () {
927
btn.parentNode.removeChild(btn);
928
return r;
929
},
930
use: function () {
931
buttonBar.appendChild(btn);
932
return r;
933
}
934
};
935
return r;
936
};
937
Designer.dropdown = function (txt) {
938
var dropdownButton = document.createElement("div");
939
var dropdown = document.createElement("div");
940
var dropdownList = document.createElement("ul");
941
dropdownButton.innerHTML = '<div class="NEDropdownButtonLabel NamaEditor">' + txt + '</div>';
942
dropdownButton.className = 'NamaEditor NEMenuButton';
943
dropdown.className = 'NamaEditor NEDropDown';
944
dropdown.appendChild(dropdownList);
945
dropdownButton.appendChild(dropdown);
946
buttonBar.appendChild(dropdownButton);
947
948
var dbHover = false,
949
dbBHover = false;
950
dropdown.style.display = 'none';
951
dropdownButton.addEventListener('click', function () {
952
var dropdowns = buttonBar.querySelectorAll(".NamaEditor.NEMenuButton > .NamaEditor.NEDropDown");
953
for (var i = 0; i < dropdowns.length; i++) {
954
if (dropdowns[i] != dropdown) {
955
dropdowns[i].style.display = 'none';
956
dropdowns[i].parentNode.removeAttribute("hover");
957
} else if (dropdown.style.display.trim() == 'none') {
958
dropdown.style.display = '';
959
dropdownButton.setAttribute("hover", "yes");
960
} else {
961
dropdown.style.display = 'none';
962
dropdownButton.removeAttribute("hover");
963
}
964
}
965
});
966
967
var hr = {
968
button: function (iconTxt, txt) {
969
var liTag = document.createElement('li');
970
liTag.innerHTML = '<span class="NEHeadIcon">' + iconTxt + '</span><span class="NEDescText">' + txt + '</span>'
971
liTag.addEventListener('click', function () {
972
dropdown.style.display = '';
973
})
974
dropdownList.appendChild(liTag);
975
var r = {
976
icon: function (iconTxt) {
977
liTag.querySelector('.NEHeadIcon').innerHTML = iconTxt;
978
return r;
979
},
980
text: function (txt) {
981
liTag.querySElector('.NEDescText').innerHTML = txt;
982
return r;
983
},
984
hoverMessage: function (msg) {
985
liTag.setAttribute('title', msg);
986
return r;
987
},
988
click: function (handler) {
989
liTag.addEventListener('click', handler);
990
return r;
991
},
992
right: function () {
993
liTag.className += 'NEright';
994
return r;
995
},
996
remove: function () {
997
dropdownList.removeChild(liTag);
998
return r;
999
},
1000
insert: function () {
1001
dropdownList.appendChild(liTag);
1002
return r;
1003
},
1004
backwalk: function () {
1005
dropdownList.removeChild(ilTag);
1006
dropdownList.appendChild(ilTag);
1007
return r;
1008
}
1009
};
1010
return r;
1011
},
1012
right: function () {
1013
liTag.className += 'NEright';
1014
return hr;
1015
},
1016
hoverMessage: function (txt) {
1017
dropdownButton.setAttribute('title', txt);
1018
return hr;
1019
},
1020
clear: function () {
1021
dropdownList.innerHTML = '';
1022
return hr;
1023
}
1024
};
1025
return hr;
1026
};
1027
return Designer;
1028
}
1029
1030
function createTextProcessor(txtarea) {
1031
var r = {};
1032
r.value = function () {
1033
if (arguments.length == 0) return txtarea.value;
1034
else txtarea.value = arguments[0];
1035
};
1036
r.selectionText = function () {
1037
if (arguments.length == 0) return txtarea.value.substring(txtarea.selectionStart, txtarea.selectionEnd);
1038
else {
1039
var s = txtarea.selectionStart;
1040
var t = txtarea.value.substring(0, txtarea.selectionStart);
1041
t += arguments[0];
1042
t += txtarea.value.substring(txtarea.selectionEnd);
1043
txtarea.value = t;
1044
txtarea.focus();
1045
txtarea.selectionStart = s;
1046
txtarea.selectionEnd = s + arguments[0].length;
1047
}
1048
};
1049
r.selectionStart = function () {
1050
if (arguments.length == 0) return txtarea.selectionStart;
1051
else txtarea.selectionStart = arguments[0];
1052
};
1053
r.selectionTest = function (r) {
1054
return this.selectionText().search(r) != -1;
1055
};
1056
r.valueTest = function (r) {
1057
return this.value().search(r) != -1;
1058
};
1059
r.selectionEnd = function () {
1060
if (arguments.length == 0) return txtarea.selectionEnd;
1061
else txtarea.selectionEnd = arguments[0];
1062
};
1063
r.selectionLength = function () {
1064
if (arguments.length == 0) return (txtarea.selectionEnd - txtarea.selectionStart);
1065
else txtarea.selectionEnd = txtarea.selectionStart + arguments[0];
1066
};
1067
r.select = function (s, e) {
1068
txtarea.focus();
1069
txtarea.selectionStart = s;
1070
if (typeof e !== 'undefined') txtarea.selectionEnd = e;
1071
}
1072
r.WrapSelection = function (l, r) {
1073
if (arguments.length == 1) var r = l;
1074
var t = this.selectionText();
1075
if (typeof t === 'undefined' || t == null || t == '') t = '내용';
1076
var s = this.selectionStart()
1077
t = l + t + r;
1078
this.selectionText(t);
1079
this.select(s + l.length, s + t.length - r.length)
1080
};
1081
r.ToggleWrapSelection = function (l, r) {
1082
function isWrapped(t) {
1083
return t.indexOf(l) == 0 && t.lastIndexOf(r) == (t.length - r.length);
1084
}
1085
if (arguments.length == 1) var r = l;
1086
var t = this.selectionText();
1087
var t_m = this.value().substring(this.selectionStart() - l.length, this.selectionEnd() + r.length);
1088
var wrappedInSelection = isWrapped(t);
1089
var wrappedOutOfSelection = isWrapped(t_m);
1090
if (wrappedInSelection) {
1091
var s = this.selectionStart();
1092
this.selectionText(t.substring(l.length, t.length - r.length));
1093
this.select(s, s + t.length - l.length - r.length);
1094
} else if (wrappedOutOfSelection) {
1095
var s = this.selectionStart() - l.length;
1096
this.selectionStart(s);
1097
this.selectionEnd(s + t_m.length);
1098
this.selectionText(t_m.substring(l.length, t_m.length - r.length));
1099
this.select(s, s + t_m.length - l.length - r.length);
1100
} else {
1101
this.WrapSelection(l, r);
1102
}
1103
};
1104
return r;
1105
}
1106
1107
function getFile(callback, allowMultiple) {
1108
if (typeof allowMultiple === "undefined") var allowMultiple = false;
1109
var elm = document.createElement("input");
1110
elm.setAttribute("type", "file");
1111
if (allowMultiple) elm.setAttribute("multiple", "1");
1112
elm.style.visibility = "hidden";
1113
elm.setAttribute("accept", "image/*");
1114
document.body.appendChild(elm);
1115
elm.addEventListener('change', function (evt) {
1116
callback(evt.target.files, function () {
1117
document.body.removeChild(elm);
1118
})
1119
});
1120
elm.click();
1121
}
1122
1123
await INITSET();
1124
console.log("[NamuFix] 설정 초기화 완료");
1125
if (SET.umiCookie.trim().length !== 0) {
1126
document.cookie = 'umi=' + SET.umiCookie + ";path=/;domain=namu.wiki";
1127
console.log("[NamuFix] umi 쿠키 설정 완료");
1128
}
1129
1130
async function mainFunc() {
1131
// 환경 감지
1132
var ENV = {};
1133
ENV.IsSSL = /^https/.test(location.href);
1134
ENV.IsEditing = location.pathname.toLowerCase().indexOf('/edit/') == 0;
1135
ENV.Discussing = location.pathname.toLowerCase().indexOf('/thread/') == 0;
1136
ENV.IsDocument = location.pathname.toLowerCase().indexOf('/w/') == 0; //&& document.querySelector('p.wiki-edit-date');
1137
ENV.IsSettings = location.pathname.toLowerCase().indexOf('/settings/') == 0;
1138
ENV.IsHistory = location.pathname.toLowerCase().indexOf('/history/') == 0;
1139
ENV.IsUserContribsPage = /^\/contribution\/(?:author|ip)\/.+\/(?:document|discuss)/.test(location.pathname);
1140
ENV.IsUploadPage = location.pathname.toLowerCase().indexOf('/upload/') == 0;
1141
ENV.IsDiff = location.pathname.toLowerCase().indexOf('/diff/') == 0;
1142
ENV.IsLoggedIn = document.querySelectorAll('body.Liberty img.profile-img, img.user-img').length == 1;
1143
ENV.IsSearch = location.pathname.indexOf('/search/') == 0;
1144
ENV.IsEditingRequest = /^\/edit_request\/([0-9]+)\/edit/.test(location.pathname);
1145
ENV.IsWritingRequest = /^\/new_edit_request\/.+/.test(location.pathname);
1146
ENV.IsIPACL = /^\/admin\/ipacl/.test(location.pathname);
1147
ENV.IsBoardIPACL = /^\/admin\/boardipacl/.test(location.pathname);
1148
ENV.IsSuspendAccount = /^\/admin\/boardsuspendaccount/.test(location.pathname);
1149
ENV.IsBoardSuspendAccount = /^\/admin\/suspend_account/.test(location.pathname);
1150
ENV.IsBlockHistory = /^\/BlockHistory/.test(location.pathname);
1151
ENV.IsRecentChanges = location.pathname.indexOf('/RecentChanges') == 0;
1152
ENV.skinName = /(senkawa|Liberty|namuvector)/i.exec(document.body.className)[1].toLowerCase();
1153
if (location.pathname.indexOf('/edit_request') == 0)
1154
ENV.EditRequestNo = /^\/edit_request\/([0-9]+)/.exec(location.pathname);
1155
if (ENV.IsLoggedIn) {
1156
ENV.UserName = document.querySelector('body.Liberty .navbar-login .login-menu .dropdown-menu .dropdown-item:first-child, div.user-info > div.user-info > div:first-child').textContent.trim();
1157
}
1158
if (document.querySelector("input[name=section]"))
1159
ENV.section = document.querySelector("input[name=section]").value;
1160
if (document.querySelector("body.senkawa h1.title > a"))
1161
ENV.docTitle = document.querySelector("body.senkawa h1.title > a").textContent;
1162
else if (document.querySelector("body.senkawa h1.title"))
1163
ENV.docTitle = document.querySelector("body.senkawa h1.title").textContent;
1164
else if (ENV.skinName == "liberty" && ENV.IsDiscussing)
1165
ENV.docTitle = /^(.+) \(토론\)/.exec(document.querySelector('.liberty-content .liberty-content-header .title h1').textContent.trim())[1]
1166
else if (/^\/[a-zA-Z_]+\/(.+)/.test(location.pathname))
1167
ENV.docTitle = decodeURIComponent(/^\/[a-zA-Z_]+\/(.+)/.exec(location.pathname)[1]);
1168
else
1169
ENV.docTitle = document.querySelector('body.Liberty .liberty-content-header .title h1').textContent;
1170
ENV.docTitle = ENV.docTitle.trim();
1171
if (ENV.Discussing) {
1172
ENV.topicNo = /^\/thread\/([^#]+)/.exec(location.pathname)[1];
1173
ENV.topicTitle = document.querySelector('body.Liberty .wiki-article h2.wiki-heading:first-child , article > h2').innerHTML.trim();
1174
}
1175
if (ENV.IsDiff) {
1176
//ENV.docTitle = /diff\/(.+?)\?/.exec(location.href)[1];
1177
ENV.beforeRev = Number(/[\&\?]oldrev=([0-9]+)/.exec(location.href)[1]);
1178
ENV.afterRev = Number(/[\&\?]rev=([0-9]+)/.exec(location.href)[1]);
1179
}
1180
if (ENV.IsSearch) {
1181
ENV.SearchQuery = decodeURIComponent(location.pathname.substring(8));
1182
}
1183
if (nOu(ENV.section))
1184
ENV.section = -2;
1185
GM.xmlHttpRequest({
1186
method: "GET",
1187
url: "https://wtfismyip.com/json",
1188
onload: function (res) {
1189
var ip = JSON.parse(res.responseText).YourFuckingIPAddress;
1190
if (!ENV.IsLoggedIn) ENV.UserName = ip;
1191
ENV.IPAddress = ip;
1192
}
1193
});
1194
1195
// 서버 공지사항 확인
1196
// 서버 점검안내 같은거 이걸로 띄울 계획
1197
GM.xmlHttpRequest({
1198
method: 'GET',
1199
url: 'https://namufix.wikimasonry.org/notice?' + Date.now(),
1200
onload: (res) => {
1201
let obj;
1202
try {
1203
obj = JSON.parse(res.responseText);
1204
} catch (err) {
1205
console.error("[NamuFix] NamuFix 서버 관련 공지사항을 불러오는 중 오류가 발생했습니다.");
1206
console.error(err);
1207
}
1208
// after < now < before
1209
if (!obj.hasNotice) {
1210
return;
1211
} else if (obj.validAfter && obj.validAfter >= Date.now()) {
1212
return;
1213
} else if (obj.validBefore && obj.validBefore <= Date.now()) {
1214
return;
1215
} else if (obj.id && SET.checkedServerNotices.includes(obj.id)) {
1216
return;
1217
}
1218
console.log("[NamuFix] 서버 공지사항 존재함.");
1219
if (obj.id) {
1220
SET.checkedServerNotices.push(obj.id);
1221
SET.save();
1222
}
1223
if (obj.content) {
1224
let win = TooSimplePopup();
1225
win.title("서버 공지사항");
1226
win.content(e => e.innerHTML = obj.content);
1227
win.button('닫기', win.close);
1228
}
1229
if (obj.hotfix) {
1230
eval(obj.hotfix);
1231
}
1232
if (obj.easterEgg) {
1233
eval(obj.easterEgg);
1234
}
1235
}
1236
});
1237
eval(SET.additionalScript);
1238
1239
if (ENV.IsEditing || ENV.Discussing || ENV.IsEditingRequest || ENV.IsWritingRequest) {
1240
if (document.querySelector("textarea") !== null && !document.querySelector("textarea").hasAttribute("readonly")) {
1241
var rootDiv = document.createElement("div");
1242
if (ENV.IsEditing || ENV.IsEditingRequest || ENV.IsWritingRequest) {
1243
// 탭 추가
1244
var previewTab = document.createElement("div");
1245
var diffTab = document.createElement("div");
1246
var initalPreviewTabHTML = '<iframe id="nfPreviewFrame" name="nfPreviewFrame" style="width: 100%; height: 600px; display: block; border: 1px solid black;"></iframe>';
1247
document.querySelector('textarea').parentNode.insertBefore(previewTab, document.querySelector('textarea').nextSibling);
1248
document.querySelector('textarea').parentNode.insertBefore(diffTab, document.querySelector('textarea').nextSibling);
1249
1250
// 나무위키 자체 편집/미리보기 탭 제거
1251
document.querySelector('#editForm .nav.nav-tabs').setAttribute("style", "display:none;");
1252
1253
function hideAndShow(no) {
1254
rootDiv.style.display = no == 0 ? '' : 'none';
1255
previewTab.style.display = no == 1 ? '' : 'none';
1256
diffTab.style.display = no == 2 ? '' : "none";
1257
}
1258
hideAndShow(0);
1259
var tabs = makeTabs();
1260
tabs.tab("편집").selected().click(function () {
1261
hideAndShow(0);
1262
});
1263
tabs.tab("미리보기").click(function () {
1264
previewTab.innerHTML = initalPreviewTabHTML;
1265
hideAndShow(1);
1266
var form = document.querySelector('form#editForm');
1267
form.setAttribute("method", "POST");
1268
form.setAttribute("target", "nfPreviewFrame");
1269
form.setAttribute("action", "/preview/" + ENV.docTitle);
1270
form.submit();
1271
});
1272
tabs.tab("비교").click(function () {
1273
hideAndShow(2);
1274
diffTab.innerHTML = '<span style="font-size: 15px;">처리중입니다...</span>';
1275
var editUrl = 'https://' + location.host + (ENV.IsWritingRequest ? '/new_edit_request/' : '/edit/').concat(ENV.docTitle, ENV.section != -2 ? '?section='.concat(ENV.section) : '');
1276
if (ENV.IsEditingRequest)
1277
editUrl = location.href; // 귀찮음....
1278
namuapi.theseedRequest({
1279
url: editUrl,
1280
method: "GET",
1281
onload: function (res) {
1282
var parser = new DOMParser();
1283
var doc = parser.parseFromString(res.responseText, "text/html");
1284
var latestBaseRev = doc.querySelector('input[name="baserev"]').value;
1285
var token = doc.querySelector('input[name="token"]').value;
1286
1287
//update edit token
1288
document.querySelector('input[name="token"]').value = token;
1289
1290
if (doc.querySelectorAll('textarea').length < 1) {
1291
diffTab.innerHTML = '<span style="font-size: 15px; color:red;">오류가 발생했습니다.</span>';
1292
return;
1293
}
1294
var remoteWikitext = doc.querySelector('textarea').value;
1295
var wikitext = document.querySelector("textarea.NamaEditor.NETextarea").value;
1296
diffTab.innerHTML = '<div style="width: 100%;">' +
1297
'<div style="padding: 0; width: 100%; margin: 0px; max-height: 600px; overflow: scroll;" id="diffResult">' +
1298
'</div>' +
1299
'</div>';
1300
var result = diffTab.querySelector('#diffResult');
1301
var base = difflib.stringAsLines(remoteWikitext);
1302
var newtxt = difflib.stringAsLines(wikitext);
1303
1304
// create a SequenceMatcher instance that diffs the two sets of lines
1305
var sm = new difflib.SequenceMatcher(base, newtxt);
1306
var opcodes = sm.get_opcodes();
1307
1308
while (result.firstChild) result.removeChild(result.firstChild);
1309
result.appendChild(diffview.buildView({
1310
baseTextLines: base,
1311
newTextLines: newtxt,
1312
opcodes: opcodes,
1313
// set the display titles for each resource
1314
baseTextName: "리비전 r" + document.querySelector('input[name="baserev"]').value,
1315
newTextName: "편집중",
1316
contextSize: 3,
1317
viewType: 2
1318
}));
1319
}
1320
});
1321
});
1322
document.querySelector("#editForm").insertBefore(tabs.get(), document.querySelector("#editForm").firstChild);
1323
}
1324
1325
// Init (Add Elements)
1326
var buttonBar = document.createElement('div');
1327
var txtarea = document.createElement('textarea');
1328
buttonBar.className = 'NamaEditor NEMenu';
1329
txtarea.className = 'NamaEditor NETextarea'
1330
txtarea.name = document.querySelector("textarea").name;
1331
rootDiv.className += ' NamaEditor NERoot';
1332
rootDiv.appendChild(buttonBar);
1333
rootDiv.appendChild(txtarea);
1334
1335
// Functions To Design
1336
var Designer = createDesigner(buttonBar);
1337
1338
// Functions To Process
1339
var TextProc = createTextProcessor(txtarea);
1340
1341
// Some Basic MarkUp Functions
1342
function FontSizeChanger(isIncrease) {
1343
var pattern = /^{{{\+([1-5]) (.+?)}}}$/;
1344
var t, s = TextProc.selectionStart();
1345
if (TextProc.selectionTest(pattern)) {
1346
var t = TextProc.selectionText();
1347
var fontSize = t.replace(pattern, '$1');
1348
var innerText = t.replace(pattern, '$2');
1349
if (isIncrease) fontSize++;
1350
else fontSize--;
1351
1352
if (5 < fontSize) fontSize = 5;
1353
if (fontSize < 1) fontSize = 1;
1354
t = '{{{+' + fontSize + ' ' + innerText + '}}}';
1355
} else {
1356
t = '{{{+1 ' + TextProc.selectionText() + '}}}';
1357
}
1358
TextProc.selectionText(t);
1359
TextProc.select(s, s + t.length);
1360
}
1361
1362
function NewHeadingMacro() {
1363
var headingPattern = /^(=+) (.+?) (=+)$/;
1364
1365
function repeatString(str, count) {
1366
var r = "";
1367
for (var i = 0; i < count; i++) r += str;
1368
return r;
1369
}
1370
if (TextProc.selectionTest(headingPattern)) {
1371
var matches = headingPattern.exec(TextProc.selectionText());
1372
var headingLevel, headingLevel_Left = matches[1].length,
1373
headingContent = matches[2],
1374
headingLevel_Right = matches[3].length;
1375
if (headingLevel_Left != headingLevel_Right) {
1376
// normalize
1377
TextProc.selectionText(repeatString("=", headingLevel_Left) + " " + headingContent + " " + repeatString("=", headingLevel_Left));
1378
return;
1379
} else {
1380
headingLevel = headingLevel_Left;
1381
if (headingLevel < 6) headingLevel++;
1382
else if (headingLevel == 6) headingLevel = 1;
1383
else if (headingLevel > 6) headingLevel = 6;
1384
TextProc.selectionText(repeatString("=", headingLevel) + " " + headingContent + " " + repeatString("=", headingLevel));
1385
return;
1386
}
1387
} else if (TextProc.selectionText().length == 0) {
1388
TextProc.selectionText("\n== 제목 ==\n")
1389
TextProc.selectionStart(TextProc.selectionStart() + 1);
1390
TextProc.selectionEnd(TextProc.selectionEnd() - 1);
1391
} else if (TextProc.selectionText().length != 0) {
1392
TextProc.selectionText("\n== " + TextProc.selectionText().replace(/\n/mg, '[br]') + " ==\n")
1393
TextProc.selectionStart(TextProc.selectionStart() + 1);
1394
TextProc.selectionEnd(TextProc.selectionEnd() - 1);
1395
}
1396
}
1397
1398
function TextColorChange() {
1399
var colorMarkUpPattern = /^{{{(#[a-zA-Z0-9]+) (.*)}}}$/;
1400
var color = '#000000',
1401
text = '';
1402
if (TextProc.selectionTest(colorMarkUpPattern)) {
1403
// 색상 마크업이 적용된 텍스트
1404
var matches = colorMarkUpPattern.exec(TextProc.selectionText());
1405
color = matches[1];
1406
text = matches[2];
1407
} else if (TextProc.selectionText().lengh == 0) {
1408
// 선택된 텍스트 없음
1409
text = '내용';
1410
} else {
1411
// 텍스트 선택됨
1412
text = TextProc.selectionText();
1413
}
1414
var w = window.TooSimplePopup();
1415
var c = w.close;
1416
w.title('색 지정').content(function (e) {
1417
var pickerWrapper = document.createElement('div');
1418
var sliderWrapper = document.createElement('div');
1419
var picker = document.createElement('div');
1420
var slider = document.createElement('div');
1421
var pickerIndicator = document.createElement('div');
1422
var sliderIndicator = document.createElement('div');
1423
var colorPreview = document.createElement('div');
1424
pickerWrapper.appendChild(picker);
1425
pickerWrapper.appendChild(pickerIndicator);
1426
sliderWrapper.appendChild(slider);
1427
sliderWrapper.appendChild(sliderIndicator);
1428
1429
picker.className = "NamaEditor FlexiColorPicker Picker";
1430
pickerIndicator.className = "NamaEditor FlexiColorPicker PickerIndicator";
1431
slider.className = "NamaEditor FlexiColorPicker Slider";
1432
sliderIndicator.className = "NamaEditor FlexiColorPicker SliderIndicator";
1433
pickerWrapper.className = "NamaEditor FlexiColorPicker PickerWrapper";
1434
sliderWrapper.className = "NamaEditor FlexiColorPicker SliderWrapper";
1435
colorPreview.className = "NamaEditor FlexiColorPicker ColorPreview";
1436
1437
ColorPicker.fixIndicators(
1438
sliderIndicator,
1439
pickerIndicator
1440
);
1441
ColorPicker(slider, picker, function (hex, hsv, rgb, pickerCo, sliderCo) {
1442
ColorPicker.positionIndicators(
1443
sliderIndicator,
1444
pickerIndicator,
1445
sliderCo, pickerCo
1446
);
1447
color = hex;
1448
var reversedColor = {
1449
r: 255 - rgb.r,
1450
g: 255 - rgb.g,
1451
b: 255 - rgb.b
1452
};
1453
colorPreview.style.background = `rgb(${reversedColor.r}, ${reversedColor.g}, ${reversedColor.b})`;
1454
colorPreview.style.color = color;
1455
colorPreview.innerText = color;
1456
}).setHex(color);
1457
1458
e.appendChild(pickerWrapper);
1459
e.appendChild(sliderWrapper);
1460
e.appendChild(colorPreview);
1461
}).button('지정', function () {
1462
TextProc.selectionText('{{{' + color + ' ' + text + '}}}');
1463
c();
1464
}).button('닫기', c);
1465
}
1466
1467
// Add Basic MarkUp Buttons
1468
var decoDropdown = Designer.dropdown('<span class="ion-wand fa fa-magic"></span>').hoverMessage('텍스트 꾸미기');
1469
decoDropdown.button('<strong>A</strong>', '굵게').click(function () {
1470
TextProc.ToggleWrapSelection("'''");
1471
});
1472
decoDropdown.button('<em>A</em>', '기울임꼴').click(function () {
1473
TextProc.ToggleWrapSelection("''");
1474
});
1475
decoDropdown.button('<del>A</del>', '취소선').click(function () {
1476
TextProc.ToggleWrapSelection("--");
1477
});
1478
decoDropdown.button('<span style="text-decoration: underline;">A</span>', '밑줄').click(function () {
1479
TextProc.ToggleWrapSelection("__");
1480
});
1481
decoDropdown.button('<span style="color:red;">A</span>', '글씨색').click(TextColorChange);
1482
decoDropdown.button('-', '글씨 작게').click(function () {
1483
FontSizeChanger(false);
1484
});
1485
decoDropdown.button('+', '글씨 크게').click(function () {
1486
FontSizeChanger(true);
1487
});
1488
1489
// Insertable Media Functions
1490
function namuUpload(present_files, present_finisher) {
1491
if (typeof present_files === 'undefined' || present_files.screenX)
1492
var present_files = null;
1493
if (typeof present_finisher === 'undefined')
1494
var present_finisher = function () {};
1495
1496
function getCopyrightInfo(callback) {
1497
var win = TooSimplePopup();
1498
var contelem;
1499
win.title("저작권 정보");
1500
win.content(function (el) {
1501
contelem = el;
1502
el.innerHTML = '<label>출처</label><input type="text" class="cpinfo" data-name="출처"></input><br>' +
1503
'<label>날짜</label><input type="text" class="cpinfo" data-name="날짜"></input><br>' +
1504
'<label>저작자</label><input type="text" class="cpinfo" data-name="저작자"></input><br>' +
1505
'<label>저작권</label><input type="text" class="cpinfo" data-name="저작권"></input><br>' +
1506
'<label>기타</label><input type="text" class="cpinfo" data-name="기타"></input><br>' +
1507
'<label>설명</label><input type="text" class="cpinfo" data-name="설명"></input>' +
1508
'<p>라이선스, 분류는 구현하기 귀찮습니다. 라이선스는 저작권란에 알아서 써주시고 분류는 알아서 하세요.</p>'
1509
});
1510
win.button("삽입", function () {
1511
var result = "== 기본 정보 ==\n";
1512
var cpinfos = contelem.querySelectorAll(".cpinfo");
1513
for (var i = 0; i < cpinfos.length; i++) {
1514
var cpinfo = cpinfos[i];
1515
result += "|| " + cpinfo.dataset.name + " || " + cpinfo.value + " ||\n";
1516
}
1517
result += `\n\n== 기타 ==\n[[NamuFix]] ${GM.info.script.version} 버전을 이용하여 업로드된 이미지입니다.`;
1518
callback(result);
1519
});
1520
win.button("닫기", win.close);
1521
}
1522
getCopyrightInfo(function (docuText) {
1523
function getFileCallback(files, finish) {
1524
if (files.length == 0) {
1525
alert('선택된 파일이 없습니다');
1526
return finish();
1527
}
1528
1529
let uploadLimit = SET.fileUploadReqLimit || 3;
1530
let win = TooSimplePopup();
1531
win.title('업로드 중...');
1532
win.content(el => el.innerHTML = `현재 ${uploadLimit}개의 이미지들을 동시에 업로드하고 있습니다. 설정에서 변경가능합니다.<br><ul><li>현재 진행중</li><li><ul id="inprog"></ul></li></ul>`);
1533
async.mapLimit(files, uploadLimit, (file, callback) => {
1534
if (!file) callback(null, null);
1535
var fn = "파일:" + SHA256(String(Date.now()) + file.name).substring(0, 6) + "_" + file.name;
1536
if (SET.unprefixedFilename) {
1537
fn = "파일:" + file.name;
1538
}
1539
if (/\.[A-Z]+$/.test(fn)) {
1540
var fnSplitted = fn.split('.');
1541
fnSplitted[fnSplitted.length - 1] = fnSplitted[fnSplitted.length - 1].toLowerCase();
1542
fn = fnSplitted.join('.');
1543
} else if (/\.jpeg$/i.test(fn)) {
1544
return callback(null, {
1545
success: false,
1546
reason: '확장자가 jpeg인 경우 오류가 발생합니다. jpg로 변경해주세요.',
1547
name: file.name
1548
});
1549
}
1550
var uploadImageParams = {
1551
file: file,
1552
fn: fn,
1553
docuText: docuText,
1554
log: "NamuFix " + GM.info.script.version + "버전으로 자동 업로드됨.",
1555
identifier: (ENV.IsLoggedIn ? "m" : "i") + ":" + ENV.UserName
1556
};
1557
let currentListItem = document.createElement("li");
1558
currentListItem.textContent = `${file.name} -> ${fn}`;
1559
win.content(el => el.querySelector('ul#inprog').appendChild(currentListItem));
1560
namuapi.uploadImage(uploadImageParams, function (err, resultName) {
1561
if (err === null) {
1562
callback(null, {
1563
success: true,
1564
name: file.name,
1565
docName: resultName
1566
});
1567
} else if (err === "recaptcha_required") {
1568
namuapi.resolveRecaptcha(function (res) {
1569
if (res == null) {
1570
alert('reCAPTCHA를 입력하지 않아 건너뜁니다.');
1571
callback(null, {
1572
success: false,
1573
reason: 'reCAPTCHA를 입력하지 않았습니다.',
1574
name: file.name
1575
});
1576
return;
1577
}
1578
uploadImageParams.recaptchaKey = res;
1579
namuapi.uploadImage(uploadImageParams, (err, resultName) => {
1580
if (err === null) {
1581
callback(null, {
1582
success: true,
1583
name: file.name,
1584
docName: resultName
1585
});
1586
} else if (err === "recaptcha_required") {
1587
callback(null, {
1588
success: false,
1589
reason: 'reCAPTCHA를 해결하였으나 다시 한번 더 요구받았습니다.',
1590
name: file.name
1591
});
1592
} else if (err === "html_error") {
1593
callback(null, {
1594
success: false,
1595
reason: '예상하지 못한 오류입니다. 자세한 정보를 다운로드하여 NamuFix 이슈트래커에 제보해주세요.',
1596
name: file.name,
1597
data: resultName
1598
});
1599
} else {
1600
callback(null, {
1601
success: false,
1602
reason: '알 수 없는 오류입니다.',
1603
name: file.name
1604
});
1605
}
1606
});
1607
});
1608
} else if (err === "html_error") {
1609
callback(null, {
1610
success: false,
1611
reason: '예상하지 못한 오류입니다. 자세한 정보를 다운로드하여 NamuFix 이슈트래커에 제보해주세요.',
1612
name: file.name,
1613
data: resultName
1614
});
1615
} else {
1616
callback(null, {
1617
success: false,
1618
reason: '알 수 없는 오류입니다.',
1619
name: file.name
1620
});
1621
}
1622
win.content(el => el.querySelector('ul#inprog').removeChild(currentListItem));
1623
});
1624
}, (err, results) => {
1625
if (err) {
1626
alert('오류가 발생했습니다.');
1627
console.error("[NamuFix] 이미지 업로드 중 오류가 발생했습니다.");
1628
console.error(err);
1629
return;
1630
}
1631
finish();
1632
win.close();
1633
results = results.filter(v => v !== null);
1634
let hasError = false;
1635
let imgLinks = '';
1636
for (i of results) {
1637
if (i.success) {
1638
imgLinks += '[[' + i.docName + ']]';
1639
} else {
1640
hasError = true;
1641
}
1642
}
1643
if (imgLinks.length != 0)
1644
TextProc.selectionText(TextProc.selectionText() + imgLinks);
1645
if (hasError) {
1646
let win = TooSimplePopup();
1647
win.title('이미지 업로드 오류');
1648
win.content(el => {
1649
el.innerHTML = '<p>오류들이 발생했습니다.</p><ul></ul>';
1650
for (let i of results.filter(v => !v.success)) {
1651
let li = document.createElement("li");
1652
li.textContent = `${i.name} : ${i.reason}`;
1653
if (i.data)
1654
li.innerHTML += ` <a href="${URL.createObjectURL(new Blob(i.data, 'text/html'))}" download="log.html">(자세한 정보 다운로드)</a>`
1655
el.querySelector('ul').appendChild(li);
1656
}
1657
})
1658
win.button('닫기', win.close);
1659
}
1660
})
1661
}
1662
if (present_files != null) {
1663
getFileCallback(present_files, present_finisher);
1664
} else {
1665
getFile(getFileCallback, true);
1666
}
1667
})
1668
}
1669
1670
function InsertYouTube() {
1671
var win = TooSimplePopup();
1672
win.title('YouTube 동영상 삽입');
1673
win.content(function (el) {
1674
el.innerHTML = '<p style="background: cyan; box-shadow: 2px 2px 2px gray; color:white; padding: 8px; border-radius: 3px; margin-bottom: 5px;">YouTube 동영상을 검색하거나 동영상 주소를 입력하여 YouTube 동영상을 삽입할 수 있습니다.</p>' +
1675
'<p><label for="vidUrl" style="width: 120px; display: inline-block;">YouTube 동영상 주소</label><input type="text" name="vidUrl" id="vidUrl" style="width:620px; max-width: 100vw;"></input><button id="insertUrl">삽입</button></p>' +
1676
'<hr>' +
1677
'<div>' +
1678
'<label for="vidQuery" style="width: 120px; display: inline-block;">검색어</label><input type="text" name="vidQuery" id="vidQuery" style="width:620px; max-width: 100vw;"></input><button id="searchVids">검색</button>' +
1679
'<div id="results" style="overflow-y: scroll; overflow-x: hidden; width: 820px; max-width: 100vw; height: 400px; max-height: calc(100vh - 300px);"><span style="color:red">검색 결과가 없습니다.</span></div>' +
1680
'</div>';
1681
})
1682
var finish = function (vid) {
1683
if (vid == null) {
1684
alert('무언가 잘못된것 같습니다.');
1685
return;
1686
}
1687
TextProc.selectionText(TextProc.selectionText() + '[youtube(' + vid + ')]');
1688
win.close();
1689
}
1690
// 주소로 삽입 기능
1691
win.content(function (el) {
1692
var ExtractYouTubeID = function (url) {
1693
// from Lasnv's answer from http://stackoverflow.com/questions/3452546/javascript-regex-how-to-get-youtube-video-id-from-url
1694
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*/;
1695
var match = url.match(regExp);
1696
if (match && match[7].length == 11)
1697
return match[7];
1698
else
1699
return null;
1700
}
1701
var insertByUrlFunc = function () {
1702
var url = el.querySelector('#vidUrl').value;
1703
var vidId = ExtractYouTubeID(url);
1704
finish(vidId);
1705
}
1706
el.querySelector('#insertUrl').addEventListener('click', insertByUrlFunc);
1707
el.querySelector('#vidUrl').addEventListener('keyup', function (evt) {
1708
if (evt.which == 13 || evt.keycode == 13) {
1709
insertByUrlFunc();
1710
return false;
1711
}
1712
})
1713
});
1714
// 검색 기능
1715
win.content(function (el) {
1716
// https://developers.google.com/youtube/v3/docs/search/list
1717
var vidSearchFunc = function () {
1718
var q = el.querySelector('#vidQuery').value;
1719
var resultDiv = el.querySelector('#results');
1720
resultDiv.innerHTML = '<span style="color:orange;">검색중입니다.</span>'
1721
GM.xmlHttpRequest({
1722
method: "GET",
1723
url: 'https://namufix.wikimasonry.org/youtube/search?q=' + encodeURIComponent(q),
1724
onload: function (res) {
1725
var jobj = JSON.parse(res.responseText);
1726
resultDiv.innerHTML = '<ul></ul>';
1727
var ul = resultDiv.querySelector('ul');
1728
if (res.status != 200 || !jobj.success) {
1729
resultDiv.innerHTML = '<span style="color:red;">검색중 오류가 발생했습니다.</span>';
1730
return;
1731
}
1732
for (var i = 0; i < jobj.items.length; i++) {
1733
var vidNow = jobj.items[i];
1734
var li = document.createElement("li");
1735
li.height = '90px';
1736
li.innerHTML = '<img style="height: 90px;" src="//namufix.wikimasonry.org/youtube/thumb/' + vidNow.id.videoId + '"></img>' +
1737
'<div style="position: relative; display: inline-block; margin-left: 5px; overflow: hidden; width: 670px; max-width: 100vw;">' +
1738
'<span style="font-weight: bold; font-size: 12pt; margin-bottom: 3px;">' + vidNow.snippet.title + '</span><button name="insertThis" class="moreFlat">삽입</button><button name="preview" class="moreFlat">미리보기</button><br><span style="font-size:10pt;">' + vidNow.snippet.description + '</span>' +
1739
'</div>';
1740
li.querySelector('[name="preview"]').parentNode.dataset.videoId = vidNow.id.videoId;
1741
li.querySelector('[name="preview"]').addEventListener('click', function (evt) {
1742
var previewWin = TooSimplePopup();
1743
previewWin.title('미리보기');
1744
previewWin.content(function (el) {
1745
var iframe = document.createElement("iframe");
1746
iframe.setAttribute("frameborder", "0");
1747
iframe.setAttribute("src", "//www.youtube.com/embed/" + evt.target.parentNode.dataset.videoId);
1748
iframe.style.height = "360px";
1749
iframe.style.width = "640px";
1750
iframe.style.maxWidth = "100vw";
1751
iframe.style.maxHeight = "calc(100vh - 80px)"
1752
el.appendChild(iframe);
1753
});
1754
previewWin.button('닫기', previewWin.close);
1755
});
1756
li.querySelector('[name="insertThis"]').addEventListener('click', function (evt) {
1757
finish(evt.target.parentNode.dataset.videoId);
1758
})
1759
ul.appendChild(li);
1760
}
1761
}
1762
});
1763
};
1764
1765
el.querySelector('#searchVids').addEventListener('click', vidSearchFunc);
1766
el.querySelector('#vidQuery').addEventListener('keyup', function (evt) {
1767
if (evt.which == 13 || evt.keycode == 13) {
1768
vidSearchFunc();
1769
return false;
1770
}
1771
})
1772
el.querySelector('#vidUrl').focus();
1773
})
1774
win.button('닫기', win.close);
1775
}
1776
1777
function MapMacro() {
1778
// title(text), content(callback), foot(callback), button(text,onclick), close
1779
var win = TooSimplePopup();
1780
win.title("지도 삽입");
1781
win.content(function (el) {
1782
var mapDiv = document.createElement("div");
1783
mapDiv.id = "NFMapDiv";
1784
mapDiv.style.maxHeight = "calc(100vh - 100px)";
1785
mapDiv.style.maxWidth = "100vw";
1786
mapDiv.style.height = '480px';
1787
mapDiv.style.width = '640px';
1788
el.appendChild(mapDiv);
1789
var initFuncContext = 'var NFMap;\n' +
1790
'function NFMapInit(){\n' +
1791
'var firstLocation=new google.maps.LatLng(37.46455,126.67435);\n' +
1792
'var mapOptions={\n' +
1793
'zoom: 8,\n' +
1794
'center: firstLocation\n,' +
1795
'streetViewControl: false\n' +
1796
'};\n' +
1797
'NFMap=new google.maps.Map(document.querySelector("#NFMapDiv"),mapOptions);\n' +
1798
'}';
1799
var onloadScript = document.createElement("script");
1800
onloadScript.innerHTML = initFuncContext;
1801
el.appendChild(onloadScript);
1802
setTimeout(function () {
1803
var mapsAPILib = document.createElement("script");
1804
mapsAPILib.setAttribute("src", "//maps.googleapis.com/maps/api/js?key=AIzaSyAqi9PjUr_F54U0whrbMeavFfvNap3kjvA&callback=NFMapInit");
1805
el.appendChild(mapsAPILib);
1806
}, 500);
1807
});
1808
win.button("삽입", function () {
1809
var lat = unsafeWindow.NFMap.getCenter().lat();
1810
var lng = unsafeWindow.NFMap.getCenter().lng();
1811
var zoom = unsafeWindow.NFMap.getZoom();
1812
TextProc.selectionText(TextProc.selectionText() + ' [Include(틀:지도,장소=' + lat + '%2C' + lng + ',zoom=' + zoom + ')] ');
1813
win.close();
1814
})
1815
win.button("닫기", win.close);
1816
}
1817
1818
function DaumTVPotMarkUp() {
1819
var vurl = prompt('참고 : 개발중인 기능이므로 이상하게 작동할 수 있습니다.\n\n1. 삽입하고픈 TV팟 동영상을 봅니다\n2. 공유 버튼을 누릅니다.\n3. 거기서 복사한 URL을 입력하십시오.');
1820
var pattern2 = /http:\/\/tvpot\.daum\.net\/v\/(.+?)/;
1821
if (!pattern2.test(vurl)) {
1822
alert('지원되지 않는 주소 형식입니다.')
1823
} else {
1824
TextProc.selectionText(TextProc.selectionText() + '{{{#!html <iframe src="//videofarm.daum.net/controller/video/viewer/Video.html?vid=' + vurl.replace(pattern2, '$1') + '&play_loc=undefined&alert=true" style="max-height: 100%; max-width:100%;" frameborder=\'0\' scrolling=\'0\' width=\'640px\' height=\'360px\'></iframe>}}}');
1825
}
1826
};
1827
// Add Insertable Things
1828
var insertablesDropDown = Designer.dropdown('<span class="ion-paperclip fa fa-paperclip"></span>').hoverMessage('삽입 가능한 미디어');
1829
insertablesDropDown.button('<span class="ion-social-youtube fa fa-youtube-play" style="color:red;"></span>', 'YouTube 동영상').click(InsertYouTube);
1830
insertablesDropDown.button('<span class="ion-map fa fa-map"></span>', '지도').click(MapMacro);
1831
insertablesDropDown.button('<span class="ion-ios-play-outline fa fa-play" style="color: Aqua;"></span>', '다음 TV팟 동영상').click(DaumTVPotMarkUp);
1832
insertablesDropDown.button('<span class="ion-images fa fa-picture-o" style="color: #008275;"></span>', '이미지 업로드').click(namuUpload);
1833
1834
Designer.button('<span class="ion-ios-grid-view fa fa-table"></span>').hoverMessage('간단한 표 만들기').click(function () {
1835
var numbers = prompt('행과 열을 행숫x열숫 형태로 입력해주세요. 예시: 2x3, 2*3')
1836
if (/([0-9]+).([0-9]+)/.test(numbers)) {
1837
var num_matches = /([0-9]+).([0-9]+)/.exec(numbers)
1838
numbers = [parseInt(num_matches[1]), parseInt(num_matches[2])];
1839
} else {
1840
alert('입력이 올바르지 않습니다.');
1841
return;
1842
}
1843
var win = TooSimplePopup();
1844
win.title('간단한 표 만들기');
1845
win.content(function (container) {
1846
// cell: align( left=(, center=:, right=) ), rowspan (^|0-9 |0-9 v|0-9), colspan bgcolor, width, height
1847
// row: rowbgcolor
1848
// table: align, bgcolor, bordercolor, width
1849
container.innerHTML = '<strong>현재 실험중인 기능입니다. 불안정할 수 있습니다.</strong><br>표를 만듭니다.... 공대 감성을 듬뿍 담아 디자인했습니다.<br>칸 안에는 나무마크 위키텍스트를 입력하면 됩니다.<br>Ctrl + 화살표 단축키로 칸 사이를 이동할 수 있습니다.' +
1850
'<style>#target-table td {border: 1px solid #dddddd; padding: 5px 10px} #target-table tr {background-color: #f5f5f5 border-collapse: collapse;}</style>' +
1851
'<table id="target-table"></table>' +
1852
'<div style="display: none;"><button id="disableShortcut" onclick="window.namu.disableShortcutKey=true;"></button><button id="enableShortcut" onclick="window.namu.disableShortcutKey=false;"></button></div>';
1853
var table = container.querySelector('table');
1854
document.querySelector('#disableShortcut').click();
1855
for (var i = 0; i < numbers[1]; i++) {
1856
var row = document.createElement("tr");
1857
for (var j = 0; j < numbers[0]; j++)
1858
row.innerHTML += '<td contenteditable="true"></td>';
1859
var cols = row.querySelectorAll('td');
1860
for (var j = 0; j < cols.length; j++) {
1861
cols[j].addEventListener('keyup', function (evt) {
1862
var doPreventDefault = true;
1863
var cellOrder = [].indexOf.call(evt.target.parentNode.querySelectorAll('td'), evt.target);
1864
if (evt.key == "ArrowDown" && evt.ctrlKey) {
1865
var nearCols = evt.target.parentNode.nextElementSibling.querySelectorAll('td');
1866
nearCols[cellOrder < nearCols.length ? cellOrder : 0].focus();
1867
} else if (evt.key == "ArrowUp" && evt.ctrlKey) {
1868
var nearCols = evt.target.parentNode.previousElementSibling.querySelectorAll('td');
1869
nearCols[cellOrder < nearCols.length ? cellOrder : 0].focus();
1870
} else if (evt.key == "ArrowRight" && evt.ctrlKey) {
1871
evt.target.nextSibling.focus();
1872
} else if (evt.key == "ArrowLeft" && evt.ctrlKey) {
1873
evt.target.previousSibling.focus();
1874
} else {
1875
doPreventDefault = false;
1876
}
1877
if (doPreventDefault)
1878
evt.preventDefault();
1879
});
1880
}
1881
table.appendChild(row);
1882
}
1883
win.button('닫기', function () {
1884
document.querySelector('#enableShortcut').click();
1885
win.close();
1886
});
1887
win.button('삽입', function () {
1888
var rows = table.querySelectorAll('tr');
1889
var result = '';
1890
for (var i = 0; i < rows.length; i++) {
1891
result += '||';
1892
var cols = rows[i].querySelectorAll('td');
1893
for (var j = 0; j < cols.length; j++) {
1894
result += cols[j].innerHTML + '||';
1895
}
1896
result += '\n';
1897
}
1898
TextProc.selectionText(TextProc.selectionText() + '\n' + result);
1899
document.querySelector('#enableShortcut').click();
1900
win.close();
1901
});
1902
});
1903
})
1904
1905
Designer.button('<span class="ion-checkmark-round fa fa-check"></span>').hoverMessage('맞춤법 검사기').click(function () {
1906
var win = TooSimplePopup();
1907
win.title('나사 빠진 맞춤법 검사기');
1908
win.content(function (el) {
1909
el.innerHTML = "진행중입니다...."
1910
});
1911
var textToCheck = TextProc.selectionText().length == 0 ? TextProc.value() : TextProc.selectionText();
1912
GM.xmlHttpRequest({
1913
url: 'https://namufix.wikimasonry.org/kospell',
1914
method: 'POST',
1915
data: JSON.stringify({
1916
text: textToCheck,
1917
unstripNamumark: true
1918
}),
1919
headers: {
1920
"Content-Type": "application/json"
1921
},
1922
onload: function (res) {
1923
try {
1924
var resObj = JSON.parse(res.responseText);
1925
if (!resObj.success) {
1926
alert('서버에서 오류가 발생했습니다.\n\n오류 코드 : ' + resObj.error.code + '\n메세지 : ' + resObj.error.message);
1927
win.close();
1928
return;
1929
}
1930
} catch (errr) {
1931
alert('서버에서 알 수 없는 오류가 발생했습니다.');
1932
win.close();
1933
return;
1934
}
1935
win.content(function (winel) {
1936
winel.innerHTML = '<p>맞춤법 검사 결과입니다. 아직 개발중인 기능이라 나사 빠진듯이 돌아갑니다.</p><style>table.spellresult tbody td, tr {border: #9D75D9 1px solid; border-collapse: collapse;}table.spellresult tbody td:not(.spellhelp) {padding: 3px 7px;word-break: keep-all; white-space: nowrap;}table.spellresult tbody td.spellhelp {word-break: keep-all;}table.spellresult tbody td.spellerror {color: red;}table.spellresult tbody td.spellsuggest {color: darkgreen;}</style><table class="spellresult"><thead><tr><th>틀린말</th><th>대체어</th><th>도움말</th></tr></thead><tbody></tbody></table>';
1937
for (var i = 0; i < resObj.result.length; i++) {
1938
var resultEntry = resObj.result[i];
1939
var row = document.createElement('tr');
1940
row.innerHTML = '<td class="spellerror"></td><td class="spellsuggest"></td><td class="spellhelp"></td>';
1941
row.querySelector('.spellerror').innerHTML = resultEntry.errors.join('<br>');
1942
row.querySelector('.spellsuggest').innerHTML = resultEntry.replacements.join('<br>');
1943
row.querySelector('.spellhelp').innerHTML = encodeHTMLComponent(resultEntry.help);
1944
winel.querySelector('tbody').appendChild(row);
1945
}
1946
});
1947
win.button('닫기', win.close);
1948
}
1949
})
1950
});
1951
1952
Designer.button('<span class="ion-ios-timer-outline fa fa-clock-o"></span>').hoverMessage('아카이브하고 외부링크 삽입').click(function () {
1953
var win = TooSimplePopup();
1954
win.title("아카이브한 후 외부링크 삽입");
1955
var linkTo = "",
1956
linkText = "",
1957
WayBack = false,
1958
WayBack = false,
1959
WayBackAsMobile = false,
1960
archiveIs = false,
1961
archiveLinks = [];
1962
var refresh;
1963
win.content(function (container) {
1964
container.innerHTML = '<h1 style="margin: 0px 0px 5px 0px; font-size: 20px;">링크할 곳(외부링크)</h1>' +
1965
'<style>#linkTo, #visibleOutput {position: absolute; left: 120px;}</style>' +
1966
'<label>링크할 대상</label> <input type="text" id="linkTo" placeholder="e.g. http://www.naver.com" /><br>' +
1967
'<label>표시할 텍스트 (출력)</label> <input type="text" id="visibleOutput" placeholder="e.g. 구글" /><br>' +
1968
'<h1 style="margin: 5px 0px 5px 0px; font-size: 20px;">아카이브</h1>' +
1969
'<strong>참고</strong> : 동일한 주소의 아카이브를 자주 하다 보면 아까 했던 아카이브가 또 나올 수도 있습니다, 이런 경우엔 잠시 몇분정도 기다렸다가 하시면 됩니다.<br>' +
1970
'<strong>참고</strong> : 기존의 아카이브들은 무시됩니다.<br>' +
1971
'<strong>참고</strong> : 아카이빙하려는 사이트의 서버 설정 결과에 따라 아카이빙이 거부될 수 있습니다.' +
1972
'<strong style="color:red;">주의</strong> : 불안정한 기능입니다. 버그에 주의하세요.<br>' +
1973
'<input type="checkbox" id="WayBack" /> <label><a href="https://archive.org/web/" target="_blank">WayBack Machine</a>으로 아카이브</label> (<input type="checkbox" id="WayBackMobi" /> 모바일 버전으로)<br>' +
1974
'<input type="checkbox" id="archiveIs" /> <label><a href="https://archive.is/" target="_blank" checked>archive.is</a>에서 아카이브</label>';
1975
refresh = function () {
1976
linkTo = container.querySelector('#linkTo').value;
1977
linkText = container.querySelector('#visibleOutput').value;
1978
WayBack = container.querySelector('#WayBack').checked;
1979
WayBackAsMobile = container.querySelector('#WayBackMobi').checked;
1980
archiveIs = container.querySelector('#archiveIs').checked;
1981
}
1982
});
1983
win.button("박제/삽입", function () {
1984
var waitwin = TooSimplePopup();
1985
waitwin.title('박제중....');
1986
refresh();
1987
if (linkTo.indexOf('http://') != 0 && linkTo.indexOf('https://') != 0) {
1988
alert('http:// 또는 https://로 시작하는 외부링크가 아닙니다!');
1989
}
1990
waitwin.content(function (container) {
1991
container.innerHTML = '박제중입니다....'
1992
});
1993
1994
function finishLinking() {
1995
var link = '[[' + linkTo + '|링크]]';
1996
if (archiveLinks.length != 0) {
1997
link += '(';
1998
for (var i = 0; i < archiveLinks.length; i++) {
1999
link += '[[' + archiveLinks[i] + '|아카이브' + (i + 1) + ']]';
2000
if (i != archiveLinks.length - 1) link += ',';
2001
}
2002
link += ')';
2003
}
2004
TextProc.selectionText(link + TextProc.selectionText());
2005
waitwin.close();
2006
win.close();
2007
}
2008
2009
function archiveOne() {
2010
var archiveType;
2011
if (WayBack) {
2012
archiveType = 'wb';
2013
WayBack = false;
2014
} else if (archiveIs) {
2015
archiveType = 'ai';
2016
archiveIs = false;
2017
} else {
2018
finishLinking();
2019
return;
2020
}
2021
var r = {};
2022
if (archiveType == 'wb') {
2023
// 'http://web.archive.org/save/'
2024
// 1 -> Mobile Agent
2025
r.method = "GET";
2026
r.url = "http://web.archive.org/save/" + linkTo;
2027
if (WayBackAsMobile) {
2028
r.headers = {};
2029
r.headers["User-Agent"] = "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16";
2030
}
2031
r.onload = function (res) {
2032
if (res.status == 403) {
2033
alert('오류가 발생했습니다. 크롤링이 금지된 사이트일 수도 있습니다.');
2034
setTimeout(archiveOne, 50);
2035
return;
2036
} else if (res.status != 200) {
2037
alert('알 수 없는 오류가 발생했습니다.');
2038
setTimeout(archiveOne, 50);
2039
return;
2040
}
2041
var matches = /var redirUrl = \"(.+?)\";/.exec(res.responseText);
2042
if (matches == null) {
2043
alert('WayBack Machine 아카이브 주소를 얻는 데 실패했습니다.');
2044
setTimeout(archiveOne, 50);
2045
return;
2046
}
2047
var archiveUrl = 'http://web.archive.org' + matches[1];
2048
archiveLinks.push(archiveUrl);
2049
setTimeout(archiveOne, 50);
2050
};
2051
} else if (archiveType == 'ai') {
2052
r.method = "GET";
2053
r.url = "https://archive.is";
2054
r.onload = function (res) {
2055
// get submitid
2056
var parser = new DOMParser();
2057
var indexDoc = parser.parseFromString(res.responseText, "text/html");
2058
var token = indexDoc.querySelector("input[name=submitid][value]");
2059
if (token) {
2060
token = token.value;
2061
2062
// archive form
2063
// 'http://archive.is/submit/'
2064
var r2 = {};
2065
r2.method = "POST";
2066
r2.url = "https://archive.is/submit/";
2067
r2.headers = {};
2068
r2.headers["Content-Type"] = "application/x-www-form-urlencoded";
2069
r2.data = "anyway=1&url=" + encodeURIComponent(linkTo) + "&submitid=" + encodeURIComponent(token);
2070
r2.onload = function (res) {
2071
var matches = /document\.location\.replace\("(.+?)"\)/.exec(res.responseText);
2072
if (matches == null) matches = /<meta property="og:url" content="(.+?)"/.exec(res.responseText);
2073
if (matches == null) {
2074
alert('archive.is 아카이브 주소를 얻는 데 실패했습니다.');
2075
setTimeout(archiveOne, 50);
2076
return;
2077
}
2078
var archiveUrl = matches[1];
2079
archiveLinks.push(archiveUrl);
2080
setTimeout(archiveOne, 50);
2081
}
2082
GM.xmlHttpRequest(r2);
2083
} else {
2084
alert('archive.is 아카이브 중 토큰을 얻는 데 실패했습니다.')
2085
}
2086
}
2087
}
2088
GM.xmlHttpRequest(r);
2089
}
2090
archiveOne();
2091
});
2092
win.button("닫기", win.close);
2093
});
2094
if (ENV.IsEditing) {
2095
// Manager Class
2096
var tempsaveManager = new function () {
2097
var ht = this;
2098
this.getTitles = function () {
2099
var r = [];
2100
for (var i in SET.tempsaves) {
2101
r.push(i);
2102
}
2103
return r;
2104
}
2105
this.getByTitle = async function (docTitle) {
2106
await SET.load();
2107
if (nOu(SET.tempsaves[docTitle])) {
2108
SET.tempsaves[docTitle] = [];
2109
await SET.save();
2110
}
2111
return SET.tempsaves[docTitle]; // {section, text, timestamp}
2112
};
2113
this.getByTitleAndSectionNo = async function (docTitle, sectno) {
2114
await SET.load();
2115
var b = ht.getByTitle(docTitle);
2116
var a = [];
2117
for (var i = 0; i < b.length; i++) {
2118
if (b[i].section == sectno)
2119
a.push(b[i]);
2120
}
2121
return a;
2122
}
2123
this.save = async function (docTitle, sectno, timestamp, text, wikihost) {
2124
if (typeof wikihost === 'undefined')
2125
var wikihost = location.host;
2126
await SET.load();
2127
if (nOu(SET.tempsaves[docTitle])) {
2128
SET.tempsaves[docTitle] = [];
2129
await SET.save();
2130
}
2131
SET.tempsaves[docTitle].push({
2132
section: sectno,
2133
timestamp: timestamp,
2134
text: text,
2135
host: wikihost
2136
});
2137
await SET.save();
2138
}
2139
this.delete = async function (docTitle, sectno, timestamp) {
2140
await SET.load();
2141
if (nOu(SET.tempsaves[docTitle])) return;
2142
var newArray = [];
2143
for (var i = 0; i < SET.tempsaves[docTitle].length; i++) {
2144
var keepThis = true;
2145
var now = SET.tempsaves[docTitle][i];
2146
switch (arguments.length) {
2147
case 1:
2148
keepThis = false;
2149
break;
2150
case 2:
2151
if (now.section == sectno) keepThis = false;
2152
break;
2153
case 3:
2154
if (now.section == sectno && now.timestamp == timestamp) keepThis = false;
2155
break;
2156
}
2157
if (keepThis) {
2158
newArray.push(SET.tempsaves[docTitle][i]);
2159
}
2160
}
2161
SET.tempsaves[docTitle] = newArray;
2162
await SET.save();
2163
}
2164
}
2165
// Tempsave Menu
2166
var tempsaveDropdown = Designer.dropdown('<span class="ion-ios-pricetags-outline fa fa-save"></span>').hoverMessage('임시저장');
2167
tempsaveDropdown.button('<span class="ion-ios-pricetag-outline fa fa-save"></span>', '임시저장').click(async function () {
2168
await tempsaveManager.save(ENV.docTitle, ENV.section, Date.now(), txtarea.value);
2169
});
2170
tempsaveDropdown.button('<span class="ion-filing fa fa-folder-open-o"></span>', '임시저장 불러오기').click(async function () {
2171
// title(text), content(callback), foot(callback), button(text,onclick), close
2172
var win = TooSimplePopup();
2173
win.title('임시저장 불러오기')
2174
var tempsaveList = await tempsaveManager.getByTitle(ENV.docTitle);
2175
win.content(function (el) {
2176
el.innerHTML = '<p>현재 편집중인 문단인 경우 문단 번호가 <strong>굵게</strong> 표시됩니다.<br>문단 번호가 -2인 경우는 문단 번호가 감지되지 않은 경우입니다.</p>';
2177
var divWithscrollbars = document.createElement("div");
2178
divWithscrollbars.style.height = '300px';
2179
divWithscrollbars.style.overflow = 'auto';
2180
var table = document.createElement("table");
2181
var headrow = document.createElement("tr");
2182
headrow.innerHTML = '<th>문단 번호</th><th>저장된 날짜와 시간</th><th>불러오기</th>';
2183
table.appendChild(headrow);
2184
for (var i = 0; i < tempsaveList.length; i++) {
2185
var now = tempsaveList[i];
2186
var tr = document.createElement("tr");
2187
tr.innerHTML = '<td>' + (now.section == ENV.section ? '<strong>' : '') + now.section + (now.section == ENV.section ? '</strong>' : '') + '</td><td>' + formatDateTime(now.timestamp) + '</td>'
2188
var td = document.createElement("td");
2189
var btn = document.createElement("button");
2190
btn.setAttribute("type", "button");
2191
btn.innerHTML = "불러오기";
2192
btn.dataset.json = JSON.stringify(now);
2193
btn.addEventListener('click', function (evt) {
2194
var now = JSON.parse(evt.target.dataset.json);
2195
txtarea.value = now.text;
2196
});
2197
td.appendChild(btn);
2198
tr.appendChild(td);
2199
table.appendChild(tr);
2200
}
2201
divWithscrollbars.appendChild(table);
2202
el.appendChild(divWithscrollbars);
2203
});
2204
win.button('닫기', win.close);
2205
});
2206
tempsaveDropdown.button('<span class="ion-trash-a fa fa-trash" style="color:red;"></span>', '이 문서의 모든 임시저장 삭제').click(async function () {
2207
await tempsaveManager.delete(ENV.docTitle);
2208
});
2209
tempsaveDropdown.button('<span class="ion-trash-a fa fa-trash" style="color:orangered;"></span>', '이 문서의 이 문단의 모든 임시저장 삭제').click(async function () {
2210
await tempsaveManager.delete(ENV.docTitle, ENV.section);
2211
});
2212
tempsaveDropdown.button('<span class="ion-trash-a fa fa-trash" style="color:orange;"></span>', '특정 임시저장만 삭제').click(async function () {
2213
// title(text), content(callback), foot(callback), button(text,onclick), close
2214
var win = TooSimplePopup();
2215
var tempsaveList = await tempsaveManager.getByTitle(ENV.docTitle);
2216
win.title('임시저장 삭제');
2217
win.content(function (el) {
2218
el.innerHTML = '<p>현재 편집중인 문단인 경우 문단 번호가 <strong>굵게</strong> 표시됩니다.<br>문단 번호가 -2인 경우는 문단 번호가 감지되지 않은 경우입니다.</p>';
2219
var divWithscrollbars = document.createElement("div");
2220
divWithscrollbars.style.height = '300px';
2221
divWithscrollbars.style.overflow = 'auto';
2222
var table = document.createElement("table");
2223
var headrow = document.createElement("tr");
2224
headrow.innerHTML = '<th>문단 번호</th><th>저장된 날짜와 시간</th><th>삭제 버튼</th>';
2225
table.appendChild(headrow);
2226
for (var i = 0; i < tempsaveList.length; i++) {
2227
var now = tempsaveList[i];
2228
var tr = document.createElement("tr");
2229
tr.innerHTML = '<td>' + (now.section == ENV.section ? '<strong>' : '') + now.section + (now.section == ENV.section ? '</strong>' : '') + '</td><td>' + formatDateTime(now.timestamp) + '</td>'
2230
var td = document.createElement("td");
2231
var btn = document.createElement("button");
2232
btn.setAttribute("type", "button");
2233
btn.innerHTML = "삭제하기";
2234
btn.dataset.json = JSON.stringify(now);
2235
btn.addEventListener('click', async function (evt) {
2236
var now = JSON.parse(evt.target.dataset.json);
2237
await tempsaveManager.delete(ENV.docTitle, now.section, now.timestamp);
2238
win.close();
2239
});
2240
td.appendChild(btn);
2241
tr.appendChild(td);
2242
table.appendChild(tr);
2243
}
2244
divWithscrollbars.appendChild(table);
2245
el.appendChild(divWithscrollbars);
2246
});
2247
win.button('닫기', win.close);
2248
});
2249
if (SET.autoTempsaveSpan > 0)
2250
setInterval(async function () {
2251
await tempsaveManager.save(ENV.docTitle, ENV.section, Date.now(), txtarea.value);
2252
}, SET.autoTempsaveSpan);
2253
}
2254
// Template Insert Feature
2255
var templatesDropdown = Designer.dropdown('<span class="ion-ios-copy-outline fa fa-file"></span>').hoverMessage('템플릿/틀 삽입과 최근에 사용/삽입한 템플릿/틀 기록');
2256
var refreshTemplatesDropdown = async function () {
2257
await SET.load();
2258
templatesDropdown.clear();
2259
var rutl = SET.recentlyUsedTemplates.length;
2260
2261
function InsertTemplateClosure(na) {
2262
return async function () {
2263
namuapi.raw(na, async function (templateContent) {
2264
await SET.load();
2265
if (SET.recentlyUsedTemplates.indexOf(na) == -1) SET.recentlyUsedTemplates.push(na);
2266
await SET.save();
2267
if (na.indexOf('틀:') == 0)
2268
TextProc.selectionText(TextProc.selectionText() + '[include(' + na + ')]');
2269
else
2270
txtarea.value = templateContent;
2271
setTimeout(refreshTemplatesDropdown, 300);
2272
}, function () {
2273
alert('존재하지 않는 템플릿/틀입니다.');
2274
return;
2275
});
2276
};
2277
}
2278
for (var i = 0; i < (rutl < 9 ? rutl : 9); i++) {
2279
templatesDropdown.button('<span class="ion-ios-paper-outline fa fa-file"></span>', SET.recentlyUsedTemplates[i]).click(InsertTemplateClosure(SET.recentlyUsedTemplates[i]));
2280
}
2281
templatesDropdown.button('<span class="ion-close-round fa fa-times"></span>', '기록 삭제').click(async function () {
2282
await SET.load();
2283
SET.recentlyUsedTemplates = [];
2284
await SET.save();
2285
setTimeout(refreshTemplatesDropdown, 300);
2286
});
2287
templatesDropdown.button('<span class="ion-plus-round fa fa-plus"></span>', '템플릿/틀 삽입').click(function () {
2288
var templateName = prompt('템플릿/틀 이름을 입력하세요.');
2289
if (!/^(?:템플릿|Template|틀):.+/.test(templateName) && !confirm('올바른 템플릿/틀 이름이 아닌 것 같습니다. 계속할까요?')) return;
2290
InsertTemplateClosure(templateName)();
2291
setTimeout(refreshTemplatesDropdown, 300);
2292
});
2293
};
2294
setTimeout(refreshTemplatesDropdown, 500);
2295
2296
// set Size
2297
if (ENV.Discussing)
2298
rootDiv.style.height = '170px';
2299
else
2300
rootDiv.style.height = '600px';
2301
2302
// Add Keyboard Shortcut
2303
function overrideBrowserDefaultShortcutKey(evt) {
2304
var overrideShortcutKey = true;
2305
if (evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
2306
switch (evt.keyCode) { // Ctrl
2307
case 66: // B
2308
case 98:
2309
case 73: // I
2310
case 105:
2311
case 68: // D
2312
case 100:
2313
case 85: // U
2314
case 117:
2315
case 72: // H
2316
case 104:
2317
case 219:
2318
case 123:
2319
case 91: // [
2320
case 221:
2321
case 125:
2322
case 93: // ]
2323
case 83: // S
2324
case 115:
2325
break;
2326
default:
2327
overrideShortcutKey = false;
2328
break;
2329
}
2330
} else if (evt.ctrlKey && evt.altKey && !evt.shiftKey) {
2331
switch (evt.keyCode) { // Ctrl + Alt
2332
case 73: // I
2333
case 105:
2334
default:
2335
overrideShortcutKey = false;
2336
break;
2337
}
2338
} else {
2339
overrideShortcutKey = false;
2340
}
2341
if (overrideShortcutKey) {
2342
evt.preventDefault();
2343
evt.stopPropagation();
2344
}
2345
}
2346
txtarea.addEventListener('keydown', overrideBrowserDefaultShortcutKey);
2347
txtarea.addEventListener('keypress', overrideBrowserDefaultShortcutKey);
2348
txtarea.addEventListener('keyup', function (evt) {
2349
var overrideShortcutKey = true;
2350
if (evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
2351
switch (evt.keyCode) { // Ctrl
2352
case 66: // B
2353
case 98:
2354
TextProc.ToggleWrapSelection("'''");
2355
break;
2356
case 73: // I
2357
case 105:
2358
TextProc.ToggleWrapSelection("''");
2359
break;
2360
case 68: // D
2361
case 100:
2362
TextProc.ToggleWrapSelection("--");
2363
break;
2364
case 85: // U
2365
case 117:
2366
TextProc.ToggleWrapSelection("__");
2367
break;
2368
case 72:
2369
case 104:
2370
NewHeadingMacro();
2371
break;
2372
case 219:
2373
case 123:
2374
case 91: // [
2375
FontSizeChanger(false);
2376
break;
2377
case 221:
2378
case 125:
2379
case 93: // ]
2380
FontSizeChanger(true);
2381
break;
2382
case 83: // S
2383
case 115:
2384
tempsaveManager.save(ENV.docTitle, ENV.section, Date.now(), txtarea.value);
2385
break;
2386
default:
2387
overrideShortcutKey = false;
2388
break;
2389
}
2390
} else if (evt.ctrlKey && evt.altKey && !evt.shiftKey) {
2391
switch (evt.keyCode) { // Ctrl + Alt
2392
case 73: // I
2393
case 105:
2394
namuUpload();
2395
break;
2396
default:
2397
overrideShortcutKey = false;
2398
break;
2399
}
2400
} else {
2401
overrideShortcutKey = false;
2402
}
2403
if (overrideShortcutKey) {
2404
evt.preventDefault();
2405
evt.stopPropagation();
2406
}
2407
});
2408
2409
// Support drag-drop file upload
2410
// from https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
2411
// from https://stackoverflow.com/questions/7237436/how-to-listen-for-drag-and-drop-plain-text-in-textarea-with-jquery
2412
txtarea.addEventListener('dragover', function (evt) {
2413
evt.preventDefault();
2414
});
2415
txtarea.addEventListener('dragend', function (evt) {
2416
evt.preventDefault();
2417
});
2418
txtarea.addEventListener('dragenter', function (evt) {
2419
evt.preventDefault();
2420
});
2421
txtarea.addEventListener('drop', function (evt) {
2422
evt.preventDefault();
2423
var dt = evt.dataTransfer;
2424
var files = [];
2425
if (dt.items) {
2426
for (var i = 0; i < dt.items.length; i++)
2427
if (dt.items[i].kind == "file") {
2428
var f = dt.items[i].getAsFile();
2429
if (f.type.indexOf('image/') == 0)
2430
files.push(f);
2431
}
2432
} else {
2433
for (var i = 0; i < dt.files.length; i++) {
2434
var f = dt.files[i];
2435
if (f.type.indexOf('image/') == 0)
2436
files.push(f);
2437
}
2438
}
2439
if (files.length > 0) {
2440
namuUpload(files, function () {});
2441
}
2442
})
2443
2444
// Support image upload by pasting
2445
txtarea.addEventListener('paste', function (evt) {
2446
var items = (evt.clipboardData || evt.originalEvent.clipboardData).items;
2447
var files = [];
2448
for (index in items) {
2449
var item = items[index];
2450
if (item.kind === 'file') {
2451
var file = item.getAsFile();
2452
if (file.type.indexOf('image/') == 0)
2453
files.push(file);
2454
}
2455
}
2456
if (files.length > 0) {
2457
namuUpload(files, function () {});
2458
}
2459
})
2460
2461
// Add NamuFix Div
2462
var oldTextarea = document.querySelector("textarea");
2463
var wText = oldTextarea.value;
2464
oldTextarea.parentNode.insertBefore(rootDiv, oldTextarea);
2465
oldTextarea.parentNode.removeChild(oldTextarea);
2466
txtarea.value = wText;
2467
2468
var srwPattern = /\?redirectTo=([^\&]+)/;
2469
if (srwPattern.test(location.search)) {
2470
if ((txtarea.value.trim().search(/^#redirect .+/) == 0 || txtarea.value.trim().length == 0) || confirm('빈 문서가 아닌것 같습니다만 그래도 계속?')) {
2471
txtarea.value = '#redirect ' + decodeURIComponent(srwPattern.exec(location.search)[1]);
2472
if (document.querySelectorAll('iframe[title="CAPTCHA 위젯"]').length == 0 && document.querySelector('.alert.alert-danger') !== null) {
2473
if (document.querySelector("input#logInput")) document.querySelector("input#logInput").value = "NamuFix를 이용하여 자동 리다이렉트 처리됨.";
2474
document.querySelector('#editBtn').click();
2475
}
2476
}
2477
}
2478
}
2479
} else if (ENV.IsDocument) {
2480
if (ENV.docTitle.trim().indexOf('기여:') == 0) {
2481
var target = ENV.docTitle.trim().substring(3).trim();
2482
if (validateIP(target)) {
2483
location.assign('/contribution/ip/' + target + '/document');
2484
} else {
2485
location.assign('/contribution/author/' + target + '/document');
2486
}
2487
}
2488
2489
2490
2491
addArticleButton('삭제', (evt) => {
2492
evt.preventDefault();
2493
2494
location.href = 'https://namu.wiki/delete/' + ENV.docTitle;
2495
})
2496
addArticleButton('이동', (evt) => {
2497
evt.preventDefault();
2498
2499
location.href = 'https://namu.wiki/move/' + ENV.docTitle;
2500
})
2501
addArticleButton('롤백', function () {
2502
var revNo = prompt('리버전 번호를 입력하세요.').trim();
2503
if (revNo.indexOf('r') == 0) revNo = revNo.substring(1);
2504
if (/[^0-9]/.test(revNo)) {
2505
alert('올바른 입력이 아닙니다! r1 혹은 1과 같이 입력해주세요.');
2506
return;
2507
}
2508
location.href = "/revert/" + encodeURIComponent(ENV.docTitle) + "?rev=" + revNo;
2509
})
2510
addArticleButton('RAW', (evt) => {
2511
evt.preventDefault();
2512
2513
location.href = 'https://namu.wiki/raw/' + ENV.docTitle;
2514
})
2515
addArticleButton('Blame', (evt) => {
2516
evt.preventDefault();
2517
2518
location.href = 'https://namu.wiki/blame/' + ENV.docTitle;
2519
})
2520
2521
// 리다이렉트 버튼 추가
2522
addArticleButton('출발지', function (evt) {
2523
evt.preventDefault();
2524
2525
var redirectFrom = prompt('출발지 문서명을 입력하세요.');
2526
if (redirectFrom != null && redirectFrom.trim().length != 0)
2527
location.href = 'https://' + location.host + '/edit/' + redirectFrom + '?redirectTo=' + ENV.docTitle;
2528
});
2529
2530
2531
if (SET.addSnsShareButton && ENV.IsDocument) {
2532
addArticleButton('<span class="icon ion-social-facebook fa fab fa-facebook-square"></span>', (evt) => {
2533
evt.preventDefault();
2534
2535
GM.openInTab(`http://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(location.href)}`)
2536
})
2537
addArticleButton('<span class="icon ion-social-twitter fa fab fa-twitter"></span>', (evt) => {
2538
evt.preventDefault();
2539
2540
GM.openInTab(`http://twitter.com/intent/tweet?url=${encodeURIComponent(location.href)}&text=${ENV.docTitle}`)
2541
})
2542
}
2543
// 리다이렉트로 왔을 시 그 라디이렉트 문서 편집/삭제 링크 추가
2544
if (document.querySelector('article .alert.alert-info, .Liberty .wiki-article .alert.alert-info') && document.querySelector('article .alert.alert-info, .Liberty .wiki-article .alert.alert-info').innerHTML.indexOf('에서 넘어옴') != -1) {
2545
var redirectAlert = document.querySelector('article .alert.alert-info, .Liberty .wiki-article .alert.alert-info');
2546
var origDocuName = decodeURIComponent(/\/w\/(.+?)\?noredirect=1/.exec(redirectAlert.querySelector('a.document').href)[1]);
2547
var editUrl = '/edit/' + origDocuName;
2548
var deleteUrl = '/delete/' + origDocuName;
2549
2550
redirectAlert.innerHTML = '<a href="/w/' + encodeURIComponent(origDocuName) + '?noredirect=1" class="document" title="' + encodeHTMLComponent(origDocuName) + '">' + encodeHTMLComponent(origDocuName) + '</a>' +
2551
'에서 여기로 넘어왔습니다. 당신은 ' + encodeHTMLComponent(origDocuName) + ' 문서를 <a href="' + editUrl + '">수정</a>하거나 <a href="' + deleteUrl + '">삭제</a>할 수 있습니다.';
2552
}
2553
// 상위 문서로의 링크
2554
if (ENV.docTitle.indexOf('/') != -1) {
2555
function spSplit(a, b) {
2556
var splitted = a.split(b);
2557
var result = [];
2558
for (var i = 1; i <= splitted.length; i++) {
2559
var now = '';
2560
for (var ii = 0; ii < i; ii++) {
2561
var append = splitted[ii];
2562
if (ii != i - 1) append += '/';
2563
now += append;
2564
}
2565
result.push(now);
2566
}
2567
return result;
2568
}
2569
var higherDocsWE = {};
2570
var higherDocs = spSplit(ENV.docTitle, '/');
2571
for (var i = 0; i < higherDocs.length; i++) {
2572
higherDocsWE[i] = null;
2573
}
2574
var codwe = 0;
2575
var codwnf = 0;
2576
for (var i = 0; i < higherDocs.length; i++) {
2577
namuapi.raw(higherDocs[i], function (r, t) {
2578
higherDocsWE[t] = true;
2579
codwe++;
2580
}, function (t) {
2581
higherDocsWE[t] = false;
2582
codwnf++;
2583
});
2584
if (i == higherDocs.length - 1) {
2585
var hdinid = setInterval(function () {
2586
if (codwe + codwnf != higherDocs.length) return;
2587
var docTitleTag = document.querySelector('h1.title, body.Liberty .liberty-content .liberty-content-header h1.title');
2588
var hdsPT = document.createElement("p");
2589
var sstl = 0;
2590
for (var i = 0; i < higherDocs.length; i++) {
2591
if (!higherDocsWE[higherDocs[i]]) continue;
2592
var higherDoc = higherDocs[i];
2593
var a = document.createElement("a");
2594
a.setAttribute("href", '/w/' + encodeURIComponent(higherDoc));
2595
a.setAttribute("title", higherDoc);
2596
a.className = "wiki-link-internal";
2597
if (i != 0 && higherDoc.substring(sstl).indexOf('/') == 0)
2598
a.innerHTML = higherDoc.substring(sstl + 1);
2599
else
2600
a.innerHTML = higherDoc.substring(sstl);
2601
sstl = higherDoc.length;
2602
hdsPT.appendChild(a);
2603
if (i != higherDocs.length - 1) hdsPT.appendChild(document.createTextNode(" > "));
2604
}
2605
docTitleTag.style.marginBottom = '0px';
2606
hdsPT.style.marginBottom = '25px';
2607
docTitleTag.parentNode.insertBefore(hdsPT, docTitleTag.nextSibling);
2608
clearInterval(hdinid);
2609
}, 200);
2610
}
2611
}
2612
}
2613
2614
// 문단 접기 이탤릭 표시
2615
GM_addStyle('.namufix-folded-heading {color: darkgray; opacity: 0.5;}');
2616
var wikiHeadings = document.querySelectorAll('.wiki-heading')
2617
for (var i = 0; i < wikiHeadings.length; i++) wikiHeadings[i].addEventListener('click', function (evt) {
2618
if (evt.target.tagName === 'A') return;
2619
if (evt.target.nextSibling.style.display === 'none') {
2620
evt.target.className += ' namufix-folded-heading';
2621
} else {
2622
evt.target.className = evt.target.className.replace('namufix-folded-heading', '');
2623
}
2624
})
2625
2626
// 차단 링크 링크화
2627
if (ENV.docTitle.startsWith('사용자:')) {
2628
let banbox = document.querySelector('.wiki-content [onmouseover][onmouseout]')
2629
if (banbox.querySelector('span:first-child').textContent.includes('이 사용자는 차단된 사용자입니다.')) {
2630
let reason = banbox.lastChild.textContent,
2631
urlPattern = /((http[s]?|ftp):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+[^#?\s]+)(.*)?(#[\w\-]+)?$/mg,
2632
urlMatch = urlPattern.exec(reason),
2633
linkIndex = 1,
2634
firstLink = null;
2635
while(urlMatch) {
2636
let link = document.createElement("a");
2637
link.href = urlMatch[0];
2638
link.textContent = `차단 링크 ${linkIndex++}`;
2639
banbox.appendChild(link);
2640
if(!firstLink) firstLink = link;
2641
urlMatch = urlPattern.exec(reason);
2642
}
2643
if (firstLink) {
2644
banbox.insertBefore(document.createElement("br"), firstLink);
2645
banbox.insertBefore(document.createElement("br"), firstLink);
2646
}
2647
}
2648
}
2649
}
2650
2651
2652
2653
if (ENV.Discussing) {
2654
// 보여지지 않은 쓰레드도 불러오기
2655
let unvisibleResesAllLoaded = false;
2656
if (SET.loadUnvisibleReses) {
2657
function doLoadUnvisibleReses() {
2658
var scriptTag = document.createElement("script");
2659
scriptTag.innerHTML = 'function nfformattime(){$("time[data-nf-format-this]").each(function(){var format = $(this).attr("data-format");var time = $(this).attr("datetime");$(this).text(formatDate(new Date(time), format));});}';
2660
document.body.appendChild(scriptTag);
2661
var allUnlockedReses = document.querySelectorAll('#res-container div.res-loading[data-locked="false"]');
2662
for (var i = 0; i < allUnlockedReses.length; i++) {
2663
allUnlockedReses[i].setAttribute('data-locked', 'true');
2664
}
2665
var reqId = parseInt(allUnlockedReses[0].dataset.id),
2666
lastReqId = parseInt(allUnlockedReses[allUnlockedReses.length - 1].dataset.id);
2667
console.log('[NamuFix] 보이지 않은 레스 불러오기를 시작합니다. 범위 : ' + reqId + ' 에서 ' + lastReqId);
2668
for (console.log('[NamuFix] loadUnvisibleReses 루프 시작!'); reqId <= lastReqId; reqId += 30) {
2669
console.log('[NamuFix] 레스 요청중, 시작 번호는 ' + reqId);
2670
namuapi.theseedRequest({
2671
method: "GET",
2672
url: "https://" + location.host + "/thread/" + ENV.topicNo + "/" + reqId,
2673
onload: function (res) {
2674
console.log('[NamuFix] 레스 응답 받음, 시작 번호는 ' + reqId);
2675
var parser = new DOMParser();
2676
var doc = parser.parseFromString(res.responseText, "text/html");
2677
var timeTags = doc.querySelectorAll('time');
2678
var resTags = doc.querySelectorAll('.res-wrapper');
2679
for (var i = 0; i < timeTags.length; i++)
2680
timeTags[i].dataset.nfFormatThis = "true";
2681
for (var i = 0; i < resTags.length; i++) {
2682
var resTag = resTags[i];
2683
var targetTag = document.querySelector('#res-container div.res-loading[data-id="' + resTag.dataset.id + '"]');
2684
if (targetTag == null) continue;
2685
targetTag.parentNode.insertBefore(resTag, targetTag.nextSibling);
2686
targetTag.parentNode.removeChild(targetTag);
2687
}
2688
var scriptTagId = 'nf-temp-s' + Date.now() + reqId;
2689
var scriptTag = document.createElement('script');
2690
scriptTag.id = scriptTagId;
2691
scriptTag.innerHTML = 'nfformattime(); var thisTag = document.querySelector("#' + scriptTagId + '"); thisTag.parentNode.removeChild(thisTag);';
2692
document.body.appendChild(scriptTag);
2693
setTimeout(() => {
2694
if (document.querySelectorAll('#res-container div.res-loading[data-locked="false"]')) {
2695
if (!unvisibleResesAllLoaded) {
2696
unvisibleResesAllLoaded = true;
2697
console.log("[NamuFix] 모든 보이지 않은 쓰레를 불러옴!");
2698
}
2699
}
2700
}, 100);
2701
}
2702
});
2703
}
2704
}
2705
setTimeout(doLoadUnvisibleReses, 600);
2706
}
2707
2708
// 아이덴티콘 설정들과 변수들
2709
var isIcon = SET.discussIdenti == 'icon';
2710
var isThreadicLike = SET.discussIdenti == 'headBg';
2711
var isIdenticon = SET.discussIdenti == 'identicon';
2712
var colorDictionary = {},
2713
identiconDictionary = {};
2714
2715
// TO-DO : Rewrite Loop codes
2716
// #[0-9]+ 엥커 미리보기
2717
function mouseoverPreview() {
2718
var anchors = document.querySelectorAll('.res .r-body .wiki-self-link:not([data-nf-title-processed])');
2719
for (var i = 0; i < anchors.length; i++) {
2720
var anchor = anchors[i];
2721
if (!/#[0-9]+$/.test(anchor.href)) {
2722
continue;
2723
}
2724
var anchorDirection = document.querySelector('.r-head .num a[id=\'' + /#([0-9]+)$/.exec(anchor.href)[1] + '\']');
2725
2726
anchor.dataset.target = (anchorDirection) ? anchorDirection.id : "";
2727
anchor.addEventListener('mouseenter', function (evt) {
2728
var anchorDirection = document.getElementById(evt.target.dataset.target);
2729
var obj = {};
2730
if (anchorDirection == null) {
2731
obj = {
2732
talker: "?????????",
2733
message: "존재하지 않는 메세지입니다.",
2734
isFirstAuthor: false,
2735
notExists: true
2736
}
2737
} else if (anchorDirection.parentNode.parentNode.parentNode.parentNode.className.indexOf('res-loading') != -1) {
2738
obj = {
2739
talker: "NOT LOADED YET",
2740
message: "아직 불러오지 않은 메세지입니다.",
2741
isFirstAuthor: false,
2742
notExists: true
2743
}
2744
} else {
2745
var anchorTarget = anchorDirection.parentNode.parentNode.parentNode;
2746
obj = {
2747
talker: anchorTarget.querySelector('.r-head > a').textContent,
2748
message: anchorTarget.querySelector('.r-body').innerHTML,
2749
isFirstAuthor: anchorTarget.querySelector('.r-head.first-author') !== null,
2750
notExists: false
2751
};
2752
}
2753
var headBackground = obj.notExists ? "red" : obj.isFirstAuthor ? "#a5df9f" : "#b3b3b3";
2754
var elem = document.createElement("div");
2755
elem.className = 'nfTopicMessage';
2756
elem.innerHTML = `<div style="font-size: 17px; font-family: sans-serif; background: ${headBackground}; padding: 7px 10px 7px 15px; font-weight: bold;">${obj.talker}</div><div style="padding: 15px; font-size: 11px; font-weight: normal;">${obj.message}</div>`;
2757
elem.style.position = 'absolute';
2758
elem.style.color = 'black';
2759
elem.style.borderRadius = '4px';
2760
elem.style.background = obj.notExists ? 'darkred' : '#d9d9d9';
2761
elem.style.zIndex = 3;
2762
evt.target.appendChild(elem);
2763
evt.target.title = '';
2764
});
2765
anchor.addEventListener('mouseleave', function (evt) {
2766
//var obj = JSON.parse(evt.target.dataset.targetMessage);
2767
if (evt.target.querySelector('.nfTopicMessage')) {
2768
var elemToRemove = evt.target.querySelector('.nfTopicMessage');
2769
elemToRemove.parentNode.removeChild(elemToRemove);
2770
}
2771
});
2772
2773
anchor.dataset.nfTitleProcessed = true;
2774
}
2775
}
2776
2777
function previewAsQuote(message) {
2778
var anchors = message.bodyElement.querySelectorAll('.wiki-self-link:not([data-nf-title-processed])');
2779
for (var i = 0; i < anchors.length; i++) {
2780
var anchor = anchors[i];
2781
if (!/#[0-9]+$/.test(anchor.href)) {
2782
continue;
2783
}
2784
var numbericId = /#([0-9]+)$/.exec(anchor.href)[1];
2785
var anchorDirection = document.querySelector('.r-head .num a[id=\'' + numbericId + '\']');
2786
if (anchorDirection == null) continue;
2787
var anchorTarget = deserializeResDom(anchorDirection.parentNode.parentNode.parentNode);
2788
var talker = anchorTarget.author.name,
2789
targetMessage = anchorTarget.bodyElement.innerHTML,
2790
talkedAt = anchorTarget.element.querySelector('.r-head > span.pull-right').textContent;
2791
var blockquoteId = uniqueID();
2792
var blockquoteElement = document.createElement("blockquote");
2793
blockquoteElement.className = "wiki-quote nf-anchor-preview";
2794
blockquoteElement.innerHTML = targetMessage;
2795
blockquoteElement.id = blockquoteId;
2796
blockquoteElement.innerHTML += `<div style="text-align: right; font-style: italic;">--#${numbericId}, ${talker}, ${talkedAt}</div>`;
2797
message.bodyElement.insertBefore(blockquoteElement, message.bodyElement.firstChild);
2798
2799
anchor.dataset.quoteId = blockquoteId;
2800
anchor.addEventListener('mouseenter', function (evt) {
2801
var quote = document.getElementById(evt.target.dataset.quoteId);
2802
quote.style.borderColor = '#CCC #CCC #CCC red !important';
2803
quote.style.boxShadow = '2px 2px 3px orange';
2804
});
2805
anchor.addEventListener('mouseleave', function (evt) {
2806
var quote = document.getElementById(evt.target.dataset.quoteId);
2807
quote.style.borderColor = '';
2808
quote.style.boxShadow = '';
2809
})
2810
}
2811
}
2812
var previewFunction;
2813
switch (SET.discussAnchorPreviewType) {
2814
case 1:
2815
previewFunction = mouseoverPreview;
2816
break;
2817
case 2:
2818
GM_addStyle('' +
2819
'blockquote.nf-anchor-preview{' +
2820
'border-color: #CCC #CCC #CCC #FF9900 !important;' +
2821
'background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABTklEQVRoge2ZvYrCQBSF71ulEVLIFIIgC5LOaQRhwcbCKSLbCYtFunmDBTuLEKwEKx9ASOsjWNlYni2WwK5ZV83PTMa9B053IedjJpCcS8RisZ5baaIxG/YhfA+eZ8i+QH84g05SlAofSd9c6Cv2ZVQMIuy0rIfP3OqEj0GsQ2E99KVFuL4fQjYgcN7yPoDjctyAsL97vDzehtjrwHrQaw70ngEYgAEYgAFsA4gR9OaAEwDghMNGYyRqmqkeoIfF7oxLnXcL9CqfqQVAIc49FgBiqMpn+ASe8h2wawawbQawbQaw7f8B4HwrQeR4L0REFKt2AwL/dFvFj9WL6sZ3iVELVazgnQcNaKeDebmKPU00poOu8f1AdzAtvx+oQ+HLX7V9watiWttIwncZINNrboniGAAR0eot+HYaDgJk+vqddBiAiOhj8u42AItlQZ8Z9UiwBSnJVAAAAABJRU5ErkJggg==") !important;' +
2822
'margin: 0.5em 0px !important;' +
2823
'}'
2824
);
2825
previewFunction = previewAsQuote;
2826
break;
2827
default:
2828
previewFunction = function () {};
2829
}
2830
2831
// 인용형식 앵커 미리보기안의 앵커 미리보기 삭제 옵션 설정시 CSS 추가
2832
if (SET.removeNFQuotesInAnchorPreview) {
2833
GM_addStyle("blockquote.nf-anchor-preview blockquote.nf-anchor-preview {display: none;}");
2834
}
2835
2836
// 아이덴티콘
2837
function identiconLoop(message) {
2838
if (/^\/discuss\/(.+?)/.test(location.pathname)) return;
2839
var colorHash = isThreadicLike ? new ColorHash({
2840
lightness: Number(SET.discussIdentiLightness),
2841
saturation: Number(SET.discussIdentiSaturation)
2842
}) : new ColorHash();
2843
if (isIdenticon && document.querySelector('#nf-identicon-css') == null) {
2844
var cssContent = 'div.nf-identicon { border: 1px solid #808080; margin: 10px; width: 64px; border: 1px black solid; background: white;} .res.hasNFIdenticon {margin-left: 88px; position: relative; top: -76px;}';
2845
var styleTag = document.createElement("style");
2846
styleTag.innerHTML = cssContent;
2847
styleTag.id = "nf-identicon-css";
2848
document.head.appendChild(styleTag);
2849
}
2850
if (isIcon && message.author.isFirst) return;
2851
let n = message.author.name;
2852
if (!message.author.isIP) {
2853
// 로그인
2854
n = '!ID!' + n;
2855
} else {
2856
// IP
2857
n = '!IP!' + n;
2858
}
2859
n = SHA1(n);
2860
2861
var nColor;
2862
if (typeof colorDictionary[n] === 'undefined') {
2863
nColor = colorHash.hex(n);
2864
colorDictionary[n] = nColor;
2865
} else {
2866
nColor = colorDictionary[n];
2867
}
2868
2869
if (isThreadicLike) {
2870
message.element.querySelector('.r-head').style.background = nColor;
2871
message.element.querySelector('.r-head').style.color = 'white';
2872
message.element.querySelector('.r-head > a').style.color = 'white';
2873
message.element.querySelector('.r-head .num a').style.color = 'white';
2874
} else if (isIcon) {
2875
var a = message.element.querySelector('.r-head > a');
2876
var span = document.createElement("span");
2877
span.style.background = nColor;
2878
span.style.color = nColor;
2879
span.style.marginLeft = '1em';
2880
span.innerHTML = '__';
2881
a.parentNode.insertBefore(span, a.nextSibling);
2882
} else if (isIdenticon) {
2883
message.element.className += " hasNFIdenticon";
2884
let identicon = document.createElement("div");
2885
identicon.className = "nf-identicon";
2886
identicon.style.position = 'relative';
2887
identicon.innerHTML = '<div class="identicon-pointer" style="width: 0px;height: 0px;position: absolute;border-top: 20px solid transparent;border-right: 15px solid #B0D3AD;left: 64px;border-bottom: 20px solid transparent;"></div><a><img style="width: 64px; height: 64px;"></img></a>';
2888
identicon.querySelector("img").dataset.hash = n;
2889
identicon.querySelector("a").dataset.hash = n;
2890
identicon.querySelector("a").href = "#NothingToLink";
2891
identicon.querySelector("a").addEventListener('click', async function (evt) {
2892
evt.preventDefault();
2893
2894
await SET.load();
2895
var h = evt.target.dataset.hash;
2896
if (typeof SET.customIdenticons[h] !== 'undefined') {
2897
// custom identicon exists
2898
if (confirm('이미 이미지가 설정되어 있습니다. 제거할까요?')) {
2899
delete SET.customIdenticons[h];
2900
await SET.save();
2901
alert('설정됐습니다. 새로고침시 적용됩니다');
2902
}
2903
} else {
2904
if (!confirm('이 아이디 또는 닉네임에 기존 아이덴티콘 대신 다른 이미지를 설정할 수 있습니다.\n설정할까요?')) return;
2905
// doesn't exists
2906
getFile(function (files, finish) {
2907
if (files.length < 0) {
2908
alert('선택된 파일이 없습니다.')
2909
if (isLastItem) finish();
2910
return;
2911
}
2912
if (files.length > 1) {
2913
alert('한 개의 파일만 선택해주세요.');
2914
finish();
2915
return;
2916
}
2917
var file = files[0];
2918
if (file) {
2919
var reader = new FileReader();
2920
reader.onload = async function (evt) {
2921
SET.customIdenticons[h] = reader.result;
2922
await SET.save();
2923
alert('설정됐습니다. 새로고침시 적용됩니다');
2924
finish();
2925
};
2926
reader.readAsDataURL(file);
2927
}
2928
});
2929
}
2930
});
2931
if (typeof identiconDictionary[n] === 'undefined' && typeof SET.customIdenticons[n] !== 'undefined')
2932
identiconDictionary[n] = SET.customIdenticons[n];
2933
if (typeof identiconDictionary[n] === 'undefined') {
2934
switch(SET.identiconLibrary) {
2935
case 'gravatar':
2936
identiconDictionary[n] = "https://secure.gravatar.com/avatar/" + n.substring(0, 32) + "?s=64&d=identicon"
2937
break;
2938
case 'gravatar-monster':
2939
identiconDictionary[n] = "https://secure.gravatar.com/avatar/" + n.substring(0, 32) + "?s=64&d=monsterid"
2940
break;
2941
case 'identicon':
2942
identiconDictionary[n] = "data:image/svg+xml;base64," + new Identicon(n, {size: 64, format: 'svg'}).toString();
2943
break;
2944
default:
2945
case 'jdenticon':
2946
identiconDictionary[n] = "data:image/svg+xml;base64," + btoa(jdenticon.toSvg(n, 64));
2947
break;
2948
}
2949
}
2950
let identiconImage = identiconDictionary[n];
2951
identicon.querySelector('img').src = identiconImage;
2952
message.element.parentNode.insertBefore(identicon, message.element);
2953
message.element.querySelector('.r-head').style.borderLeft = 'none';
2954
2955
identicon.querySelector('.identicon-pointer').style.borderRight = '15px solid ' + getComputedStyle(message.element.querySelector('.r-head')).backgroundColor;
2956
if (message.element.parentNode.dataset.id != 1) {
2957
message.element.parentNode.style.marginTop = '-76px';
2958
identicon.style.marginTop = '-66px';
2959
}
2960
}
2961
}
2962
2963
function checkIP(message) {
2964
if (!message.author.isIP)
2965
return;
2966
var span = document.createElement("span");
2967
span.style.color = "red";
2968
message.nfHeadspan.appendChild(span);
2969
span.innerHTML = "[(IP 확인중: IP정보 조회중)]";
2970
// get ip info
2971
getIpInfo(message.author.name, function (resObj) {
2972
if (resObj !== null) {
2973
var country = resObj.country;
2974
var countryName = korCountryNames[country.toUpperCase()] || country;
2975
var isp = resObj.org;
2976
getFlagIcon(country.toLowerCase(), async function (data) {
2977
let whoisResult = SET.checkWhoisNetTypeOnDiscuss ? await (new Promise((resolve, reject) => {
2978
getIpWhois(message.author.name, whoisRes => resolve(whoisRes));
2979
})) : null;
2980
let whoisNetType = false;
2981
if (whoisResult && whoisResult.success && !whoisResult.raw) {
2982
if (whoisResult.result.korean.ISP && whoisResult.result.korean.ISP.netinfo && whoisResult.result.korean.ISP.netinfo.netType)
2983
whoisNetType = whoisResult.result.korean.ISP.netinfo.netType
2984
else if (whoisResult.result.korean.user && whoisResult.result.korean.user.netinfo && whoisResult.result.korean.user.netinfo.netType)
2985
whoisNetType = whoisResult.result.korean.user.netinfo.netType
2986
}
2987
span.innerHTML = `[<img src="${data}" style="height: 0.9rem;" title="${countryName}"></img> ${isp}${await checkVPNGateIP(message.author.name) ? " (VPNGATE)" : ""}]${whoisNetType ? "<span style=\"color: darkred;\">[" + whoisNetType + "]</span>" : ""}<a href="#" class="get-whois">[WHOIS]</a>`;
2988
span.querySelector('a.get-whois').addEventListener('click', function (evt) {
2989
evt.preventDefault();
2990
whoisPopup(message.author.name);
2991
});
2992
});
2993
} else {
2994
span.innerHTML = "[IP조회실패]<a href=\"#\" class=\"get-whois\">[WHOIS]</a>"
2995
span.querySelector('a.get-whois').addEventListener('click', function (evt) {
2996
evt.preventDefault();
2997
whoisPopup(message.author.name);
2998
})
2999
}
3000
});
3001
}
3002
3003
function quickBlockLoop(i) {
3004
let blockAnchor = document.createElement("a");
3005
blockAnchor.href = "#";
3006
blockAnchor.textContent = "[차단]";
3007
blockAnchor.addEventListener('click', (evt) => {
3008
evt.preventDefault();
3009
quickBlockPopup({
3010
author: i.author,
3011
defaultDuration: SET.quickBlockDefaultDuration,
3012
defaultReason: SET.quickBlockReasonTemplate_discuss.replace(/\$\{host\}/g, location.host)
3013
.replace(/\$\{threadNo\}/g, ENV.topicNo)
3014
.replace(/\$\{messageNo\}/g, i.no)
3015
});
3016
});
3017
i.nfHeadspan.appendChild(blockAnchor);
3018
}
3019
3020
let emphasizeResStyle = document.createElement("style");
3021
let emphasizedHash = null;
3022
document.head.appendChild(emphasizeResStyle);
3023
3024
function emphasizeWhenMouseoverLoop(i) {
3025
let usernameHash = SHA1((i.author.isIP ? '!IP!' : '!ID!') + i.author.name);
3026
i.element.dataset.usernameHash = usernameHash;
3027
let linkAnchor = document.createElement("a");
3028
linkAnchor.href = "#";
3029
linkAnchor.className = "nf-emp-thread-link"
3030
linkAnchor.innerHTML = '[강조]' // ionicon에 압정 아이콘이 안 보인다. 이런데 쓰면 딱인데...
3031
linkAnchor.title = "이 사용자의 쓰레드를 강조합니다."
3032
linkAnchor.addEventListener("click", (evt) => {
3033
evt.preventDefault();
3034
if (emphasizedHash !== usernameHash) {
3035
emphasizedHash = usernameHash;
3036
emphasizeResStyle.innerHTML = `.res[data-username-hash="${usernameHash}"] .r-head {background: #8a8a8a !important} .res[data-username-hash="${usernameHash}"] .r-head.first-author {background: #8DAD8A !important} .res[data-username-hash="${usernameHash}"] .nf-emp-thread-link {color: red;} .res:not([data-username-hash="${usernameHash}"]) {filter: blur(1.5px);}`
3037
} else {
3038
emphasizedHash = null;
3039
emphasizeResStyle.innerHTML = "";
3040
}
3041
console.log(emphasizedHash);
3042
});
3043
i.nfRightHeadSpan.appendChild(linkAnchor);
3044
i.element.addEventListener('mouseover', () => {
3045
if(emphasizedHash === null) emphasizeResStyle.innerHTML = `.res[data-username-hash="${usernameHash}"] .r-head {background: #8a8a8a !important} .res[data-username-hash="${usernameHash}"] .r-head.first-author {background: #8DAD8A !important}`
3046
})
3047
i.element.addEventListener('mouseout', () => {
3048
if(emphasizedHash === null) emphasizeResStyle.innerHTML = ''
3049
});
3050
}
3051
3052
function deserializeResDom(resElement) {
3053
let userLink = resElement.querySelector('.r-head > a'),
3054
anchor = resElement.querySelector('.r-head .num > a');
3055
return {
3056
element: resElement,
3057
no: anchor.id,
3058
author: {
3059
name: resElement.querySelector('.r-head > a').textContent.trim(),
3060
isFirst: resElement.querySelector('.r-head').className.includes('first-author'),
3061
isIP: resElement.querySelector('.r-head > a').href.includes('/contribution/ip')
3062
},
3063
bodyElement: resElement.querySelector('.r-body'),
3064
get nfHeadspan() {
3065
let headspan = resElement.querySelector('.nf-headinfo')
3066
if (!headspan) {
3067
headspan = document.createElement("span");
3068
headspan.className = "nf-headinfo";
3069
userLink.parentNode.insertBefore(headspan, userLink.nextSibling);
3070
headspan.style.marginLeft = "1em";
3071
}
3072
delete this.nfHeadspan;
3073
return this.nfHeadspan = headspan;
3074
},
3075
get nfRightHeadSpan() {
3076
let rightHeadSpan = resElement.querySelector('.nf-headinfo-right')
3077
if (!rightHeadSpan) {
3078
rightHeadSpan = document.createElement("span");
3079
rightHeadSpan.className = "nf-headinfo-right"
3080
let time = resElement.querySelector('.r-head > .pull-right > time');
3081
time.parentNode.insertBefore(rightHeadSpan, time.nextSibling);
3082
rightHeadSpan.style.marginLeft = "0.25rem";
3083
}
3084
delete this.nfRightHeadSpan;
3085
return this.nfRightHeadSpan = rightHeadSpan;
3086
}
3087
}
3088
}
3089
3090
function discussLoop() {
3091
let handles = {
3092
"preview": previewFunction,
3093
"identicon": identiconLoop
3094
};
3095
if (SET.addQuickBlockLink) handles.quickBlock = quickBlockLoop
3096
if (SET.lookupIPonDiscuss) handles.checkIp = checkIP;
3097
if (SET.emphasizeResesWhenMouseover) handles.emphasizeReses = emphasizeWhenMouseoverLoop;
3098
let messages = document.querySelectorAll('.res-wrapper:not(.res-loading) > .res:not([nf-looped])');
3099
let tmpcnt = 0; //fordebug
3100
for (let i of messages)
3101
for (let j in handles) {
3102
let resObj = deserializeResDom(i);
3103
if (i.dataset["nfLoop_" + j] !== "yes") {
3104
i.dataset["nfLoop_" + j] = "yes";
3105
setTimeout(() => {
3106
handles[j](resObj);
3107
}, 0);
3108
}
3109
}
3110
if (SET.notifyForUnvisibleThreads) {
3111
Notification.requestPermission().then(() => {
3112
if (document.visibilityState === 'hidden') {
3113
if (SET.loadUnvisibleReses && !unvisibleResesAllLoaded)
3114
return;
3115
let n = new Notification(`토론 알림 : ${ENV.topicTitle}`, {
3116
body: '토론을 확인해주세요. 알람을 원하지 않으시면 NamuFix 설정에서 비활성화할 수 있습니다.',
3117
icon: `/favicon.ico`
3118
})
3119
}
3120
});
3121
}
3122
}
3123
discussLoop();
3124
var observer = new MutationObserver(discussLoop);
3125
observer.observe(document.querySelector("#res-container"), {
3126
childList: true
3127
});
3128
3129
} else if (ENV.IsUserContribsPage) {
3130
function insertBeforeTable(element) {
3131
var bread = document.querySelector("article > ol.breadcrumb.link-nav, body.Liberty .wiki-article ol.breadcrumb.link-nav");
3132
bread.parentNode.insertBefore(element, bread);
3133
}
3134
3135
function makeHeatTable(times) {
3136
try {
3137
// 가공
3138
var maps = {}; // { day: {0: int, 1: int}, .... }
3139
var maxValue = 0;
3140
for (var i = 0; i < 7; i++) {
3141
maps[i] = {};
3142
for (var ii = 0; ii < 24; ii++) {
3143
maps[i][ii] = 0;
3144
}
3145
}
3146
for (var i = 0; i < times.length; i++) {
3147
var ti = times[i];
3148
var v = ++maps[ti.getDay()][ti.getHours()];
3149
if (maxValue < v) maxValue = v;
3150
}
3151
3152
// 표 생성
3153
var table = document.createElement("table");
3154
var headTr = document.createElement("tr");
3155
headTr.innerHTML = '<th>요일</th>';
3156
table.appendChild(headTr);
3157
var dayNames = ['일', '월', '화', '수', '목', '금', '토'];
3158
for (var i = 0; i < 7; i++) {
3159
var tr = document.createElement("tr");
3160
tr.innerHTML += `<th>${dayNames[i]}</th>`;
3161
for (var ii = 0; ii < 24; ii++) {
3162
var td = document.createElement("td");
3163
td.innerHTML = '&nbsp;'
3164
td.style.background = `rgba(61,0,61,${maps[i][ii] / maxValue})`;
3165
if (i == 0) {
3166
function twoDigits(a) {
3167
var p = String(a);
3168
return p.length == 1 ? '0' + p : p;
3169
}
3170
headTr.innerHTML += `<th>${twoDigits(ii)}:00 ~ ${twoDigits(ii + 1)}:00</th>`;
3171
}
3172
tr.appendChild(td);
3173
}
3174
table.appendChild(tr);
3175
}
3176
return table;
3177
} catch (err) {
3178
alert(err.message);
3179
}
3180
}
3181
3182
var p = document.createElement("p");
3183
p.innerHTML += '<style>.contInfo { border-collapse: collapse; border: 1px solid black; padding: 2px;} #contInfo td {padding: 3px;} #contInfo td:nth-child(2) {border-left: 1px solid black;}</style>';
3184
var ipPattern = /\/ip\/([a-zA-Z0-9:\.]+)\/(?:document|discuss)(?:#.+|)$/;
3185
if (ipPattern.test(location.href)) {
3186
// ip
3187
// check ip
3188
var ip = ipPattern.exec(location.href)[1];
3189
var ipInfo = document.createElement("p");
3190
ipInfo.innerHTML = '<div style="border: 1px black solid; padding: 2px;">IP 관련 정보를 조회중입니다. 잠시만 기다려주세요.</div>'
3191
insertBeforeTable(ipInfo);
3192
getIpInfo(ip, function (resObj) {
3193
var country = resObj.country;
3194
var countryName = korCountryNames[country.toUpperCase()] || country.toUpperCase();
3195
var isp = resObj.org;
3196
getFlagIcon(country.toLowerCase(), async function (countryIcon) {
3197
ipInfo.innerHTML = `<table class="contInfo">
3198
<tbody>
3199
<tr><td>국가</td><td><img src=\"${countryIcon}\" style=\"height: 0.9rem;\"></img> ${countryName}</td></tr>
3200
<tr><td>통신사</td><td>${isp}</td></tr>
3201
<tr><td>VPNGATE</td><td>${await checkVPNGateIP(ip) ? "<span style=\"color: black;\">Y</span>" : "N"}</td></tr>
3202
<tr><td>KISA WHOIS</td><td><a href="#" class="get-whois">조회하기</a></td></tr>
3203
<tr class="nf_ipblockchecking"><td colspan="2">IP 차단기록을 검색하고 있습니다... 잠시만 기다려주세요.</td></tr>
3204
<tr class="nf-quickblock-row"><td>빠른차단</td><td><a class="nf-quickblock">빠른차단</a></td></tr>
3205
</tbody>
3206
<tfoot>
3207
<tr><td colspan="2" style="border-top: 1px solid black;">기술적인 한계로, VPNGATE 여부는 "현재 VPNGATE VPN인가?"의 여부이지, "작성 당시에 VPNGATE VPN인가?"의 여부가 아닙니다.<br>
3208
또한 외국 IP의 차단기록의 경우 /32 마스크만 검색하며, 사측의 비공개 차단은 검색이 불가능합니다.</td></tr>
3209
</foot>
3210
</table>`;
3211
ipInfo.querySelector('a.get-whois').addEventListener('click', function (evt) {
3212
evt.preventDefault();
3213
whoisPopup(ip);
3214
})
3215
ipInfo.querySelector('a.nf-quickblock').addEventListener('click', (evt) => {
3216
evt.preventDefault();
3217
quickBlockPopup({
3218
author: {
3219
name: ip,
3220
isIP: true
3221
},
3222
defaultDuration: SET.quickBlockDefaultDuration,
3223
defaultReason: "긴급조치"
3224
})
3225
});
3226
if(!SET.addQuickBlockLink)
3227
ipInfo.querySelector('.nf-quickblock-row').style.display = 'none';
3228
function displayIsBlocked(mask, tbody) {
3229
let row = document.createElement("tr");
3230
row.innerHTML = "<td>IP차단기록 (" + mask + ")</td><td class=\"nf_isipblocked\">조회중입니다...</td>";
3231
tbody.appendChild(row);
3232
namuapi.searchBlockHistory({
3233
query: ip + mask,
3234
isAuthor: false
3235
}, function (result) {
3236
var filtered = result.filter(function (v) {
3237
return v.blocked === ip + mask;
3238
});
3239
if (filtered.length == 0) {
3240
return row.querySelector('.nf_isipblocked').innerHTML = "차단기록 없음.";
3241
}
3242
var actType = filtered[0].type;
3243
if (actType == "blockIP") {
3244
row.querySelector('.nf_isipblocked').innerHTML = filtered[0].blocker + '에 의해 ' + (filtered[0].duration || '') + ' <span style="color:red">차단됨.</span> (일시 : ' + formatDateTime(filtered[0].at) + ', 이유 : ' + filtered[0].reason + ')';
3245
} else if (actType == "unblockIP") {
3246
row.querySelector('.nf_isipblocked').innerHTML = filtered[0].blocker + '에 의해 ' + (filtered[0].duration || '') + ' <span style="color:green">차단이 해제됨.</span> (일시 : ' + formatDateTime(filtered[0].at) + ')';
3247
} else {
3248
row.querySelector('.nf_isipblocked').innerHTML = "??? 기록 검색중 오류가 발생함.";
3249
}
3250
});
3251
}
3252
getIpWhois(ip, (whoisRes) => {
3253
ipInfo.querySelector('.nf_ipblockchecking').parentNode.removeChild(ipInfo.querySelector('.nf_ipblockchecking'));
3254
if(!whoisRes.success || whoisRes.raw) {
3255
displayIsBlocked('/32', ipInfo.querySelector('tbody'))
3256
} else {
3257
let prefixes = [];
3258
if(whoisRes.result.korean.ISP && whoisRes.result.korean.ISP.netinfo && whoisRes.result.korean.ISP.netinfo.prefix)
3259
prefixes = prefixes.concat(whoisRes.result.korean.ISP.netinfo.prefix.split('+'));
3260
if(whoisRes.result.korean.user && whoisRes.result.korean.user.netinfo && whoisRes.result.korean.user.netinfo.prefix)
3261
prefixes = prefixes.concat(whoisRes.result.korean.user.netinfo.prefix.split('+'));
3262
prefixes = prefixes.filter((v,i,s) => s.indexOf(v) === i); // https://stackoverflow.com/a/14438954
3263
if(!prefixes.includes('/32')) prefixes.push('/32');
3264
let delay = 0;
3265
for(let prefix of prefixes) {
3266
setTimeout(() => {displayIsBlocked(prefix, ipInfo.querySelector('tbody'));}, (delay++) * SET.ipBlockHistoryCheckDelay + 1);
3267
}
3268
}
3269
});
3270
});
3271
});
3272
} else {
3273
// parse username
3274
var userIdPattern = /^\/contribution\/author\/(.+?)\/(?:document|discuss)/;
3275
var userId = userIdPattern.exec(location.pathname)[1];
3276
3277
// block user link
3278
if(SET.addQuickBlockLink) {
3279
let quickBlockLink = document.createElement("div");
3280
quickBlockLink.innerHTML = '<div style="border: 1px black solid; padding: 2px;">빠른차단 : <a href="#">[차단]</a></div>'
3281
quickBlockLink.querySelector('a').addEventListener('click', (evt)=>{
3282
evt.preventDefault();
3283
quickBlockPopup({
3284
author: {
3285
name: userId,
3286
isIP: false
3287
},
3288
defaultDuration: SET.quickBlockDefaultDuration,
3289
defaultReason: "긴급조치"
3290
})
3291
})
3292
insertBeforeTable(quickBlockLink);
3293
}
3294
// user blockhistory
3295
var userInfo = document.createElement("p");
3296
userInfo.innerHTML = '<div style="border: 1px black solid; padding: 2px;" class="nf_blockhistory">차단 기록 조회중...</div>'
3297
insertBeforeTable(userInfo);
3298
namuapi.searchBlockHistory({
3299
query: userId,
3300
isAuthor: false
3301
}, function (result) {
3302
var filtered = result.filter(function (v) {
3303
return v.blocked == userId
3304
});
3305
if (filtered.length == 0) {
3306
return userInfo.querySelector('.nf_blockhistory').innerHTML = "차단기록 없음.";
3307
}
3308
userInfo.querySelector('.nf_blockhistory').innerHTML = '<style>.nf_blockhistory p {margin: 1px; padding: 0px;}</style>';
3309
for(var i = 0; i < filtered.length; i++) {
3310
var filteredItem = filtered[i];
3311
var actType = filteredItem.type;
3312
if (actType == "blockUser") {
3313
userInfo.querySelector(i == 0 ? '.nf_blockhistory' : '.nf_blockhistory_rest').innerHTML += '<p>' + filteredItem.blocker + '에 의해 ' + (filteredItem.duration || '') + ' <span style="color:red">차단됨.</span> (일시 : ' + formatDateTime(filteredItem.at) + ', 이유 : ' + filteredItem.reason + ')' + '</p>';
3314
} else if (actType == "unblockUser") {
3315
userInfo.querySelector(i == 0 ? '.nf_blockhistory' : '.nf_blockhistory_rest').innerHTML += '<p>' + filteredItem.blocker + '에 의해 ' + (filteredItem.duration || '') + ' <span style="color:green">차단이 해제됨.</span> (일시 : ' + formatDateTime(filteredItem.at) + ')' + '</p>';
3316
}
3317
if (i == 0) {
3318
userInfo.querySelector('.nf_blockhistory').innerHTML += '<a href="#" id="nf_more_blockhistory">[차단기록 더 보기]</a><div class="nf_blockhistory_rest" style="display: none;"></div>'
3319
userInfo.querySelector('#nf_more_blockhistory').addEventListener('click', (evt) => {
3320
evt.preventDefault();
3321
document.querySelector('.nf_blockhistory_rest').style.display = '';
3322
evt.target.style.display = 'none';
3323
})
3324
} else if (i == filtered.length - 1) {
3325
userInfo.querySelector('.nf_blockhistory_rest').innerHTML += '<a href="#" id="nf_less_blockhistory">[숨기기]</a>';
3326
userInfo.querySelector('#nf_less_blockhistory').addEventListener('click', (evt) => {
3327
evt.preventDefault();
3328
document.querySelector('.nf_blockhistory_rest').style.display = 'none';
3329
userInfo.querySelector('#nf_more_blockhistory').style.display = '';
3330
})
3331
}
3332
}
3333
});
3334
}
3335
3336
if (/\/document(?:#.+|)$/.test(location.href)) {
3337
var rows = document.querySelectorAll('table tbody tr');
3338
var contCount = 0,
3339
contTotalBytes = 0,
3340
contDocuments = 0,
3341
deletedDocuments = [],
3342
createdDocuments = [],
3343
documentNameBefore = "",
3344
contributedAt = [];
3345
var documents = [];
3346
for (var i = 0; i < rows.length; i++) {
3347
var row = rows[i];
3348
if (row.querySelector('a')) {
3349
var documentName = row.querySelector('a').getAttribute('href');
3350
documentNameBefore = documentName;
3351
var contributedBytes = row.querySelector('span.f_r > span').innerHTML;
3352
var negativeContribution = /^\-[0-9]+/.test(contributedBytes);
3353
if (/^\+[0-9]+/.test(contributedBytes)) contributedBytes = contributedBytes.substring(contributedBytes.indexOf('+'));
3354
contributedBytes = Number(contributedBytes);
3355
if (documents.indexOf(documentName) == -1) documents.push(documentName);
3356
contCount++;
3357
if (negativeContribution)
3358
contTotalBytes -= contributedBytes;
3359
else
3360
contTotalBytes += contributedBytes;
3361
3362
if (row.querySelector('time')) {
3363
contributedAt.push(new Date(row.querySelector('time').getAttribute("datetime")));
3364
}
3365
} else if (row.querySelector('i')) {
3366
var italicText = row.querySelector('i').innerHTML;
3367
if (italicText == '(새 문서)' && createdDocuments.indexOf(documentNameBefore) == -1) createdDocuments.push(documentNameBefore);
3368
else if (italicText == '(삭제)' && deletedDocuments.indexOf(documentNameBefore) == -1) deletedDocuments.push(documentNameBefore);
3369
}
3370
}
3371
p.innerHTML += `<table class="contInfo">
3372
<tfoot>
3373
<tr><td colspan="2" style="border-top: 1px solid black;">최근 30일간의 데이터만 반영되었으므로, 최근 30일 간의 기여 정보입니다.</td></tr>
3374
</foot>
3375
<tbody>
3376
<tr><td>총 기여 횟수</td><td>${contCount}회</td></tr>
3377
<tr><td>기여한 바이트 총합</td><td>${contTotalBytes}자</td></tr>
3378
<tr><td>총 기여한 문서 (ACL 변경, 문서 이동 포함) 수</td><td>${documents.length}개</td></tr>
3379
<tr><td>삭제한 문서 수</td><td>${deletedDocuments.length}개</td></tr>
3380
<tr><td>새로 만든 문서 수</td><td>${createdDocuments.length}개</td></tr>
3381
<tr><td>한 문서당 평균 기여 바이트</td><td>${(contTotalBytes / documents.length)}자</td></tr>
3382
<tr><td>시간대별 기여/활동 횟수 분포(문서 기여)</td><td><a href="#NothingToLink" id="punch">여기를 눌러 확인</a></td></tr>
3383
</tbody>
3384
</table>`;
3385
p.querySelector('a#punch').addEventListener('click', function (evt) {
3386
evt.preventDefault();
3387
3388
var win = TooSimplePopup();
3389
win.title('시간대별 기여/활동 횟수 분포(문서 기여)');
3390
win.content(function (element) {
3391
element.appendChild(makeHeatTable(contributedAt));
3392
});
3393
win.button('확인', win.close);
3394
})
3395
} else if (/\/discuss(?:#.+|)$/.test(location.href)) {
3396
function standardDeviation(numbers) {
3397
var total = 0;
3398
for (var i = 0; i < numbers.length; i++) {
3399
total += numbers[i];
3400
}
3401
var avg = total / numbers.length;
3402
var temp1 = 0;
3403
for (var i = 0; i < numbers.length; i++) {
3404
temp1 += Math.pow(numbers[i] - avg, 2);
3405
}
3406
temp1 /= numbers.length;
3407
return Math.sqrt(temp1);
3408
}
3409
var rows = document.querySelectorAll('table tbody tr');
3410
var docuAndTalks = {};
3411
var talkedAt = [];
3412
for (var i = 0; i < rows.length; i++) {
3413
var row = rows[i];
3414
if (row.querySelectorAll('a').length == 0) continue;
3415
var docuNow = rows[i].querySelector('a').getAttribute('href').replace(/#[0-9]+$/, '');
3416
docuNow = /^\/thread\/(.+)(?:#[0-9]+|)/.exec(docuNow)[1];
3417
if (docuAndTalks[docuNow]) {
3418
docuAndTalks[docuNow]++;
3419
} else {
3420
docuAndTalks[docuNow] = 1;
3421
}
3422
if (row.querySelector('time')) {
3423
talkedAt.push(new Date(row.querySelector('time').getAttribute("datetime")));
3424
}
3425
}
3426
var totalTalks = 0,
3427
avgTalks = 0,
3428
discussCount = 0,
3429
Talks = [];
3430
for (var i in docuAndTalks) {
3431
totalTalks += docuAndTalks[i];
3432
Talks.push(docuAndTalks[i]);
3433
}
3434
discussCount = Object.keys(docuAndTalks).length;
3435
avgTalks = totalTalks / discussCount;
3436
p.innerHTML += `<table class="contInfo">
3437
<tfoot>
3438
<tr><td colspan="2" style="border-top: 1px solid black;">최근 30일 간의 토론 정보만 반영되었으므로, 최근 30일 간의 토론 정보입니다.</td></tr>
3439
</tfoot>
3440
<tbody>
3441
<tr><td>총 발언 수</td><td>${totalTalks}</td></tr>
599
|| 1085 || [[9.11 테러]] || 1086 || [[러그너트]] ||
600
|| 1087 || [[트레이서]] || 1088 || [[서태지]] ||
601
|| 1089 || [[지렁이]] || 1090 || [[이 멋진 세계에 축복을! 부활의 베르디아]] ||
602
|| 1091 || [[아캄 정신병원 : 엄숙한 위의 엄숙한 집]] || 1092 || [[집단]] ||
603
|| 1093 || [[단어]] || 1094 || [[어묵]] ||
604
|| 1095 || [[묵호역]] || 1096 || [[역가드]] ||
605
|| 1097 || [[드림랜드선]] || 1098 || [[선수]] ||
606
|| 1099 || [[수박바]] || 1100 || [[바순]] ||
3442
<tr><td>참여한 토론 수</td><td>${discussCount}</td></tr>
3443
<tr><td>한 토론당 평균 발언 수</td><td>${avgTalks}</td></tr>
3444
<tr><td>한 토론당 발언 표준편차</td><td>${standardDeviation(Talks)}</td></tr>
3445
<tr><td>시간대별 기여/활동 횟수 분포(토론)</td><td><a href="#NothingToLink" id="punch">여기를 눌러 확인</a></td></tr>
3446
</tbody>
3447
</table>`;
3448
p.querySelector('a#punch').addEventListener('click', function (evt) {
3449
evt.preventDefault();
6073450
608
=== 1101 ~ 1200(완료) ===
609
|| 1101 || [[순천시]] || 1102 || [[메이펑]] ||
610
|| 1103 || [[펑고]] || 1104 || [[고인]] ||
611
|| 1105 || [[인성]] || 1106 || [[성기사단]] ||
612
|| 1107 || [[단장]] || 1108 || [[장유시외버스정류장]] ||
613
|| 1109 || [[장군]] || 1110 || [[군대]] ||
614
|| 1111 || [[대륙간 탄도 미사일]] || 1112 || [[일루미나티]] ||
615
|| 1113 || [[티라노]] || 1114 || [[노르트라-베스트팔렌]] ||
616
|| 1115 || [[렌더링]] || 1116 || [[링거]] ||
617
|| 1117 || [[거미줄]] || 1118 || [[줄넘기]] ||
618
|| 1119 || [[기린대로]] || 1120 || [[로고]] ||
619
|| 1121 || [[고스톱]] || 1122 || [[톱니]] ||
620
|| 1123 || [[니트]] || 1124 || [[트럭]] ||
621
|| 1125 || [[럭비]] || 1126 || [[비 내리는 단칸방]] ||
622
|| 1127 || [[방랑시인 김삿갓]] || 1128 || [[갓김치]] ||
623
|| 1129 || [[치킨]] || 1130 || [[킨키 일본 철도]] ||
624
|| 1131 || [[도시락]] || 1132 || [[락다운]] ||
625
|| 1133 || [[운영자]] || 1134 || [[자, 가라 다즐린]] ||
626
|| 1135 || [[린도우즈]] || 1136 || [[즈시]] ||
627
|| 1137 || [[시집 가려면 시다다 같은 남자를 만나라]] || 1138 || [[라 갈리소니에르급 경순양함]] ||
628
|| 1139 || [[함대]] || 1140 || [[대 마도학원 35시험소대]] ||
629
|| 1141 || [[대대장]] || 1142 || [[장영실]] ||
630
|| 1143 || [[실전주식투자 마이너 갤러리]] || 1144 || [[리본]] ||
631
|| 1145 || [[본인]] || 1146 || [[인트라게임즈]] ||
632
|| 1147 || [[즈이카쿠(함대 컬렉션)]] || 1148 || [[쿠사나기 츠요시]] ||
633
|| 1149 || [[시린빛 점쟁이]] || 1150 || [[이것이 정치다]] ||
634
|| 1151 || [[다 좋은데 말야 자네만 없으면 좋겟군]] || 1152 || [[군고구마]] ||
635
|| 1153 || [[마징가Z]] || 1154 || [[Z세대]] ||
636
|| 1155 || [[대정령]] || 1156 || [[영구]] ||
637
|| 1157 || [[구하라]] || 1158 || [[라그나로크 오디세이 에이스]] ||
638
|| 1159 || [[스타리그]] || 1160 || [[그레이스]] ||
639
|| 1161 || [[스타 플래티나]] || 1162 || [[나바론 요새]] ||
640
|| 1163 || [[새우]] || 1164 || [[우유]] ||
641
|| 1165 || [[유한양행]] || 1166 || [[행주산성]] ||
642
|| 1167 || [[성소]] || 1168 || [[소련]] ||
643
|| 1169 || [[연습]] || 1170 || [[습자지]] ||
644
|| 1171 || [[지영]] || 1172 || [[영화관 아르바이트]] ||
645
|| 1173 || [[트리케라톱스]] || 1174 || [[스트레스]] ||
646
|| 1175 || [[스카이팀]] || 1176 || [[팀데스매치]] ||
647
|| 1177 || [[치과]] || 1178 || [[과잠]] ||
648
|| 1179 || [[잠지]] || 1180 || [[지하철]] ||
649
|| 1181 || [[철도]] || 1182 || [[도쿠가와 츠나요시]] ||
650
|| 1183 || [[시소]] || 1184 || [[소위]] ||
651
|| 1185 || [[위키미키]] || 1186 || [[키리아코스 파파도풀로스]] ||
652
|| 1187 || [[스나]] || 1188 || [[나연]] ||
653
|| 1189 || [[연인]] || 1190 || [[인정]] ||
654
|| 1191 || [[정연]] || 1192 || [[연두]] ||
655
|| 1193 || [[두타산]] || 1194 || [[산화적 인산화]] ||
656
|| 1195 || [[화사]] || 1196 || [[사나]] ||
657
|| 1197 || [[나연(TWICE)]] || 1198 || [[연수]] ||
658
|| 1199 || [[수지]] || 1200 || [[지효]] ||
3451
var win = TooSimplePopup();
3452
win.title('간대별 기여/활동 횟수 분포(토론)');
3453
win.content(function (container) {
3454
container.appendChild(makeHeatTable(talkedAt));
3455
});
3456
win.button('확', win.close);
3457
})
3458
} else {
3459
delete p;
3460
}
3461
if (typeof p !== 'undefined') insertBeforeTable(p);
3462
} else if (ENV.IsDiff) {
3463
setTimeout(function () {
3464
var diffLinksHtml = `<nav>
3465
<ul class="pagination">
3466
<li class="page-item"><a href="/diff/${ENV.docTitle}?oldrev=${ENV.beforeRev - 1}&rev=${ENV.beforeRev}">&lt;-- r${ENV.beforeRev - 1} vs r${ENV.beforeRev}</a></li>
3467
<li class="page-item"><a href="/w/${ENV.docTitle}?rev=${ENV.beforeRev}">r${ENV.beforeRev} 보기</a></li>
3468
<li class="page-item"><a href="/history/${ENV.docTitle}?from=${ENV.afterRev}">역사로</a></li>
3469
<li class="page-item"><a href="/w/${ENV.docTitle}?rev=${ENV.afterRev}">r${ENV.afterRev} 보기</a></li>
3470
<li class="page-item"><a href="/diff/${ENV.docTitle}?oldrev=${ENV.afterRev}&rev=${ENV.afterRev + 1}">r${ENV.afterRev} vs r${ENV.afterRev + 1} --&gt;</a></li>
3471
</ul>
3472
</nav>`;
3473
if (ENV.skinName == "liberty") {
3474
var divTag = document.createElement("div");
3475
var articleTag = document.querySelector('.wiki-article');
3476
if (articleTag.querySelector('.diff') == null) return;
3477
divTag.innerHTML = diffLinksHtml;
3478
articleTag.insertBefore(divTag, articleTag.firstChild.nextSibling);
3479
}
3480
var divTag = document.createElement("div");
3481
var articleTag = document.querySelector('article');
3482
if (articleTag == null) return;
3483
divTag.innerHTML = diffLinksHtml;
3484
articleTag.insertBefore(divTag, articleTag.querySelector('article h1').nextSibling);
3485
}, 500);
3486
} else if (ENV.IsSearch) {
3487
if (ENV.SearchQuery.indexOf('기여:') == 0) {
3488
var target = ENV.SearchQuery.substring(3).trim();
3489
if (validateIP(target)) {
3490
location.pathname = '/contribution/ip/' + target + '/document';
3491
} else {
3492
location.pathname = '/contribution/author/' + target + '/document';
3493
}
3494
}
3495
} else if (ENV.IsIPACL || ENV.IsSuspendAccount || ENV.IsBoardIPACL || ENV.IsBoardSuspendAccount) {
3496
var expireSelect = document.querySelector('select[name=expire]');
6593497
660
=== 1201 ~ 1300(완료) ===
661
|| 1201 || [[효명옹주]] || 1202 || [[주제]] ||
662
|| 1203 || [[제목학원]] || 1204 || [[원기옥]] ||
663
|| 1205 || [[옥타레이]] || 1206 || [[이168]] ||
664
|| 1207 || [[8 seconds]] || 1208 || [[Search]] ||
665
|| 1209 || [[Heroes of the Storm 2016 Spring Global Championship]] || 1210 || [[PICK ME]] ||
666
|| 1211 || [[E reviver]] || 1212 || [[RB 라이프치히]] ||
667
|| 1213 || [[히로인]] || 1214 || [[인류]] ||
668
|| 1215 || [[류시아]] || 1216 || [[아이스크림]] ||
669
|| 1217 || [[림프 비즈킷]] || 1218 || [[킷 피스토]] ||
670
|| 1219 || [[토플리스]] || 1220 || [[스벤 크라머]] ||
671
|| 1221 || [[머루]] || 1222 || [[루마니아]] ||
672
|| 1223 || [[아메리카노]] || 1224 || [[노 노 라이프]] ||
673
|| 1225 || [[프로판디올]] || 1226 || [[올레]] ||
674
|| 1227 || [[레인보우 테이블]] || 1228 || [[블락비]] ||
675
|| 1229 || [[비적오의 난무-독나방]] || 1230 || [[방구]] ||
676
|| 1231 || [[구대성]] || 1232 || [[성기사]] ||
677
|| 1233 || [[사퀴엘]] || 1234 || [[엘리제를 위하여]] ||
678
|| 1235 || [[여수시/행정]] || 1236 || [[정자]] ||
679
|| 1237 || [[자유로]] || 1238 || [[로동심문]] ||
680
|| 1239 || [[문제]] || 1240 || [[제거제]] ||
681
|| 1241 || [[제한상영가]] || 1242 || [[가자 지구]] ||
682
|| 1243 || [[구파발 검문소 총사건]] || 1244 || [[건빵]] ||
683
|| 1245 || [[빵 사건]] || 1246 || [[건조기후]] ||
684
|| 1247 || [[후까시]] || 1248 || [[시 미셸 건 엘리펀트]] ||
685
|| 1249 || [[트위터는 인생의 낭비]] || 1250 || [[비선실세]] ||
686
|| 1251 || [[세계자연보호기금]] || 1252 || [[금요일]] ||
687
|| 1253 || [[일요일]] || 1254 || [[일본 고도인재 외국인 대상 포인트 우대제도]] ||
688
|| 1255 || [[도망자 아이리 플리나]] || 1256 || [[나문희]] ||
689
|| 1257 || [[희로애락]] || 1258 || [[락카]] ||
690
|| 1259 || [[카놀라유]] || 1260 || [[유리구슬]] ||
691
|| 1261 || [[슬기]] || 1262 || [[기승전결]] ||
692
|| 1263 || [[결자해지]] || 1264 || [[지하]] ||
693
|| 1265 || [[하천]] || 1266 || [[천왕성]] ||
694
|| 1267 || [[성왕]] || 1268 || [[왕자]] ||
695
|| 1269 || [[자립형 사립고]] || 1270 || [[고자]] ||
696
|| 1271 || [[자명종]] || 1272 || [[종언의 수호자 아드레우스]] ||
697
|| 1273 || [[스타 다이빙 쇼 스플래시]] || 1274 || [[시정연설]] ||
698
|| 1275 || [[설빙]] || 1276 || [[빙수]] ||
699
|| 1277 || [[수명물]] || 1278 || [[물고기]] ||
700
|| 1279 || [[기가바이트배 히어로즈 오브 더 스톰 파워리그 시즌 1]] || 1280 || [[1792년 8월 10일 봉기]] ||
701
|| 1281 || [[기차]] || 1282 || [[차인표]] ||
702
|| 1283 || [[표준 템플릿 라이브러리]] || 1284 || [[리턴 투 캐슬 울펜슈타인]] ||
703
|| 1285 || [[인사]] || 1286 || [[사장]] ||
704
|| 1287 || [[장사]] || 1288 || [[사고]] ||
705
|| 1289 || [[고스트 리콘 어드밴스드 워파이터 2]] || 1290 || [[2017 Heroes of the Storm Global Championship North America Phase 1]] ||
706
|| 1291 || [[1907 Fenerbae Esports]] || 1292 || [[Star Trek:The Next Generation/Doctor Who: Assimilation2]] ||
707
|| 1293 || [[2016년 두만강 유역 대홍수]] || 1294 || [[수박]] ||
708
|| 1295 || [[박근령]] || 1296 || [[영대]] ||
709
|| 1297 || [[대전]] || 1298 || [[전시]] ||
710
|| 1299 || [[시망]] || 1300 || [[망자]] ||
3498
function enterEasily() {
3499
enterTimespanPopup('차단기간 입력하기', function (result) {
3500
if (result !== null)
3501
document.querySelector('input[name="expire"]').value = result;
3502
});
3503
}
7113504
712
=== 1301 ~ 1400(완료) ===
713
|| 1301 || [[자산어]] || 1302 || [[보드카]] ||
714
|| 1303 || [[카메라]] || 1304 || [[라땡면]] ||
715
|| 1305 || [[면간이]] || 1306 || [[승만]] ||
716
|| 1307 || [[만두]] || 1308 || [[두목]] ||
717
|| 1309 || [[목사]] || 1310 || [[사회]] ||
718
|| 1311 || [[장]] || 1312 || [[장암역]] ||
719
|| 1313 || [[역기]] || 1314 || [[사]] ||
720
|| 1315 || [[사반왕]] || 1316 || [[왕조]] ||
721
|| 1317 || [[조상]] || 1318 || [[상계역]] ||
722
|| 1319 || [[역대 대한민국 도지사]] || 1320 || [[사이비]] ||
723
|| 1321 || [[비상구]] || 1322 || [[구구단(아이돌)|구구단]] ||
724
|| 1323 || [[단수]] || 1324 || [[수도]] ||
725
|| 1325 || [[도까]] || 1326 || [[까까]] ||
726
|| 1327 || [[까나리]] || 1328 || [[리무진]] ||
727
|| 1329 || [[진지충]] || 1330 || [[충북선]] ||
728
|| 1331 || [[선생님이 여러가지로 열심이라서 잠들 없는 CD]] || 1332 || [[Degradation Trip Volumes 1 & 2]] ||
729
|| 1333 || [[2014 FIBA 세계 여자 선수권 대회 터키]] || 1334 || [[키위위키]] ||
730
|| 1335 || [[키르기스스탄]] || 1336 || [[탄소]] ||
731
|| 1337 || [[소이(구구단)|소이]] || 1338 || [[이사]] ||
732
|| 1339 || [[사격]] || 1340 || [[격전]] ||
733
|| 1341 || [[전도서에 바치는 장미]] || 1342 || [[미미(구구단)|미미]] ||
734
|| 1343 || [[미술]] || 1344 || [[술]] ||
735
|| 1345 || [[술잔 깨기]] || 1346 || [[기술 격차]] ||
736
|| 1347 || [[차량기지]] || 1348 || [[지행역]] ||
737
|| 1349 || [[역대 걸그룹 음반 초동 순위]] || 1350 || [[위대한 유산]] ||
738
|| 1351 || [[산성역]] || 1352 || [[역적]] ||
739
|| 1353 || [[적절]] || 1354 || [[절기]] ||
740
|| 1355 || [[기적의 논리]] || 1356 || [[이앙기]] ||
741
|| 1357 || [[기동전사 건담 00 극장판 코믹스]] || 1358 || [[스타크래프트 2: 공허의 유산]] ||
742
|| 1359 || [[산소]] || 1360 || [[소총]] ||
743
|| 1361 || [[총황무진 파프니르]] || 1362 || [[르노]] ||
744
|| 1363 || [[노지선]] || 1364 || [[선박]] ||
745
|| 1365 || [[박물관]] || 1366 || [[관선]] ||
746
|| 1367 || [[선생님]] || 1368 || [[님아, 강을 건너지 마오]] ||
747
|| 1369 || [[오리고기]] || 1370 || [[기아차]] ||
748
|| 1371 || [[차별]] || 1372 || [[별내IC]] ||
749
|| 1373 || [[CLC]] || 1374 || [[CCTV]] ||
750
|| 1375 || [[V 라이브]] || 1376 || [[브릭]] ||
751
|| 1377 || [[릭키1]] || 1378 || [[123 Slaughter Me Street]] ||
752
|| 1379 || [[The Beatles at the Hollywood Bowl]] || 1380 || [[Live In Australia With The Melbourne Symphony Orchestra]] ||
753
|| 1381 || [[Angel's Feather 흑의 잔영]] || 1382 || [[영어]] ||
754
|| 1383 || [[어묵꼬치]] || 1384 || [[치루란 2분의 1]] ||
755
|| 1385 || [[1SWASP J093010.78+533859.5]] || 1386 || [[57mm ZiS-2]] ||
756
|| 1387 || [[2015 League of Legends Challengers Korea Spring 1차 토너먼트]] || 1388 || [[트레일러]] ||
757
|| 1389 || [[러시아 제국]] || 1390 || [[국가]] ||
758
|| 1391 || [[가라귀신]] || 1392 || [[신당역]] ||
759
|| 1393 || [[역발산]] || 1394 || [[산탄총]] ||
760
|| 1395 || [[총장]] || 1396 || [[장근석]] ||
761
|| 1397 || [[석가모니]] || 1398 || [[니모를 찾아서]] ||
762
|| 1399 || [[서울가요대상]] || 1400 || [[상평형]] ||
3505
function replaceExpireSelect() {
3506
var newExpireInput = document.createElement('input');
3507
newExpireInput.setAttribute("type", "number");
3508
newExpireInput.setAttribute("class", "form-control");
3509
newExpireInput.setAttribute("name", "expire");
3510
var explain = document.createElement("p");
3511
explain.innerText = "차단기간은 초 단위로 입력해야 하며, 영구차단시에는 0을, 사용자 차단에서 차단 해제시에는 -1 입력하시면 됩니다.";
3512
var enterEasilyLink = document.createElement("a");
3513
enterEasilyLink.innerText = "차단기간 간편하게 입력하기";
3514
enterEasilyLink.href = "#";
3515
enterEasilyLink.addEventListener('click', function (evt) {
3516
evt.preventDefault();
3517
enterEasily();
3518
})
3519
expireSelect.parentNode.insertBefore(newExpireInput, expireSelect);
3520
expireSelect.parentNode.insertBefore(explain, expireSelect);
3521
expireSelect.parentNode.insertBefore(document.createElement("br"), expireSelect);
3522
expireSelect.parentNode.insertBefore(enterEasilyLink, expireSelect);
3523
expireSelect.parentNode.removeChild(expireSelect);
3524
}
3525
if (expireSelect != null) {
3526
var replaceExpireLink = document.createElement("a");
3527
replaceExpireLink.href = "#";
3528
replaceExpireLink.innerText = "초 단위로 차단 기간 입력하기";
3529
replaceExpireLink.addEventListener('click', function (evt) {
3530
evt.preventDefault();
3531
replaceExpireSelect();
3532
replaceExpireLink.parentNode.removeChild(replaceExpireLink);
3533
});
3534
expireSelect.parentNode.insertBefore(replaceExpireLink, expireSelect.nextSibling);
3535
}
3536
} else if (ENV.IsHistory) {
3537
addArticleButton('토론', function () {
3538
location.href = "/discuss/" + encodeURIComponent(ENV.docTitle);
3539
})
3540
addArticleButton('리버전 점프', function () {
3541
var revNo = prompt(' 싶은 리버전 번호를 입력하세요.').trim();
3542
if (revNo.indexOf('r') == 0) revNo = revNo.substring(1);
3543
if (/[^0-9]/.test(revNo)) {
3544
alert('올바른 입력이 아닙니다! r1 혹은 1과 같이 입력해주세요.');
3545
return;
3546
}
3547
location.href = "/w/" + encodeURIComponent(ENV.docTitle) + "?rev=" + revNo;
3548
})
3549
var historyRows = document.querySelectorAll('article .wiki-list li, body.Liberty .wiki-article .wiki-list li');
3550
for (let historyRow of [].slice.call(historyRows)) {
3551
// 긴급차단
3552
if (SET.addQuickBlockLink) {
3553
let revisionNo = historyRow.querySelector('strong').textContent.trim();
3554
let temp = historyRow.querySelector('span').innerHTML.trim();
3555
historyRow.querySelector('span').innerHTML = temp.substring(0, temp.length - 1) + ' | <a href="#" class="nf-history-quickblock">차단</a>)';
3556
historyRow.querySelector('a.nf-history-quickblock').addEventListener('click', (evt) => {
3557
evt.preventDefault();
3558
let user = historyRow.querySelectorAll('a');
3559
user = user[user.length - 1].textContent.trim();
3560
quickBlockPopup({
3561
author: {
3562
name: user,
3563
isIP: validateIP(user)
3564
},
3565
defaultDuration: SET.quickBlockDefaultDuration,
3566
defaultReason: SET.quickBlockReasonTemplate_history.replace(/\$\{host\}/g, location.host)
3567
.replace(/\$\{revisionNo\}/g, revisionNo)
3568
.replace(/\$\{docName\}/g, ENV.docTitle)
3569
});
3570
});
3571
}
3572
// ACL 가독성
3573
var italicTag = historyRow.querySelector('i');
3574
if (italicTag) {
3575
var pattern = /\(([a-zA-Z,]+?)으로 ACL 변경\)/;
3576
// 열람 수정 삭제 (한국) 토론
3577
var valuePatternWithBlockKorea = /(admin|member|everyone),(admin|member|everyone),(admin|member|everyone),(true|false),(admin|member|everyone),(admin|member|everyone)/;
3578
var valuePattern = /(admin|member|everyone),(admin|member|everyone),(admin|member|everyone),(admin|member|everyone),(admin|member|everyone)/;
3579
var icons = ['ion-eye fa fa-eye', 'ion-edit fa fa-edit', 'ion-trash-a fa fa-trash', 'ion-android-textsms fa fa-comments', 'ion-arrow-right-c fa fa-arrow-right', 'ion-flag fa fa-flag'];
3580
var koreanAclConds = {
3581
'member': '원',
3582
'admin': '관리자',
3583
'everyone': '모두'
3584
};
3585
if (pattern.test(italicTag.innerHTML)) {
3586
var aclText = pattern.exec(italicTag.innerHTML)[1];
3587
var newAclText = '';
3588
var acl = null,
3589
isKoreaBlocked = null;
3590
var matched = false;
3591
if (aclText == 'delete') {
3592
newAclText = '(ACL 초화)';
3593
matched = true;
3594
} else if (valuePatternWithBlockKorea.test(aclText)) {
3595
acl = valuePatternWithBlockKorea.exec(aclText);
3596
isKoreaBlocked = acl[4] == 'true';
3597
acl = [acl[0], acl[1], acl[2], acl[3], acl[5], acl[6]];
3598
matched = true;
3599
} else if (valuePattern.test(aclText)) {
3600
acl = valuePattern.exec(aclText);
3601
matched = true;
3602
}
3603
if (!matched)
3604
continue;
3605
if (newAclText == '') {
3606
newAclText = '(';
3607
for (var i = 1; i <= 5; i++) {
3608
var color = acl[i] == 'admin' ? 'red' : acl[i] == 'member' ? 'orange' : null;
3609
newAclText += '<span ' + (color ? 'style="color:' + color + '" ' : '') + '><span class="icon ' + icons[i - 1] + '"></span>' + koreanAclConds[acl[i]] + '</span>,';
3610
}
3611
if (isKoreaBlocked) {
3612
newAclText += '<span style="color: blue;"><span class="icon ' + icons[5] + '"></span>한국 IP 차단</span>,';
3613
}
3614
newAclText = newAclText.substring(0, newAclText.length - 1);
3615
newAclText += '으로 ACL 변경)';
3616
}
3617
italicTag.innerHTML = italicTag.innerHTML.replace(pattern.exec(italicTag.innerHTML)[0], newAclText);
3618
}
3619
}
3620
}
3621
} else if (ENV.IsRecentChanges) {
3622
let changeRows = document.querySelectorAll('article table.table tbody tr');
3623
for (let row of changeRows) {
3624
if (!row.querySelector('a')) continue; // 편집 코멘트 필요없음.
3625
let author = {
3626
name: row.querySelector('td:nth-child(2) a').textContent.trim()
3627
}
3628
author.isIP = validateIP(author.name);
3629
if(SET.addQuickBlockLink) {
3630
let quickBlockAnchor = document.createElement("a");
3631
quickBlockAnchor.textContent = " [차단] "
3632
quickBlockAnchor.href = "#";
3633
quickBlockAnchor.addEventListener('click', (evt) => {
3634
evt.preventDefault();
3635
quickBlockPopup({
3636
author: author,
3637
defaultReason: '긴급차단 - 반달리즘',
3638
defaultDuration: SET.quickBlockDefaultDuration
3639
});
3640
})
3641
row.querySelector('td:first-child').insertBefore(quickBlockAnchor, row.querySelector('td:first-child span'));
3642
}
3643
}
3644
}
7633645
764
=== 1401 ~ 1500(완료) ===
765
|| 1401 || [[형가]] || 1402 || [[가! 가란 말이야! 레이디가가]] ||
766
|| 1403 || [[가남풍]] || 1404 || [[풍선]] ||
767
|| 1405 || [[선물]] || 1406 || [[물차]] ||
768
|| 1407 || [[차차차]] || 1408 || [[차우찬]] ||
769
|| 1409 || [[찬미]] || 1410 || [[미나(구구단)|미나]] ||
770
|| 1411 || [[나이키]] || 1412 || [[키타오오지 이츠키]] ||
771
|| 1413 || [[키퍼]] || 1414 || [[퍼센트]] ||
772
|| 1415 || [[트라이앵글]] || 1416 || [[글 581]] ||
773
|| 1417 || [[1¼톤 트럭]] || 1418 || [[럭 로직]] ||
774
|| 1419 || [[직전신장]] || 1420 || [[장준하]] ||
775
|| 1421 || [[하동훈]] || 1422 || [[훈련교관 라주비어스]] ||
776
|| 1423 || [[스팀 라이더 바남]] || 1424 || [[남행열]] ||
777
|| 1425 || [[차대왕]] || 1426 || [[왕왕]] ||
778
|| 1427 || [[왕창]] || 1428 || [[창세기]] ||
779
|| 1429 || [[기관총]] || 1430 || [[총신]] ||
780
|| 1431 || [[신념]] || 1432 || [[염제]] ||
781
|| 1433 || [[제사해 운동]] || 1434 || [[동양고속]] ||
782
|| 1435 || [[속리산고속]] || 1436 || [[속리산]] ||
783
|| 1437 || [[산화이수소]] || 1438 || [[소스]] ||
784
|| 1439 || [[스라소니]] || 1440 || [[이국미로의 크로와제]] ||
785
|| 1441 || [[제14대 중화민국 정부총통 선거]] || 1442 || [[거식증]] ||
786
|| 1443 || [[증조]] || 1444 || [[조봉암]] ||
787
|| 1445 || [[암초]] || 1446 || [[초록 눈]] ||
788
|| 1447 || [[눈싸움]] || 1448 || [[움집]] ||
789
|| 1449 || [[집중공격]] || 1450 || [[격파]] ||
790
|| 1451 || [[파도]] || 1452 || [[도를 아십니까]] ||
791
|| 1453 || [[까투리]] || 1454 || [[이촌역]] ||
792
|| 1455 || [[역전의 백귀야행]] || 1456 || [[행신역]] ||
793
|| 1457 || [[역정서]] || 1458 || [[서보라]] ||
794
|| 1459 || [[라이터]] || 1460 || [[터렛]] ||
795
|| 1461 || [[렛잇고]] || 1462 || [[고산역]] ||
796
|| 1463 || [[역상]] || 1464 || [[상경용천부]] ||
797
|| 1465 || [[부울경]] || 1466 || [[경칩]] ||
798
|| 1467 || [[칩거]] || 1468 || [[거장]] ||
799
|| 1469 || [[장제스]] || 1470 || [[스압]] ||
800
|| 1471 || [[압정뿌리기]] || 1472 || [[기동전사 건담 AGE 추억의 시드]] ||
801
|| 1473 || [[드네프로페트로프스크 매니악]] || 1474 || [[악수]] ||
802
|| 1475 || [[수아레스]] || 1476 || [[스타 오션 5: Integrity and Faithlessness]] ||
803
|| 1477 || [[Sgt. Pepper's Lonely Hearts Club Band]] || 1478 || [[Doki Doki Literature Club!]] ||
804
|| 1479 || [[BBC 국가 영향 평가 여론조사]] || 1480 || [[사묘아리]] ||
805
|| 1481 || [[리스본]] || 1482 || [[본죽]] ||
806
|| 1483 || [[죽간]] || 1484 || [[간송 전형필]] ||
807
|| 1485 || [[필라 오브 오톰]] || 1486 || [[톰슨]] ||
808
|| 1487 || [[슨상]] || 1488 || [[상상임신]] ||
809
|| 1489 || [[신기루]] || 1490 || [[루미너스]] ||
810
|| 1491 || [[스갤]] || 1492 || [[갤럭시 히어로즈: 라쳇 클랭크]] ||
811
|| 1493 || [[크루픽스의 예언자]] || 1494 || [[자위]] ||
812
|| 1495 || [[위도]] || 1496 || [[도량형]] ||
813
|| 1497 || [[형제]] || 1498 || [[제페니아 만]] ||
814
|| 1499 || [[만약 고교야구 여자 매니저가 피터 드러커를 읽는다면]] || 1500 || [[면제로이드]]('''1500개!''') ||
3646
if (ENV.skinName == "liberty") {
3647
if (SET.addAdminLinksForLiberty) {
3648
// 링크 추가
3649
document.querySelector('nav.navbar ul.nav li.nav-item.dropdown .dropdown-menu').innerHTML += '<div class="dropdown-divider"></div>' +
3650
'<a class="dropdown-item" href="/admin/suspend_account">계정단</a>' +
3651
'<a class="dropdown-item" href="/admin/ipacl">IP </a>' +
3652
'<a class="dropdown-item" href="/admin/grant">권한 부여</a>';
3653
if (ENV.IsDocument) {
3654
addArticleButton("ACL", function () {
3655
location.href = "/acl/" + ENV.docTitle;
3656
})
3657
}
3658
}
3659
if (ENV.IsDocument && ENV.docTitle.indexOf('사용자:') == 0) {
3660
addArticleButton("기여내역", function () {
3661
var userName = ENV.docTitle.substring(4);
3662
location.href = "/contribution/" + (validateIP(userName) ? "ip/" : "author/") + userName + "/document";
3663
})
3664
}
3665
}
3666
}
3667
document.querySelector('nav.navbar ul.nav li.nav-item.dropdown .dropdown-menu').innerHTML += '<div class="dropdown-divider"></div>' +
3668
'<a class="dropdown-item" href="https://phpgongbu.ga/archive"><span class="icon ion-wrench"></span> 게시판 아카이브 도구</a>' +
3669
'<a class="dropdown-item" href="https://phpgongbu.ga/tocont.php"><span class="icon ion-wrench"></span> 기여내역 점프기</a>' +
3670
'<a class="dropdown-item" href="https://phpgongbu.ga/ip.php"><span class="icon ion-wrench"></span> 내 IP 주소 확인</a>';
8153671
816
=== 1501 ~ 1600(완료) ===
817
|| 1501 || [[드조위키]] || 1502 || [[키르기즈]] ||
818
|| 1503 || [[즈곡크E]] || 1504 || [[Encyclopedia Dramatica]] ||
819
|| 1505 || [[archive.is]] || 1506 || [[S라인]] ||
820
|| 1507 || [[인천광역시]] || 1508 || [[시발]] ||
821
|| 1509 || [[발냄새]] || 1510 || [[새끼]] ||
822
|| 1511 || [[끼워팔기]] || 1512 || [[기벡]] ||
823
|| 1513 || [[벡터(수학)]] || 1514 || [[터너]] ||
824
|| 1515 || [[너님]] || 1516 || [[임자]] ||
825
|| 1517 || [[자취]] || 1518 || [[취소]] ||
826
|| 1519 || [[소인수분해]] || 1520 || [[해충갤]] ||
827
|| 1521 || [[갤러리]] || 1522 || [[리리스 타이탄본]] ||
828
|| 1523 || [[본명]] || 1524 || [[명찰]] ||
829
|| 1525 || [[찰갑]] || 1526 || [[갑각류]] ||
830
|| 1527 || [[유관]] || 1528 || [[관계]] ||
831
|| 1529 || [[계곡]] || 1530 || [[곡성군/교통]] ||
832
|| 1531 || [[통일]] || 1532 || [[일베]] ||
833
|| 1533 || [[베를린]] || 1534 || [[린다]] ||
834
|| 1535 || [[다트]] || 1536 || [[트위스티드 시스터]] ||
835
|| 1537 || [[터널 3D]] || 1538 || [[DD]] ||
836
|| 1539 || [[DTD]] || 1540 || [[DDT]] ||
837
|| 1541 || [[TD]] || 1542 || [[Devenjer]] ||
838
|| 1543 || [[robots.txt]] || 1544 || [[T store]] ||
839
|| 1545 || [[EXID]] || 1546 || [[Deorro]] ||
840
|| 1547 || [[O tvN]] || 1548 || [[N.Flying]] ||
841
|| 1549 || [[GOT7]] || 1550 || [[710번 지방도]] ||
842
|| 1551 || [[도르니안 헤러시]] || 1552 || [[시애틀 매리너스]] ||
843
|| 1553 || [[스리랑카]] || 1554 || [[카이젤 드 엠페러너 벨제바브 4세]] ||
844
|| 1555 || [[세계가 지켜보고 있습니다 ~을(를) 자제해 주십시오]] || 1556 || [[오스트리아]] ||
845
|| 1557 || [[아기]] || 1558 || [[기니]] ||
846
|| 1559 || [[니카라과]] || 1560 || [[과수원]] ||
847
|| 1561 || [[원반생물 로베르가]] || 1562 || [[가면라이더 에그제이드(가면라이더)/신체 부위]] ||
848
|| 1563 || [[위암]] || 1564 || [[암사역]] ||
849
|| 1565 || [[역설]] || 1566 || [[설날]] ||
850
|| 1567 || [[날선승]] || 1568 || [[승리]] ||
851
|| 1569 || [[이가]] || 1570 || [[가이거 계수기]] ||
852
|| 1571 || [[기대승]] || 1572 || [[승호]] ||
853
|| 1573 || [[호서대학교]] || 1574 || [[교사]] ||
854
|| 1575 || [[사진]] || 1576 || [[진시황]] ||
855
|| 1577 || [[황산]] || 1578 || [[산할아버지]] ||
856
|| 1579 || [[지동설]] || 1580 || [[설총]] ||
857
|| 1581 || [[총구]] || 1582 || [[구리(원소)]] ||
858
|| 1583 || [[리그 오브 레전드]] || 1584 || [[드라군 놀이]] ||
859
|| 1585 || [[이산수학 ]] || 1586 || [[학사경고]] ||
860
|| 1587 || [[고슴도치의 딜레마]] || 1588 || [[마술사]] ||
861
|| 1589 || [[사무관 Rainbow 권한 남용 사건]] || 1590 || [[건포도]] ||
862
|| 1591 || [[도비]] || 1592 || [[비기]] ||
863
|| 1593 || [[기술]] || 1594 || [[술 게임]] ||
864
|| 1595 || [[임업]] || 1596 || [[업보]] ||
865
|| 1597 || [[보름달]] || 1598 || [[달거리 ]] ||
866
|| 1599 || [[리소자임]] || 1600 || [[임수동]] ||
8673672
868
=== 1601 ~ 1700(완료) ===
869
|| 1601 || [[동구]] || 1602 || [[구상성단]] ||
870
|| 1603 || [[단발머리]] || 1604 || [[리스식 해안]] ||
871
|| 1605 || [[안성]] || 1606 || [[성승헌]] ||
872
|| 1607 || [[헌릉IC]] || 1608 || [[CNo.9 천개요성 카오스 다 스피어]] ||
873
|| 1609 || [[어 트라이브 콜드 퀘스트]] || 1610 || [[트리톤]] ||
874
|| 1611 || [[톤즈라]] || 1612 || [[라임]] ||
875
|| 1613 || [[임신]] || 1614 || [[신 광신화 파르테나의 거울]] ||
876
|| 1615 || [[울린]] || 1616 || [[린든 존슨]] ||
877
|| 1617 || [[슨상존]] || 1618 || [[존 윅]] ||
878
|| 1619 || [[윅스]] || 1620 || [[ 스노보드]] ||
879
|| 1621 || [[드와이트 아이젠하워 ]] || 1622 || [[워 머신]] ||
880
|| 1623 || [[신자]] || 1624 || [[자낳괴]] ||
881
|| 1625 || [[괴동선]] || 1626 || [[선장]] ||
882
|| 1627 || [[장선]] || 1628 || [[선 텔레비전]] ||
883
|| 1629 || [[전룡단]] || 1630 || [[단란주점 ]] ||
884
|| 1631 || [[점쟁이]] || 1632 || [[이 자식 안 되겠어 빨리 어떻게든 하지 않으면]] ||
885
|| 1633 || [[면벌부]] || 1634 || [[부스트]] ||
886
|| 1635 || [[트루먼 독트린]] || 1636 || [[인지 첩부·첨부 및 공탁 제공에 관한 특례법]] ||
887
|| 1637 || [[법흥왕]] || 1638 || [[왕자지]] ||
888
|| 1639 || [[지랄]] || 1640 || [[랄곤킨 바스카크]] ||
889
|| 1641 || [[크툴루 스타 스폰]] || 1642 || [[폰투스]] ||
890
|| 1643 || [[스크린]] || 1644 || [[린드버라이오노스]] ||
891
|| 1645 || [[스트레이트 에지 소사이어티]] || 1646 || [[티셔츠]] ||
892
|| 1647 || [[츠와나어]] || 1648 || [[어부]] ||
893
|| 1649 || [[부르한 산]] || 1650 || [[산]] ||
894
|| 1651 || [[산업혁명]] || 1652 || [[명대사]] ||
895
|| 1653 || [[사지]] || 1654 || [[지바니우통 마르틴스 페헤이라]] ||
896
|| 1655 || [[라에노르 벨라리온]] || 1656 || [[온두라스]] ||
897
|| 1657 || [[스타쉽엔터테인먼트]] || 1658 || [[트랜스포머 G1 시즌4 더 리버스]] ||
898
|| 1659 || [[스타크래프트 2: 공허의 유산/협동전 임무/돌연변이/자린고비]] || 1660 || [[비투비]] ||
899
|| 1661 || [[비산식]] || 1662 || [[식염수]] ||
900
|| 1663 || [[수산화수소]] || 1664 || [[소고기]] ||
901
|| 1665 || [[기술자]] || 1666 || [[자아]] ||
902
|| 1667 || [[아빠는 웬만해서 눈물이 나는 사람인데 눈물이 나오네]] || 1668 || [[네페]] ||
903
|| 1669 || [[페티시]] || 1670 || [[시작되지 않은 종말전쟁과 끝나버린 우리들의 청춘활극]] ||
904
|| 1671 || [[극화]] || 1672 || [[화장실]] ||
905
|| 1673 || [[실버 쏜 드래곤 엠프레스 비너스 루키에]] || 1674 || [[에피]] ||
906
|| 1675 || [[피로스의 승리]] || 1676 || [[이승]] ||
907
|| 1677 || [[승천의 뿔피리]] || 1678 || [[이빨치료]] ||
908
|| 1679 || [[요금]] || 1680 || [[금액]] ||
909
|| 1681 || [[액정]] || 1682 || [[정령이 되지 못한 자]] ||
910
|| 1683 || [[자립]] || 1684 || [[입대]] ||
911
|| 1685 || [[대관]] || 1686 || [[관종]] ||
912
|| 1687 || [[종자]] || 1688 || [[자세]] ||
913
|| 1689 || [[세율]] || 1690 || [[율법학자]] ||
914
|| 1691 || [[자식]] || 1692 || [[식구]] ||
915
|| 1693 || [[구의야구공원]] || 1694 || [[원가]] ||
916
|| 1695 || [[가족]] || 1696 || [[족발]] ||
917
|| 1697 || [[발상]] || 1698 || [[상부상조]] ||
918
|| 1699 || [[조연]] || 1700 || [[연가]] ||
3673
// 아이덴티콘 버그 수정
3674
setInterval(function () {
3675
if (!/^\/discuss\/(.+?)/.test(location.pathname)) {
3676
return;
3677
}
3678
var identicons = document.querySelectorAll('.nf-identicon');
3679
for (var i = 0; i < identicons.length; i++) {
3680
var ide = identicons[i];
3681
var pa = ide.parentNode;
9193682
920
=== 1701 ~ 1800(진행 중) ===
921
##단어를 추가하기 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
922
##한 번에 한 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
923
|| 1701 || [[가장 따뜻한 색, 블루]] || 1702 || [[루비플러스]] ||
924
|| 1703 || [[스팀]] || 1704 || [[팀플레이]] ||
925
|| 1705 || [[이씨]] || 1706 || [[씨랜드 청소년수련원 화재사고]] ||
926
|| 1707 || [[고스트 RE:BIRTH 가면라이더 스펙터]] || 1708 || [[터닝메카드]] ||
927
|| 1709 || [[드래곤 퀘스트 몬스터즈 2: 이루와 루카의 신기한 열쇠]] || 1710 || [[쇠데르만란드 주]] ||
928
|| 1711 || [[ ]] || 1712 || [[ ]] ||
929
|| 1713 || [[ ]] || 1714 || [[ ]] ||
930
|| 1715 || [[ ]] || 1716 || [[ ]] ||
931
|| 1717 || [[ ]] || 1718 || [[ ]] ||
932
|| 1719 || [[ ]] || 1720 || [[ ]] ||
933
|| 1721 || [[ ]] || 1722 || [[ ]] ||
934
|| 1723 || [[ ]] || 1724 || [[ ]] ||
935
|| 1725 || [[ ]] || 1726 || [[ ]] ||
936
|| 1727 || [[ ]] || 1728 || [[ ]] ||
937
|| 1729 || [[ ]] || 1730 || [[ ]] ||
938
|| 1731 || [[ ]] || 1732 || [[ ]] ||
939
|| 1733 || [[ ]] || 1734 || [[ ]] ||
940
|| 1735 || [[ ]] || 1736 || [[ ]] ||
941
|| 1737 || [[ ]] || 1738 || [[ ]] ||
942
|| 1739 || [[ ]] || 1740 || [[ ]] ||
943
|| 1741 || [[ ]] || 1742 || [[ ]] ||
944
|| 1743 || [[ ]] || 1744 || [[ ]] ||
945
|| 1745 || [[ ]] || 1746 || [[ ]] ||
946
|| 1747 || [[ ]] || 1748 || [[ ]] ||
947
|| 1749 || [[ ]] || 1750 || [[ ]] ||
948
|| 1751 || [[ ]] || 1752 || [[ ]] ||
949
|| 1753 || [[ ]] || 1754 || [[ ]] ||
950
|| 1755 || [[ ]] || 1756 || [[ ]] ||
951
|| 1757 || [[ ]] || 1758 || [[ ]] ||
952
|| 1759 || [[ ]] || 1760 || [[ ]] ||
953
|| 1761 || [[ ]] || 1762 || [[ ]] ||
954
|| 1763 || [[ ]] || 1764 || [[ ]] ||
955
|| 1765 || [[ ]] || 1766 || [[ ]] ||
956
|| 1767 || [[ ]] || 1768 || [[ ]] ||
957
|| 1769 || [[ ]] || 1770 || [[ ]] ||
958
|| 1771 || [[ ]] || 1772 || [[ ]] ||
959
|| 1773 || [[ ]] || 1774 || [[ ]] ||
960
|| 1775 || [[ ]] || 1776 || [[ ]] ||
961
|| 1777 || [[ ]] || 1778 || [[ ]] ||
962
|| 1779 || [[ ]] || 1780 || [[ ]] ||
963
|| 1781 || [[ ]] || 1782 || [[ ]] ||
964
|| 1783 || [[ ]] || 1784 || [[ ]] ||
965
|| 1785 || [[ ]] || 1786 || [[ ]] ||
966
|| 1787 || [[ ]] || 1788 || [[ ]] ||
967
|| 1789 || [[ ]] || 1790 || [[ ]] ||
968
|| 1791 || [[ ]] || 1792 || [[ ]] ||
969
|| 1793 || [[ ]] || 1794 || [[ ]] ||
970
|| 1795 || [[ ]] || 1796 || [[ ]] ||
971
|| 1797 || [[ ]] || 1798 || [[ ]] ||
972
|| 1799 || [[ ]] || 1800 || [[ ]] ||
3683
pa.removeChild(ide);
3684
pa.style.marginTop = '';
3685
}
3686
if (document.querySelector('#nf-identicon-css') != null) {
3687
var cssTag = document.querySelector('#nf-identicon-css');
3688
cssTag.parentNode.removeChild(cssTag);
3689
}
3690
}, 50);
9733691
974
##1800까지 적으신 분은 문단 제목을 완료로 바꿔주세요.
3692
addItemToMemberMenu('내 사용자 토론', function (evt) {
3693
evt.preventDefault();
9753694
976
=== 1801 ~ 1900 ===
977
##단어를 추가하기 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
978
##한 번에 한 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
979
|| 1801 || [[ ]] || 1802 || [[ ]] ||
980
|| 1803 || [[ ]] || 1804 || [[ ]] ||
981
|| 1805 || [[ ]] || 1806 || [[ ]] ||
982
|| 1807 || [[ ]] || 1808 || [[ ]] ||
983
|| 1809 || [[ ]] || 1810 || [[ ]] ||
984
|| 1811 || [[ ]] || 1812 || [[ ]] ||
985
|| 1813 || [[ ]] || 1814 || [[ ]] ||
986
|| 1815 || [[ ]] || 1816 || [[ ]] ||
987
|| 1817 || [[ ]] || 1818 || [[ ]] ||
988
|| 1819 || [[ ]] || 1820 || [[ ]] ||
989
|| 1821 || [[ ]] || 1822 || [[ ]] ||
990
|| 1823 || [[ ]] || 1824 || [[ ]] ||
991
|| 1825 || [[ ]] || 1826 || [[ ]] ||
992
|| 1827 || [[ ]] || 1828 || [[ ]] ||
993
|| 1829 || [[ ]] || 1830 || [[ ]] ||
994
|| 1831 || [[ ]] || 1832 || [[ ]] ||
995
|| 1833 || [[ ]] || 1834 || [[ ]] ||
996
|| 1835 || [[ ]] || 1836 || [[ ]] ||
997
|| 1837 || [[ ]] || 1838 || [[ ]] ||
998
|| 1839 || [[ ]] || 1840 || [[ ]] ||
999
|| 1841 || [[ ]] || 1842 || [[ ]] ||
1000
|| 1843 || [[ ]] || 1844 || [[ ]] ||
1001
|| 1845 || [[ ]] || 1846 || [[ ]] ||
1002
|| 1847 || [[ ]] || 1848 || [[ ]] ||
1003
|| 1849 || [[ ]] || 1850 || [[ ]] ||
1004
|| 1851 || [[ ]] || 1852 || [[ ]] ||
1005
|| 1853 || [[ ]] || 1854 || [[ ]] ||
1006
|| 1855 || [[ ]] || 1856 || [[ ]] ||
1007
|| 1857 || [[ ]] || 1858 || [[ ]] ||
1008
|| 1859 || [[ ]] || 1860 || [[ ]] ||
1009
|| 1861 || [[ ]] || 1862 || [[ ]] ||
1010
|| 1863 || [[ ]] || 1864 || [[ ]] ||
1011
|| 1865 || [[ ]] || 1866 || [[ ]] ||
1012
|| 1867 || [[ ]] || 1868 || [[ ]] ||
1013
|| 1869 || [[ ]] || 1870 || [[ ]] ||
1014
|| 1871 || [[ ]] || 1872 || [[ ]] ||
1015
|| 1873 || [[ ]] || 1874 || [[ ]] ||
1016
|| 1875 || [[ ]] || 1876 || [[ ]] ||
1017
|| 1877 || [[ ]] || 1878 || [[ ]] ||
1018
|| 1879 || [[ ]] || 1880 || [[ ]] ||
1019
|| 1881 || [[ ]] || 1882 || [[ ]] ||
1020
|| 1883 || [[ ]] || 1884 || [[ ]] ||
1021
|| 1885 || [[ ]] || 1886 || [[ ]] ||
1022
|| 1887 || [[ ]] || 1888 || [[ ]] ||
1023
|| 1889 || [[ ]] || 1890 || [[ ]] ||
1024
|| 1891 || [[ ]] || 1892 || [[ ]] ||
1025
|| 1893 || [[ ]] || 1894 || [[ ]] ||
1026
|| 1895 || [[ ]] || 1896 || [[ ]] ||
1027
|| 1897 || [[ ]] || 1898 || [[ ]] ||
1028
|| 1899 || [[ ]] || 1900 || [[ ]] ||
3695
location.href = 'https://namu.wiki/discuss/%EC%82%AC%EC%9A%A9%EC%9E%90:Accessable'
3696
});
10293697
1030
##1900까지 적으신 분은 문단 제목을 완료로 바꿔주세요.
3698
addItemToMemberMenu('<div class="dropdown-divider"></div>');
10313699
1032
=== 1901 ~ 2000 ===
1033
##단어를 추가하기 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
1034
##한 번에 한 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
1035
|| 1901 || [[ ]] || 1902 || [[ ]] ||
1036
|| 1903 || [[ ]] || 1904 || [[ ]] ||
1037
|| 1905 || [[ ]] || 1906 || [[ ]] ||
1038
|| 1907 || [[ ]] || 1908 || [[ ]] ||
1039
|| 1909 || [[ ]] || 1910 || [[ ]] ||
1040
|| 1911 || [[ ]] || 1912 || [[ ]] ||
1041
|| 1913 || [[ ]] || 1914 || [[ ]] ||
1042
|| 1915 || [[ ]] || 1916 || [[ ]] ||
1043
|| 1917 || [[ ]] || 1918 || [[ ]] ||
1044
|| 1919 || [[ ]] || 1920 || [[ ]] ||
1045
|| 1921 || [[ ]] || 1922 || [[ ]] ||
1046
|| 1923 || [[ ]] || 1924 || [[ ]] ||
1047
|| 1925 || [[ ]] || 1926 || [[ ]] ||
1048
|| 1927 || [[ ]] || 1928 || [[ ]] ||
1049
|| 1929 || [[ ]] || 1930 || [[ ]] ||
1050
|| 1931 || [[ ]] || 1932 || [[ ]] ||
1051
|| 1933 || [[ ]] || 1934 || [[ ]] ||
1052
|| 1935 || [[ ]] || 1936 || [[ ]] ||
1053
|| 1937 || [[ ]] || 1938 || [[ ]] ||
1054
|| 1939 || [[ ]] || 1940 || [[ ]] ||
1055
|| 1941 || [[ ]] || 1942 || [[ ]] ||
1056
|| 1943 || [[ ]] || 1944 || [[ ]] ||
1057
|| 1945 || [[ ]] || 1946 || [[ ]] ||
1058
|| 1947 || [[ ]] || 1948 || [[ ]] ||
1059
|| 1949 || [[ ]] || 1950 || [[ ]] ||
1060
|| 1951 || [[ ]] || 1952 || [[ ]] ||
1061
|| 1953 || [[ ]] || 1954 || [[ ]] ||
1062
|| 1955 || [[ ]] || 1956 || [[ ]] ||
1063
|| 1957 || [[ ]] || 1958 || [[ ]] ||
1064
|| 1959 || [[ ]] || 1960 || [[ ]] ||
1065
|| 1961 || [[ ]] || 1962 || [[ ]] ||
1066
|| 1963 || [[ ]] || 1964 || [[ ]] ||
1067
|| 1965 || [[ ]] || 1966 || [[ ]] ||
1068
|| 1967 || [[ ]] || 1968 || [[ ]] ||
1069
|| 1969 || [[ ]] || 1970 || [[ ]] ||
1070
|| 1971 || [[ ]] || 1972 || [[ ]] ||
1071
|| 1973 || [[ ]] || 1974 || [[ ]] ||
1072
|| 1975 || [[ ]] || 1976 || [[ ]] ||
1073
|| 1977 || [[ ]] || 1978 || [[ ]] ||
1074
|| 1979 || [[ ]] || 1980 || [[ ]] ||
1075
|| 1981 || [[ ]] || 1982 || [[ ]] ||
1076
|| 1983 || [[ ]] || 1984 || [[ ]] ||
1077
|| 1985 || [[ ]] || 1986 || [[ ]] ||
1078
|| 1987 || [[ ]] || 1988 || [[ ]] ||
1079
|| 1989 || [[ ]] || 1990 || [[ ]] ||
1080
|| 1991 || [[ ]] || 1992 || [[ ]] ||
1081
|| 1993 || [[ ]] || 1994 || [[ ]] ||
1082
|| 1995 || [[ ]] || 1996 || [[ ]] ||
1083
|| 1997 || [[ ]] || 1998 || [[ ]] ||
1084
|| 1999 || [[ ]] || 2000 || [[ ]] ||
3700
// 설정 메뉴 추가
3701
addItemToMemberMenu("NamuFix 설정", async function (evt) {
3702
evt.preventDefault();
10853703
1086
##2000지 다 적은 문단 제완료바꿔주세요.
3704
var win = TooSimplePopup();
3705
var elems = {};
3706
win.title('NamuFix 설정');
3707
await SET.load();
3708
win.content(async function (el) {
3709
el.className += " NFSettingsContainer";
3710
el.innerHTML = `<style>.NFSettingsContainer h1 {font-size: 17pt; margin-left: 8px;} .NFSettingsContainer h2 {font-size: 13pt; margin-left: 8px;} .NFSettingsContainer .settings-paragraph {margin-left: 8px;}</style>
3711
<h1>NamuFix 전역</h1>
3712
<div class="settings-paragraph">
3713
<h2>IP정보 조회시 기관명</h2>
3714
<div class="settings-paragraph">
3715
<p>IP정보 조회시에 무슨 기관명을 이용할지 결정합니다. 아래 설정에서 KISA WHOIS 결과에서의 기관명이 선택됐는데 KISA WHOIS 결과가 조회되지 않을 시 자동으로 ipinfo.io에서 조회됩니다. 이 설정은 KISA WHOIS 조회를 제외한 모든 IP정보 조회를 동반한 기능(예: 토론시 익명 기여자 IP주소 조회, 익명 기여자 기여목록에서의 IP 정보 등)에 영향을 끼칩니다.</p>
3716
<input type="radio" name="ipInfoDefaultOrg" data-setname="ipInfoDefaultOrg" data-setvalue="ipinfo.io">ipinfo.io에서 조회 (기본값)<br>
3717
<input type="radio" name="ipInfoDefaultOrg" data-setname="ipInfoDefaultOrg" data-setvalue="KISAuser">KISA WHOIS 결과에서 IP 이용 기관명<br>
3718
<input type="radio" name="ipInfoDefaultOrg" data-setname="ipInfoDefaultOrg" data-setvalue="KISAISP">KISA WHOIS 결과에서 IP 보유 기관명<br>
3719
<input type="radio" name="ipInfoDefaultOrg" data-setname="ipInfoDefaultOrg" data-setvalue="KISAuserOrISP">KISA WHOIS 결과에서 IP 보유 기관명 혹은 IP 이용 기관명<br>
3720
</div>
3721
<h2>동시요청제한</h2>
3722
<div class="settings-paragraph">
3723
관리작업시의 동시요청제한 :
3724
<input type="number" data-setname="adminReqLimit"></input><br>
3725
이미지 업로드시의 동시 요청 제한 :
3726
<input type="number" data-setname="fileUploadReqLimit"></input><br>
3727
IP이용자의 기여목록에서 차단기록 검색 딜레이 :
3728
<input type="number" data-setname="ipBlockHistoryCheckDelay"></input>ms (1000ms=1초)
3729
<br><strong>경고 : 너무 높게 설정하면 reCAPTCHA가 뜹니다.</strong>
3730
</div>
3731
<h2>umi 쿠키</h2>
3732
<div class="settings-paragraph">
3733
<p>NamuFix 실행시 umi 쿠키값을 다음과 같이 변경합니다. 공백으로 설정시 변경하않습니.</p>
3734
<input type="text" data-setname="umiCookie"></input>
3735
</div>
3736
</div>
3737
<h1>토론 편의성</h1>
3738
<div class="settings-paragraph">
3739
<h2>토론 아이덴티콘</h2>
3740
<div class="settings-paragraph">
3741
<input type="radio" name="discussIdenti" data-setname="discussIdenti" data-setvalue="icon">디시라이트 갤러콘 방식<br>
3742
<input type="radio" name="discussIdenti" data-setname="discussIdenti" data-setvalue="headBg">스레딕 헬퍼 방식<br>
3743
<input type="radio" name="discussIdenti" data-setname="discussIdenti" data-setvalue="identicon">아이덴티콘<br>
3744
<input type="radio" name="discussIdenti" data-setname="discussIdenti" data-setvalue="none">사용 안함
3745
</div>
3746
<h2>아이덴티콘 라이브러리</h2>
3747
<div class="settings-paragraph">
3748
<p>참고 : NamuFix에서 특정 사용자의 이메일주소를 조회할 수 없기에 Gravatar, Gravatar(monsterid)는 해당 사용자의 그라바타와 다르게 나옴.</p>
3749
<input type="radio" name="identiconLibrary" data-setname="identiconLibrary" data-setvalue="identicon">stewartlord/identicon.js (GitHub스타일의 아이덴티콘)<br>
3750
<input type="radio" name="identiconLibrary" data-setname="identiconLibrary" data-setvalue="jdenticon">jdenticon (원을 포함하는 여러 도형과 다양한 색으로 이루어진 아이덴티콘)<br>
3751
<input type="radio" name="identiconLibrary" data-setname="identiconLibrary" data-setvalue="gravatar">Gravatar (Gravatar에서 생성되는 기하학 패턴 기반의 아이덴티콘)<br>
3752
<input type="radio" name="identiconLibrary" data-setname="identiconLibrary" data-setvalue="gravatar-monster">Gravatar(monsterid) (Gravatar에서 생성되는 <del style="color: gray;">존나 못생긴</del> 몬스터)
3753
</div>
3754
<h2>토론에서 익명 기여자 IP주소 조회</h2>
3755
<div class="settings-paragraph">
3756
<p>VPNGate 여부, 통사, 국가이미지를 IP 주소 옆에 표시합니다. 요청 수가 많을 시 실패할 수 도 있습니다.</p>
3757
<input type="checkbox" name="lookupIPonDiscuss" data-setname="lookupIPonDiscuss" data-as-boolean>토론시 익명 기여자 IP 주소 조회</input><br>
3758
<input type="checkbox" name="checkWhoisNetTypeOnDiscuss" data-setname="checkWhoisNetTypeOnDiscuss" data-as-boolean>네트워크 유형도 함께 조회 (단 한국 IP만 가능)</input>
3759
</div>
3760
<h2>토론에서 보여지지 않쓰레도 불러오기</h2>
3761
<div class="settings-paragraph">
3762
<p>보여지지 않은 쓰레드도 불러오도록 나무위키 토론 스크립트를 수정합니다.</p>
3763
<input type="checkbox" name="loadUnvisibleReses" data-setname="loadUnvisibleReses" data-as-boolean>보여지지 않은 토론 쓰레도 불러오기</input>
3764
</div>
3765
<h2>토론 아이덴티콘 명도</h2>
3766
<div class="settings-paragraph">
3767
<p>스레딕 헬퍼 방식을 사용하는 경우에만 적용됩니다.</p>
3768
<label for="discussIdentiLightness">명도</label><input name="discussIdentiLightness" data-setname="discussIdentiLightness" type="range" max="1" min="0" step="0.01"><br>
3769
<label for="discussIdentiSaturation">순도</label><input name="discussIdentiSaturation" data-setname="discussIdentiSaturation" type="range" max="1" min="0" step="0.01">
3770
</div>
3771
<h2>토론시 앵커 미리보기</h2>
3772
<div class="settings-paragraph">
3773
<input type="radio" name="discussAnchorPreviewType" data-setname="discussAnchorPreviewType" data-setvalue="0">사용하지 않음<br>
3774
<input type="radio" name="discussAnchorPreviewType" data-setname="discussAnchorPreviewType" data-setvalue="1">마우스를 올리면 미리보기 표시<br>
3775
<input type="radio" name="discussAnchorPreviewType" data-setname="discussAnchorPreviewType" data-setvalue="2">토론 메세지 위에 인용형식으로 표시<br>
3776
<input type="checkbox" name="removeNFQuotesInAnchorPreview" data-setname="removeNFQuotesInAnchorPreview" data-as-boolean>토론 메세지 위에 인용형식으로 표시할때, 인용 안에 인용 형식으로 표시된 미리보기 제거
3777
</div>
3778
<h2>보지 않는 토론 알림</h2>
3779
<div class="settings-paragraph">
3780
<p>보지 않는 토론(예 : 탭 여러개를 여는 경우)에 새로운 발언 혹은 블라인드가 생길시 브라우저 API를 이용해 알림을 띄웁니다.<br>
3781
참고 : 토론에서 보여지지 않은 쓰레도 불러오기 기능이 활성화된 경우 보이지 않은 쓰레들을 불려오는 동안은 알림이 뜨지 않습니다.<br>
3782
경고 : 현재 실험중인 기능입니다.</p>
3783
<input type="checkbox" data-setname="notifyForUnvisibleThreads" data-as-boolean>보지 않는 토론 알림</input>
3784
</div>
3785
<h2>마우스를 올리면 해당 사용자의 다른 쓰레 강조</h2>
3786
<div class="settings-paragraph">
3787
<p>토론에서 쓰레에 마우스를 올리면 그 사용자의 다른 쓰레를 강조합니다.</p>
3788
<input type="checkbox" data-setname="emphasizeResesWhenMouseover" data-as-boolean>마우스를 올리면 해당 사용자의 다른 쓰레 강조</input>
3789
</div>
3790
</div>
3791
<h1>관리 편의성</h1>
3792
<div class="settings-paragraph">
3793
<h2>편의기능</h2>
3794
<div class="settings-paragraph">
3795
<input type="checkbox" name="addAdminLinksForLiberty" data-setname="addAdminLinksForLiberty" data-as-boolean>Liberty 스킨에 관리자 링크 추가하기</input><br>
3796
<input type="checkbox" name="addBatchBlockMenu" data-setname="addBatchBlockMenu" data-as-boolean>일괄 차메뉴 추가</input><br>
3797
<input type="checkbox" name="addQuickBlockLink" data-setname="addQuickBlockLink" data-as-boolean>빠른 차단 링크 추가</input><br>
3798
토론중 빠른차단 기능에서의 차단사유 템플릿 : <input type="text" data-setname="quickBlockReasonTemplate_discuss" style="width: 500px; max-width: 75vw;"></input><br>
3799
역사페이지 빠른차단 기능에서의 차단사유 템플릿 : <input type="text" data-setname="quickBlockReasonTemplate_history" style="width: 500px; max-width: 75vw;"></input><br>
3800
<strong>참고:</strong> 문서명(\${docName})은 URL 인코딩이 되지 않고 리버전 번호(\${revisionNo})는 r로 시작하기 때문에 주소형태의 차단사유 템플릿을 쓰는 것을 권장하지 않습니다.)<br>
3801
빠른차단 기능에서의 차단기간 기본값(초) : <input type="text" data-setname="quickBlockDefaultDuration"></input>
3802
</div>
3803
</div>
3804
<h1>편집 편의성</h1>
3805
<div class="settings-paragraph">
3806
<h2>자동저장 시간 간격</h2>
3807
<div class="settings-paragraph">
3808
<p>편집중 자동저장 간격을 설정합니다. 0 이하의 값으로 설정할 시 자동으로 이루어지지 않으며 이 경우 단축키나 메뉴를 이용해 수동으로 저장해야 합니다.</p>
3809
<input type="number" name="autoTempsaveSpan" data-setname="autoTempsaveSpan"></input>ms (1000ms = 1s)
3810
</div>
3811
<h2>이미지 업로드시 파일이름 유지</h2>
3812
<div class="settings-paragraph">
3813
<p>파일이름에 난수를 덧붙이지 않고 그대로 유지합니다. 파일이름이 중복될시 오류가 발생할 수 있습니다.</p>
3814
<input type="checkbox" data-setname="unprefixedFilename" data-as-boolean>파일이름 그대로 유지</input>
3815
</div>
3816
</div>
3817
<h1>게시판</h1>
3818
<div class="settings-paragraph">
3819
<h2>게시판 시간대 변경</h2>
3820
<div class="settings-paragraph">
3821
<input type="checkbox" name="noLocaltimeOnNamuBoard" data-setname="noLocaltimeOnNamuBoard" data-as-boolean>게시판 시간대를 사용자의 시간대로 자동 변경합니다.</input>
3822
</div>
3823
<h2>댓글 사용구</h2>
3824
<div class="settings-paragraph">
3825
<p>상용구와 상용구는 ,로 구분하며 상용구 이름과 상용구 내용은 :로 구분합니다. (예시: <em>처리중:처리중입니다,기각:기각합니다</em>)</p>
3826
<input type="text" data-setname="commentMacros"></input>
3827
</div>
3828
</div>
3829
<h1>기타</h1>
3830
<div class="settings-paragraph">
3831
<h2>SNS 공유 버튼</h2>
3832
<div class="settings-paragraph">
3833
<input type="checkbox" name="addSnsShareButton" data-setname="addSnsShareButton" data-as-boolean>문서에 트위터/페이스북 공유 버튼을 추가합니다.</input>
3834
</div>
3835
</div>`
3836
var optionTags = document.querySelectorAll('[data-setname]');
3837
await SET.load();
3838
for (var i = 0; i < optionTags.length; i++) {
3839
var tag = optionTags[i];
3840
var t = tag.getAttribute('type');
3841
var sn = tag.dataset.setname;
3842
if (t == "radio" && tag.dataset.setvalue == SET[sn]) {
3843
tag.checked = true;
3844
} else if ((t == "checkbox" && tag.dataset.setvalueOnChecked == SET[sn]) || (t == "checkbox" && tag.hasAttribute("data-as-boolean") && SET[sn])) {
3845
tag.checked = true;
3846
} else if (["text", "password", "number", "range"].indexOf(t) != -1) {
3847
tag.value = ["number", "range"].indexOf(t) != -1 ? Number(SET[sn]) : SET[sn];
3848
}
3849
}
3850
});
3851
win.button('저장하지 않고 닫기', win.close);
3852
win.button('저장하고 닫기', async function () {
3853
var optionTags = document.querySelectorAll('[data-setname]');
3854
await SET.load();
3855
for (var i = 0; i < optionTags.length; i++) {
3856
var tag = optionTags[i];
3857
var t = tag.getAttribute('type');
3858
var sn = tag.dataset.setname;
3859
if (t == "radio" && tag.checked) {
3860
SET[sn] = tag.dataset.setvalue;
3861
} else if (t == "checkbox") {
3862
SET[sn] = tag.hasAttribute("data-as-boolean") ? tag.checked : tag.checked ? t.dataset.setvalueOnChecked : t.dataset.setvalueOnUnchecked;
3863
} else if (["text", "password", "number", "range"].indexOf(t) != -1) {
3864
SET[sn] = tag.value;
3865
}
3866
}
3867
await SET.save();
3868
if (confirm("새로고침해야 설정이 적용됩니다. 새로고침할까요?"))
3869
location.reload();
3870
win.close();
3871
});
3872
win.button('설정 백업', async function () {
3873
let excludeTempsaves = confirm('임시조치를 외할까요?');
3874
let excludes = [];
3875
if (excludeTempsaves) excludes.push('tempsaves');
3876
let backupText = [JSON.stringify(await SET.export(excludes))];
3877
let win = TooSimplePopup();
3878
win.title('백업중...');
3879
win.content(el => el.innerHTML = "백업중...");
3880
let blob = new Blob(backupText, {
3881
type: "application/json"
3882
});
3883
let url = URL.createObjectURL(blob);
3884
win.content(el => el.innerHTML = `<a href="${url}" download="namufix_backup.json">이 링크</a>다른 이름으저장해주세요.`)
3885
win.button('닫기', win.close);
3886
});
3887
win.button('설정 복원', async function () {
3888
getFile(async(files) => {
3889
if (files.length === 0)
3890
return alert('아무것도 선택하지 않았습니다!');
3891
try {
3892
let fileReader = new FileReader();
3893
fileReader.onload = async(evt) => {
3894
try {
3895
let data = JSON.parse(evt.target.result);
3896
await SET.import(data);
3897
alert('완료했습니다. 확인 버튼을 누르면 새로고침됩니다.');
3898
location.reload();
3899
} catch (err) {
3900
return alert('파일을 읽는 중 오류가 발생했습니다: ' + err.message);
3901
console.error(err);
3902
}
3903
}
3904
fileReader.readAsText(files[0]);
3905
} catch (err) {
3906
return alert('파일을 읽는 중 오류가 발생했습니다: ' + err.message);
3907
console.error(err);
3908
}
3909
}, false)
3910
});
3911
win.button('설정 초기화', async function () {
3912
if (confirm('되돌릴 수 없습니다. 계속 진행하시겠습니까?')) {
3913
await SET.load();
3914
for (let i in SET) {
3915
if (i == "save" || i == "load" || i == "delete")
3916
continue;
3917
await SET.delete(i);
3918
}
3919
if (confirm('초기화에 성공했습니다. 확인 버튼을 누르면 새로고침합니다.')) {
3920
location.reload();
3921
}
3922
win.close();
3923
}
3924
});
3925
});
3926
addItemToMemberMenu('NamuFix 이슈트래커', function (evt) {
3927
evt.preventDefault();
10873928
1088
=== 2001 ~ 2100 ===
1089
##단어를 추가하기 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
1090
##한 번에 한 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
1091
|| 2001 || [[ ]] || 2002 || [[ ]] ||
1092
|| 2003 || [[ ]] || 2004 || [[ ]] ||
1093
|| 2005 || [[ ]] || 2006 || [[ ]] ||
1094
|| 2007 || [[ ]] || 2008 || [[ ]] ||
1095
|| 2009 || [[ ]] || 2010 || [[ ]] ||
1096
|| 2011 || [[ ]] || 2012 || [[ ]] ||
1097
|| 2013 || [[ ]] || 2014 || [[ ]] ||
1098
|| 2015 || [[ ]] || 2016 || [[ ]] ||
1099
|| 2017 || [[ ]] || 2018 || [[ ]] ||
1100
|| 2019 || [[ ]] || 2020 || [[ ]] ||
1101
|| 2021 || [[ ]] || 2022 || [[ ]] ||
1102
|| 2023 || [[ ]] || 2024 || [[ ]] ||
1103
|| 2025 || [[ ]] || 2026 || [[ ]] ||
1104
|| 2027 || [[ ]] || 2028 || [[ ]] ||
1105
|| 2029 || [[ ]] || 2030 || [[ ]] ||
1106
|| 2031 || [[ ]] || 2032 || [[ ]] ||
1107
|| 2033 || [[ ]] || 2034 || [[ ]] ||
1108
|| 2035 || [[ ]] || 2036 || [[ ]] ||
1109
|| 2037 || [[ ]] || 2038 || [[ ]] ||
1110
|| 2039 || [[ ]] || 2040 || [[ ]] ||
1111
|| 2041 || [[ ]] || 2042 || [[ ]] ||
1112
|| 2043 || [[ ]] || 2044 || [[ ]] ||
1113
|| 2045 || [[ ]] || 2046 || [[ ]] ||
1114
|| 2047 || [[ ]] || 2048 || [[ ]] ||
1115
|| 2049 || [[ ]] || 2050 || [[ ]] ||
1116
|| 2051 || [[ ]] || 2052 || [[ ]] ||
1117
|| 2053 || [[ ]] || 2054 || [[ ]] ||
1118
|| 2055 || [[ ]] || 2056 || [[ ]] ||
1119
|| 2057 || [[ ]] || 2058 || [[ ]] ||
1120
|| 2059 || [[ ]] || 2060 || [[ ]] ||
1121
|| 2061 || [[ ]] || 2062 || [[ ]] ||
1122
|| 2063 || [[ ]] || 2064 || [[ ]] ||
1123
|| 2065 || [[ ]] || 2066 || [[ ]] ||
1124
|| 2067 || [[ ]] || 2068 || [[ ]] ||
1125
|| 2069 || [[ ]] || 2070 || [[ ]] ||
1126
|| 2071 || [[ ]] || 2072 || [[ ]] ||
1127
|| 2073 || [[ ]] || 2074 || [[ ]] ||
1128
|| 2075 || [[ ]] || 2076 || [[ ]] ||
1129
|| 2077 || [[ ]] || 2078 || [[ ]] ||
1130
|| 2079 || [[ ]] || 2080 || [[ ]] ||
1131
|| 2081 || [[ ]] || 2082 || [[ ]] ||
1132
|| 2083 || [[ ]] || 2084 || [[ ]] ||
1133
|| 2085 || [[ ]] || 2086 || [[ ]] ||
1134
|| 2087 || [[ ]] || 2088 || [[ ]] ||
1135
|| 2089 || [[ ]] || 2090 || [[ ]] ||
1136
|| 2091 || [[ ]] || 2092 || [[ ]] ||
1137
|| 2093 || [[ ]] || 2094 || [[ ]] ||
1138
|| 2095 || [[ ]] || 2096 || [[ ]] ||
1139
|| 2097 || [[ ]] || 2098 || [[ ]] ||
1140
|| 2099 || [[ ]] || 2100 || [[ ]] ||
3929
GM.openInTab("https://github.com/LiteHell/NamuFix/issues");
3930
});
3931
addItemToMemberMenu('KISA WHOIS', function (evt) {
3932
evt.preventDefault();
11413933
1142
##2100까지 적으신 분은 문단 제목을 완료로 바꿔주세요.
3934
whoisPopup(prompt('조회할 IP소를 입력하세요.'));
3935
})
11433936
1144
=== 2101 ~ 2200 ===
1145
##어를 추가하 "반드시" 찾기능을 사용해서 이미 나온어가 아닌지 해주세요!!
1146
##한 번에 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 단어추가하셔도 니다.
1147
|| 2101 || [[ ]] || 2102 || [[ ]] ||
1148
|| 2103 || [[ ]] || 2104 || [[ ]] ||
1149
|| 2105 || [[ ]] || 2106 || [[ ]] ||
1150
|| 2107 || [[ ]] || 2108 || [[ ]] ||
1151
|| 2109 || [[ ]] || 2110 || [[ ]] ||
1152
|| 2111 || [[ ]] || 2112 || [[ ]] ||
1153
|| 2113 || [[ ]] || 2114 || [[ ]] ||
1154
|| 2115 || [[ ]] || 2116 || [[ ]] ||
1155
|| 2117 || [[ ]] || 2118 || [[ ]] ||
1156
|| 2119 || [[ ]] || 2120 || [[ ]] ||
1157
|| 2121 || [[ ]] || 2122 || [[ ]] ||
1158
|| 2123 || [[ ]] || 2124 || [[ ]] ||
1159
|| 2125 || [[ ]] || 2126 || [[ ]] ||
1160
|| 2127 || [[ ]] || 2128 || [[ ]] ||
1161
|| 2129 || [[ ]] || 2130 || [[ ]] ||
1162
|| 2131 || [[ ]] || 2132 || [[ ]] ||
1163
|| 2133 || [[ ]] || 2134 || [[ ]] ||
1164
|| 2135 || [[ ]] || 2136 || [[ ]] ||
1165
|| 2137 || [[ ]] || 2138 || [[ ]] ||
1166
|| 2139 || [[ ]] || 2140 || [[ ]] ||
1167
|| 2141 || [[ ]] || 2142 || [[ ]] ||
1168
|| 2143 || [[ ]] || 2144 || [[ ]] ||
1169
|| 2145 || [[ ]] || 2146 || [[ ]] ||
1170
|| 2147 || [[ ]] || 2148 || [[ ]] ||
1171
|| 2149 || [[ ]] || 2150 || [[ ]] ||
1172
|| 2151 || [[ ]] || 2152 || [[ ]] ||
1173
|| 2153 || [[ ]] || 2154 || [[ ]] ||
1174
|| 2155 || [[ ]] || 2156 || [[ ]] ||
1175
|| 2157 || [[ ]] || 2158 || [[ ]] ||
1176
|| 2159 || [[ ]] || 2160 || [[ ]] ||
1177
|| 2161 || [[ ]] || 2162 || [[ ]] ||
1178
|| 2163 || [[ ]] || 2164 || [[ ]] ||
1179
|| 2165 || [[ ]] || 2166 || [[ ]] ||
1180
|| 2167 || [[ ]] || 2168 || [[ ]] ||
1181
|| 2169 || [[ ]] || 2170 || [[ ]] ||
1182
|| 2171 || [[ ]] || 2172 || [[ ]] ||
1183
|| 2173 || [[ ]] || 2174 || [[ ]] ||
1184
|| 2175 || [[ ]] || 2176 || [[ ]] ||
1185
|| 2177 || [[ ]] || 2178 || [[ ]] ||
1186
|| 2179 || [[ ]] || 2180 || [[ ]] ||
1187
|| 2181 || [[ ]] || 2182 || [[ ]] ||
1188
|| 2183 || [[ ]] || 2184 || [[ ]] ||
1189
|| 2185 || [[ ]] || 2186 || [[ ]] ||
1190
|| 2187 || [[ ]] || 2188 || [[ ]] ||
1191
|| 2189 || [[ ]] || 2190 || [[ ]] ||
1192
|| 2191 || [[ ]] || 2192 || [[ ]] ||
1193
|| 2193 || [[ ]] || 2194 || [[ ]] ||
1194
|| 2195 || [[ ]] || 2196 || [[ ]] ||
1195
|| 2197 || [[ ]] || 2198 || [[ ]] ||
1196
|| 2199 || [[ ]] || 2200 || [[ ]] ||
3937
if (SET.addBatchBlockMenu) {
3938
addItemToMemberMenu('계정/IP 일괄 차단', function (evt) {
3939
evt.preventDefault();
3940
var win = TooSimplePopup();
3941
win.title('계정/IP 일괄 차단');
3942
win.content(function (con) {
3943
var expire = 0;
3944
con.innerHTML = '<p>아래에 차단할 IP주소/계정들을 입력해주세요.' +
3945
'차단 사유 : <input type="text" id="note"></input><br>' +
3946
'차단 기 : <span id="expire_display">영구</span> <a href="#" id="setExpire">(차단 설정)</a>(참고 : 0초 = 영구차)<br>' +
3947
'로그 허용 : <input type="checkbox" id="allowLogin"></input><br>' +
3948
'※ IP 차단 해제에는 차단사유/영구차단 여부/로그인 허용 여부설정할 필요 없으며 IP는 IPv4만 인식합니다.</p>' +
3949
'차단할 계정/IP (개행으로 구분) : <br>' +
3950
'<textarea style="width: 800px; max-width: 80vw; height: 500px; max-height: 80vh;"></textarea>';
3951
con.querySelector('a#setExpire').addEventListener('click', function (evt) {
3952
evt.preventDefault();
3953
enterTimespanPopup('차단기간 설정', function (span) {
3954
if (span === null) {
3955
return alert('입력이 없습니다.');
3956
} else {
3957
expire = span;
3958
con.querySelector('#expire_display').textContent = span == 0 ? '영구' : expire + '초 ';
3959
}
3960
});
3961
})
3962
win.button('취소', win.close);
3963
win.button('VPNGATE IP 불러오기', async function () {
3964
let win = TooSimplePopup();
3965
win.title('불러오는 중');
3966
win.content(e => e.innerHTML = "불러오는 중입니다. 잠시만 기다려주십시오.");
3967
let vpngateIPs = await getVPNGateIPList();
3968
let textarea = con.querySelector('textarea');
3969
textarea.value += '\n' + vpngateIPs.map(v => v + "/32").join("\n");
3970
win.close();
3971
})
3972
win.button('차단기록 검색', async function () {
3973
let searchWin = TooSimplePopup();
3974
let queryInfo;
3975
searchWin.title('차단기록 검색');
3976
searchWin.content(searchWinCon => {
3977
searchWinCon.innerHTML = `
3978
<style>.search-prev[disabled], .search-next[disabled] {background: darkgray; text-decoration: line-through;}</style>
3979
<div class="table-responsive-sm">
3980
<table class="table table-striped table-bordered table-hover table-sm">
3981
<thead>
3982
<tr>
3983
<td>선택</td>
3984
<td>유형</td>
3985
<td>실행자</td>
3986
<td>피실행자</td>
3987
<td>사유</td>
3988
<td>기간</td>
3989
<td>차단일시</td>
3990
</tr>
3991
</thead>
3992
<tbody>
3993
</tbody>
3994
</table>
3995
</div>
3996
<div class="search-pagination">
3997
</div>
3998
`;
11973999
1198
##2200지 다 적으신 분은 문단 제목을 완료로 바꿔주세요.
4000
function processQuery() {
4001
let waitingWin = TooSimplePopup();
4002
waitingWin.title("진행중입니다");
4003
waitingWin.content(waitingWinCon => waitingWinCon.innerHTML = "진행중입니다. 잠시만 기다려주십시오.");
4004
namuapi.searchBlockHistory(queryInfo, (result) => {
4005
queryInfo.from = result.nextResultPageFrom || null;
4006
queryInfo.until = result.prevResultPageUntil || null;
4007
let tbody = searchWinCon.querySelector('tbody');
4008
tbody.innerHTML = "";
4009
for (let i of result) {
4010
// checkbox, type, blocker, blocked, reason, duration, at
4011
tbody.innerHTML += `<tr data-blocked="${encodeHTMLComponent(JSON.stringify(i.blocked))}"><td><input type="checkbox" checked></td><td>${i.type}</td><td>${encodeHTMLComponent(i.blocker)}</td><td>${encodeHTMLComponent(i.blocked)}</td><td>${encodeHTMLComponent(i.reason)}</td><td>${i.duration}</td><td>${formatDateTime(i.at)}</td></tr>`;
4012
}
4013
waitingWin.close();
4014
});
4015
}
4016
searchWin.button('검색', () => {
4017
let queryWin = TooSimplePopup();
4018
queryWin.title('쿼리 입력');
4019
queryWin.content(queryWinCon => {
4020
queryWinCon.innerHTML = `
4021
<div>
4022
쿼리 :
4023
<input type="text" class="search-query" style="width: 500px; max-width: 80vw;"></input>
4024
</div>`;
4025
queryWin.button('실행자 검색', () => {
4026
queryInfo = {
4027
query: queryWinCon.querySelector('.search-query').value,
4028
isAuthor: true
4029
};
4030
processQuery();
4031
});
4032
queryWin.button('내용 검색', () => {
4033
queryInfo = {
4034
query: queryWinCon.querySelector('.search-query').value,
4035
isAuthor: false
4036
};
4037
processQuery();
4038
});
4039
queryWin.button('닫기', queryWin.close);
4040
});
4041
});
4042
searchWin.button('선택된 항목 추가', () => {
4043
let isFirst = true;
4044
let textarea = con.querySelector('textarea');
4045
let waitingWin = TooSimplePopup();
4046
waitingWin.title('진행중입니다.');
4047
waitingWin.content(c => c.innerHTML = "진행중입니다.");
4048
for (let i of searchWinCon.querySelectorAll('tbody tr')) {
4049
if (isFirst) {
4050
textarea.value += '\n';
4051
isFirst = false;
4052
}
4053
if (i.querySelector('input[type="checkbox"]').checked)
4054
textarea.value += JSON.parse(i.dataset.blocked) + "\n";
4055
}
4056
waitingWin.close();
4057
});
4058
searchWin.button('이전 결과', () => {
4059
if (queryInfo.until) {
4060
delete queryInfo.from;
4061
processQuery();
4062
} else {
4063
alert('첫 페이입니다.');
4064
}
4065
});
4066
searchWin.button(' 결과', () => {
4067
if (queryInfo.from) {
4068
delete queryInfo.until;
4069
processQuery();
4070
} else {
4071
alert('마지막 페이지입니다.');
4072
}
4073
});
4074
searchWin.button('닫기', searchWin.close);
4075
});
4076
})
11994077
1200
=== 2201 ~ 2300 ===
1201
##단어를 추가하기 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
1202
##한 번에 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 단어를 추가하셔도 됩니다.
1203
|| 2201 || [[ ]] || 2202 || [[ ]] ||
1204
|| 2203 || [[ ]] || 2204 || [[ ]] ||
1205
|| 2205 || [[ ]] || 2206 || [[ ]] ||
1206
|| 2207 || [[ ]] || 2208 || [[ ]] ||
1207
|| 2209 || [[ ]] || 2210 || [[ ]] ||
1208
|| 2211 || [[ ]] || 2212 || [[ ]] ||
1209
|| 2213 || [[ ]] || 2214 || [[ ]] ||
1210
|| 2215 || [[ ]] || 2216 || [[ ]] ||
1211
|| 2217 || [[ ]] || 2218 || [[ ]] ||
1212
|| 2219 || [[ ]] || 2220 || [[ ]] ||
1213
|| 2221 || [[ ]] || 2222 || [[ ]] ||
1214
|| 2223 || [[ ]] || 2224 || [[ ]] ||
1215
|| 2225 || [[ ]] || 2226 || [[ ]] ||
1216
|| 2227 || [[ ]] || 2228 || [[ ]] ||
1217
|| 2229 || [[ ]] || 2230 || [[ ]] ||
1218
|| 2231 || [[ ]] || 2232 || [[ ]] ||
1219
|| 2233 || [[ ]] || 2234 || [[ ]] ||
1220
|| 2235 || [[ ]] || 2236 || [[ ]] ||
1221
|| 2237 || [[ ]] || 2238 || [[ ]] ||
1222
|| 2239 || [[ ]] || 2240 || [[ ]] ||
1223
|| 2241 || [[ ]] || 2242 || [[ ]] ||
1224
|| 2243 || [[ ]] || 2244 || [[ ]] ||
1225
|| 2245 || [[ ]] || 2246 || [[ ]] ||
1226
|| 2247 || [[ ]] || 2248 || [[ ]] ||
1227
|| 2249 || [[ ]] || 2250 || [[ ]] ||
1228
|| 2251 || [[ ]] || 2252 || [[ ]] ||
1229
|| 2253 || [[ ]] || 2254 || [[ ]] ||
1230
|| 2255 || [[ ]] || 2256 || [[ ]] ||
1231
|| 2257 || [[ ]] || 2258 || [[ ]] ||
1232
|| 2259 || [[ ]] || 2260 || [[ ]] ||
1233
|| 2261 || [[ ]] || 2262 || [[ ]] ||
1234
|| 2263 || [[ ]] || 2264 || [[ ]] ||
1235
|| 2265 || [[ ]] || 2266 || [[ ]] ||
1236
|| 2267 || [[ ]] || 2268 || [[ ]] ||
1237
|| 2269 || [[ ]] || 2270 || [[ ]] ||
1238
|| 2271 || [[ ]] || 2272 || [[ ]] ||
1239
|| 2273 || [[ ]] || 2274 || [[ ]] ||
1240
|| 2275 || [[ ]] || 2276 || [[ ]] ||
1241
|| 2277 || [[ ]] || 2278 || [[ ]] ||
1242
|| 2279 || [[ ]] || 2280 || [[ ]] ||
1243
|| 2281 || [[ ]] || 2282 || [[ ]] ||
1244
|| 2283 || [[ ]] || 2284 || [[ ]] ||
1245
|| 2285 || [[ ]] || 2286 || [[ ]] ||
1246
|| 2287 || [[ ]] || 2288 || [[ ]] ||
1247
|| 2289 || [[ ]] || 2290 || [[ ]] ||
1248
|| 2291 || [[ ]] || 2292 || [[ ]] ||
1249
|| 2293 || [[ ]] || 2294 || [[ ]] ||
1250
|| 2295 || [[ ]] || 2296 || [[ ]] ||
1251
|| 2297 || [[ ]] || 2298 || [[ ]] ||
1252
|| 2299 || [[ ]] || 2300 || [[ ]] ||
4078
function parseTextarea() {
4079
let commonData = {
4080
note: con.querySelector('input#note').value,
4081
expire: expire,
4082
allowLogin: con.querySelector('input#allowLogin').checked
4083
}
4084
return con.querySelector('textarea').value
4085
.split('\n')
4086
.map(v => v.trim())
4087
.filter(v => v != "")
4088
.map((v) => {
4089
let ipWithCIDR = /^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
4090
let data = JSON.parse(JSON.stringify(commonData));
4091
if (ipWithCIDR.test(v))
4092
data.ip = v;
4093
else
4094
data.id = v;
4095
if (data.ip && !data.ip.includes("/")) data.ip += "/32";
4096
return data;
4097
});
4098
}
12534099
1254
##2300까지적으신 분은 단 제목을 완료 바꿔주세요.
4100
function commonLoop(_datas, progressCallback) { // returns error object
4101
return new Promise((resolve, reject) => {
4102
let result = {
4103
errors: [],
4104
success: []
4105
};
4106
let datas = JSON.parse(JSON.stringify(_datas));
4107
async.eachLimit(datas, SET.adminReqLimit, (data, callback) => {
4108
namuapi[data.handlerName](data.parameter, (err, target) => {
4109
if (err) {
4110
result.errors.push({
4111
target: data.parameter,
4112
error: err
4113
});
4114
} else {
4115
result.success.push(data.parameter);
4116
}
4117
if (progressCallback)
4118
progressCallback(data);
4119
callback();
4120
});
4121
}, (err) => {
4122
return resolve(result);
4123
});
4124
});
4125
}
4126
win.button('차단', async function () {
4127
var waitingWin = TooSimplePopup();
4128
var errors = [];
4129
waitingWin.title('처리중');
4130
waitingWin.content(function (wwcon) {
4131
wwcon.innerHTML = "처리중입니다."
4132
});
4133
let datas = parseTextarea().map(v => ({
4134
parameter: v,
4135
handlerName: v.ip ? "blockIP" : "blockAccount"
4136
}));
4137
let result = await commonLoop(datas, d => waitingWin.content(wwcon => wwcon.innerHTML = `처리 완료: ${d.parameter.ip || d.parameter.id}`));
4138
if (result.errors.length > 0) {
4139
waitingWin.content(wwcon => {
4140
wwcon.innerHTML = "오류가 있습니.<br><br>" + result.errors.map(v => `${encodeHTMLComponent(v.target.ip || v.target.id)} : ${v.error}`).join("<br>");
4141
});
4142
waitingWin.button('닫기', waitingWin.close);
4143
} else {
4144
waitingWin.close();
4145
}
4146
});
4147
win.button('차', async function () {
4148
var waitingWin = TooSimplePopup();
4149
var errors = [];
4150
waitingWin.title('처리중');
4151
waitingWin.content(function (wwcon) {
4152
wwcon.innerHTML = "처리중입니다."
4153
});
4154
let datas = parseTextarea().map(v => {
4155
if (v.ip) {
4156
return {
4157
parameter: v.ip,
4158
handlerName: 'unblockIP'
4159
};
4160
} else {
4161
let tmp = {
4162
parameter: v,
4163
handlerName: 'blockAccount'
4164
};
4165
tmp.parameter.expire = -1;
4166
return tmp;
4167
}
4168
});
4169
let result = await commonLoop(datas, d => waitingWin.content(wwcon => wwcon.innerHTML = `처리 완료: ${d.parameter.id ? d.parameter.id : d.parameter}`));
4170
if (result.errors.length > 0) {
4171
waitingWin.content(wwcon => {
4172
wwcon.innerHTML = "오류가 있습니다.<br><br>" + result.errors.map(v => `${encodeHTMLComponent(v.target.id || v.target)} : ${v.error}`).join("<br>");
4173
});
4174
waitingWin.button('닫기', waitingWin.close);
4175
} else {
4176
waitingWin.close();
4177
}
4178
});
4179
});
4180
})
4181
}
12554182
1256
=== 2301 ~ 2400 ===
1257
##단어를 추가하기 전 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
1258
##한 번에 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
1259
|| 2301 || [[ ]] || 2302 || [[ ]] ||
1260
|| 2303 || [[ ]] || 2304 || [[ ]] ||
1261
|| 2305 || [[ ]] || 2306 || [[ ]] ||
1262
|| 2307 || [[ ]] || 2308 || [[ ]] ||
1263
|| 2309 || [[ ]] || 2310 || [[ ]] ||
1264
|| 2311 || [[ ]] || 2312 || [[ ]] ||
1265
|| 2313 || [[ ]] || 2314 || [[ ]] ||
1266
|| 2315 || [[ ]] || 2316 || [[ ]] ||
1267
|| 2317 || [[ ]] || 2318 || [[ ]] ||
1268
|| 2319 || [[ ]] || 2320 || [[ ]] ||
1269
|| 2321 || [[ ]] || 2322 || [[ ]] ||
1270
|| 2323 || [[ ]] || 2324 || [[ ]] ||
1271
|| 2325 || [[ ]] || 2326 || [[ ]] ||
1272
|| 2327 || [[ ]] || 2328 || [[ ]] ||
1273
|| 2329 || [[ ]] || 2330 || [[ ]] ||
1274
|| 2331 || [[ ]] || 2332 || [[ ]] ||
1275
|| 2333 || [[ ]] || 2334 || [[ ]] ||
1276
|| 2335 || [[ ]] || 2336 || [[ ]] ||
1277
|| 2337 || [[ ]] || 2338 || [[ ]] ||
1278
|| 2339 || [[ ]] || 2340 || [[ ]] ||
1279
|| 2341 || [[ ]] || 2342 || [[ ]] ||
1280
|| 2343 || [[ ]] || 2344 || [[ ]] ||
1281
|| 2345 || [[ ]] || 2346 || [[ ]] ||
1282
|| 2347 || [[ ]] || 2348 || [[ ]] ||
1283
|| 2349 || [[ ]] || 2350 || [[ ]] ||
1284
|| 2351 || [[ ]] || 2352 || [[ ]] ||
1285
|| 2353 || [[ ]] || 2354 || [[ ]] ||
1286
|| 2355 || [[ ]] || 2356 || [[ ]] ||
1287
|| 2357 || [[ ]] || 2358 || [[ ]] ||
1288
|| 2359 || [[ ]] || 2360 || [[ ]] ||
1289
|| 2361 || [[ ]] || 2362 || [[ ]] ||
1290
|| 2363 || [[ ]] || 2364 || [[ ]] ||
1291
|| 2365 || [[ ]] || 2366 || [[ ]] ||
1292
|| 2367 || [[ ]] || 2368 || [[ ]] ||
1293
|| 2369 || [[ ]] || 2370 || [[ ]] ||
1294
|| 2371 || [[ ]] || 2372 || [[ ]] ||
1295
|| 2373 || [[ ]] || 2374 || [[ ]] ||
1296
|| 2375 || [[ ]] || 2376 || [[ ]] ||
1297
|| 2377 || [[ ]] || 2378 || [[ ]] ||
1298
|| 2379 || [[ ]] || 2380 || [[ ]] ||
1299
|| 2381 || [[ ]] || 2382 || [[ ]] ||
1300
|| 2383 || [[ ]] || 2384 || [[ ]] ||
1301
|| 2385 || [[ ]] || 2386 || [[ ]] ||
1302
|| 2387 || [[ ]] || 2388 || [[ ]] ||
1303
|| 2389 || [[ ]] || 2390 || [[ ]] ||
1304
|| 2391 || [[ ]] || 2392 || [[ ]] ||
1305
|| 2393 || [[ ]] || 2394 || [[ ]] ||
1306
|| 2395 || [[ ]] || 2396 || [[ ]] ||
1307
|| 2397 || [[ ]] || 2398 || [[ ]] ||
1308
|| 2399 || [[ ]] || 2400 || [[ ]] ||
4183
listenPJAX(mainFunc);
4184
await mainFunc();
13094185
1310
##2400까지 적으 분은 문단 제목완료바꿔주세요.
4186
if (document.querySelector('body').getAttribute('class').indexOf('senkawa') == -1 && document.querySelector('body').getAttribute('class').indexOf('Liberty') == -1) {
4187
await SET.load();
4188
if (!SET.ignoreNonSenkawaWarning) {
4189
var win = TooSimplePopup();
4190
win.title('스킨 관련 안내');
4191
win.content(function (element) {
4192
element.innerHTML = '<p><strong>안내:</strong> NamuFix는 senkawa/liberty 스킨이 아닌 경우 비정상적으 작동할 있습니다.<br>가능하면 senkawa/liberty 스킨사용해주십시오.<br><em>(이 메세지는 한번만 보여집니다.)</em></p>'
4193
});
4194
win.button('확인', win.close);
4195
SET.ignoreNonSenkawaWarning = true;
4196
await SET.save();
4197
}
4198
}
4199
if (GM.info.scriptHandler === "Greasemonkey" && GM.info.version.startsWith("4.") && !SET.ignoreGM4Warning) {
4200
var win = TooSimplePopup();
4201
win.title('Greasemonkey 4와의 호환성 안내');
4202
win.content((container) => container.innerHTML = "<p>Greasemonkey 4+ 버전에서는 NamuFix가 비정상적으작동할 가능성이 <strong>매우</strong> 높습니다. 버그를 작동하면 즉시 이슈트래커에 신고해주세요.</p>");
4203
win.button('닫기', win.close);
4204
SET.ignoreGM4Warning = true;
4205
await SET.save();
4206
}
4207
})();
4208
} catch (err) {
4209
console.error("[NamuFix] 오류 발생!");
4210
console.error(err);
4211
}
13114212
1312
=== 2401 ~ 2500 ===
1313
##단어를 추가하기 전 "반드시" 찾기 기능을 사용해서 이미 나온 단어가 아닌지 확인해주세요!!
1314
##한 번에 한 단어만 추가해 주시기 바랍니다. 만약 10분 뒤에 아무도 추가하지 않았다면 다시 새 단어를 추가하셔도 됩니다.
1315
|| 2401 || [[ ]] || 2402 || [[ ]] ||
1316
|| 2403 || [[ ]] || 2404 || [[ ]] ||
1317
|| 2405 || [[ ]] || 2406 || [[ ]] ||
1318
|| 2407 || [[ ]] || 2408 || [[ ]] ||
1319
|| 2409 || [[ ]] || 2410 || [[ ]] ||
1320
|| 2411 || [[ ]] || 2412 || [[ ]] ||
1321
|| 2413 || [[ ]] || 2414 || [[ ]] ||
1322
|| 2415 || [[ ]] || 2416 || [[ ]] ||
1323
|| 2417 || [[ ]] || 2418 || [[ ]] ||
1324
|| 2419 || [[ ]] || 2420 || [[ ]] ||
1325
|| 2421 || [[ ]] || 2422 || [[ ]] ||
1326
|| 2423 || [[ ]] || 2424 || [[ ]] ||
1327
|| 2425 || [[ ]] || 2426 || [[ ]] ||
1328
|| 2427 || [[ ]] || 2428 || [[ ]] ||
1329
|| 2429 || [[ ]] || 2430 || [[ ]] ||
1330
|| 2431 || [[ ]] || 2432 || [[ ]] ||
1331
|| 2433 || [[ ]] || 2434 || [[ ]] ||
1332
|| 2435 || [[ ]] || 2436 || [[ ]] ||
1333
|| 2437 || [[ ]] || 2438 || [[ ]] ||
1334
|| 2439 || [[ ]] || 2440 || [[ ]] ||
1335
|| 2441 || [[ ]] || 2442 || [[ ]] ||
1336
|| 2443 || [[ ]] || 2444 || [[ ]] ||
1337
|| 2445 || [[ ]] || 2446 || [[ ]] ||
1338
|| 2447 || [[ ]] || 2448 || [[ ]] ||
1339
|| 2449 || [[ ]] || 2450 || [[ ]] ||
1340
|| 2451 || [[ ]] || 2452 || [[ ]] ||
1341
|| 2453 || [[ ]] || 2454 || [[ ]] ||
1342
|| 2455 || [[ ]] || 2456 || [[ ]] ||
1343
|| 2457 || [[ ]] || 2458 || [[ ]] ||
1344
|| 2459 || [[ ]] || 2460 || [[ ]] ||
1345
|| 2461 || [[ ]] || 2462 || [[ ]] ||
1346
|| 2463 || [[ ]] || 2464 || [[ ]] ||
1347
|| 2465 || [[ ]] || 2466 || [[ ]] ||
1348
|| 2467 || [[ ]] || 2468 || [[ ]] ||
1349
|| 2469 || [[ ]] || 2470 || [[ ]] ||
1350
|| 2471 || [[ ]] || 2472 || [[ ]] ||
1351
|| 2473 || [[ ]] || 2474 || [[ ]] ||
1352
|| 2475 || [[ ]] || 2476 || [[ ]] ||
1353
|| 2477 || [[ ]] || 2478 || [[ ]] ||
1354
|| 2479 || [[ ]] || 2480 || [[ ]] ||
1355
|| 2481 || [[ ]] || 2482 || [[ ]] ||
1356
|| 2483 || [[ ]] || 2484 || [[ ]] ||
1357
|| 2485 || [[ ]] || 2486 || [[ ]] ||
1358
|| 2487 || [[ ]] || 2488 || [[ ]] ||
1359
|| 2489 || [[ ]] || 2490 || [[ ]] ||
1360
|| 2491 || [[ ]] || 2492 || [[ ]] ||
1361
|| 2493 || [[ ]] || 2494 || [[ ]] ||
1362
|| 2495 || [[ ]] || 2496 || [[ ]] ||
1363
|| 2497 || [[ ]] || 2498 || [[ ]] ||
1364
|| 2499 || [[ ]] || 2500 || [[ ]] ||
1365
1366
##2500까지 다 적으신 분은 문단 제목을 완료로 바꿔주세요.
1367
1368
[각주]
1369
[include(틀:문서 가져옴, title=알파위키:보존문서/끝말잇기, version=35)]