미디어위키:Gadget-DB2-SpecialPage.js
참고: 설정을 저장한 후에 바뀐 점을 확인하기 위해서는 브라우저의 캐시를 새로 고쳐야 합니다.
- 파이어폭스 / 사파리: Shift 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5 또는 Ctrl-R을 입력 (Mac에서는 ⌘-R)
- 구글 크롬: Ctrl-Shift-R키를 입력 (Mac에서는 ⌘-Shift-R)
- 인터넷 익스플로러 / 엣지: Ctrl 키를 누르면서 새로 고침을 클릭하거나, Ctrl-F5를 입력.
- 오페라: Ctrl-F5를 입력.
/**
* DB2로 저장한 키들을 관리하는 페이지 전용 기능들
* 원 저작자: [[사용자:hsl0|hsl0]]
* 코드 관리: [[사용자:Senouis|Senouis]]
**/
var metadataQueue = {};
var metaRqTimeout = null;
function requestMetadata() {
var queue = metadataQueue;
$.get('/w/api.php', {
action: "query",
format: "json",
prop: "revisions",
titles: Object.keys(queue).join('|'),
formatversion: 2,
rvprop: "content",
rvslots: "main"
}).then(function(res) {
res.query.pages.forEach(function(obj) {
queue[obj.title](JSON.parse(obj.revisions[0].slots.main.content));
});
});
metadataQueue = {};
metaRqTimeout = null;
}
function newPage(name, label, prefix) {
function create(res) {
var $table = keyList(keys, res.about, res.loc);
var reload = new OO.ui.ButtonInputWidget({
label: '새로고침',
icon: 'reload'
});
reload.$button.click(function() {
reload.setDisabled(true);
reload.setFlags('pending');
Promise.all([hybridStorage.refresh(), loadAbout()]).then(function(res) {
allkeys = getKeys();
keys = name === 'global'? allkeys.global : allkeys.local[prefix];
page.$element.find('table').remove();
$table = keyList(keys, res[1].about, res[1].loc);
page.$element.append($table);
reload.setDisabled(false);
reload.setFlags({pending: false});
}, notifyApiError.bind(null, '항목을 불러오는 데 실패했습니다.', null));
});
var remove = new OO.ui.ButtonInputWidget({
classes: ['removeall'],
label: '모두 제거',
icon: 'trash',
flags: 'destructive'
});
remove.$button.click(function() {
$table.find('.remove button').click();
if(name === 'global') {
keys = allkeys.global = {};
page.$element.find('table').remove();
$table = keyList(keys);
page.$element.append($table);
} else {
delete allkeys.local[prefix];
layout.removePages([page]);
}
});
/*
remove.$button.click(function() {
OO.ui.confirm('정말로 ' + (name === 'global'? '모든 전역키' : '"' + label + '"의 모든 키') + '를 제거하시겠습니까?').then(function(response) {
var process = null;
var Processfunc = function(){
function resolvedProcess(check, length) {
if (check === length) process.resolved();
}
var check = 0;
process = $.Deferred();
for(var key in keys) {
check++;
hybridStorage.removeItem(keys[key]).then(resolvedProcess(check,keys.length),notifyApiError.bind(null, '항목을 제거하지 못했습니다.', null));
}
return process.promise();
};
var postProcessfunc = function() {
if(name === 'global') {
keys = allkeys.global = {};
page.$element.find('table').remove();
$table = keyList(keys);
page.$element.append($table);
} else {
delete allkeys.local[prefix];
layout.removePages([page]);
}
};
if(response) {
remove.setDisabled(true);
remove.setFlags('pending');
Processfunc().done(function (){
postProcessfunc();
remove.setDisabled(false);
remove.setFlags({pending: false});
});
}
});
});
*/
var revert = new OO.ui.ButtonInputWidget({
classes: ['revertall'],
label: '모두 복원',
icon: 'undo',
flags: 'progressive'
});
revert.$button.click(function() {
$table.find('.removed .revert button').click();
});
page.$element.empty().append([
$('<h3 />').html(name === 'global'? '전역키' : $('<a />', {href: mw.util.getUrl(label), target: '_blank'}).text(label)),
$('<div />', {
class: 'buttons'
}).append(new OO.ui.ButtonGroupWidget({
items: [reload],
classes: ['buttons-left']
}).$element).append(new OO.ui.ButtonGroupWidget({
items: [revert, remove],
classes: ['buttons-right']
}).$element),
$table
]);
}
function loadAbout() {
if(name === 'global') return $.get('/w/index.php?title=틀:DB2/전역키&action=render').then(function(res) {
function process(key) {
var $row = $(res).find('tr:has(td:nth-child(1):contains(' + JSON.stringify(key) +'))');
about[key] = $row.find('td')[1].innerText;
loc[key] = $row.find('td:nth-child(4)');
loc[key].find(':not(a)').contents().unwrap();
loc[key].find('a').filter(function() {
var href = new URL(this.href, location);
href.search = '';
this.href = href;
this.target = '_blank';
return href.origin !== location.origin || !href.pathname.startsWith('/w');
}).contents().unwrap();
loc[key] = loc[key].html();
}
var about = {};
var loc = {};
if(DEFAULT_KEY in keys) process(DEFAULT_KEY);
for(var key in keys) process(key);
return {
about: about,
loc: loc
};
});
else return new Promise(function(resolve) {
if(metaRqTimeout) clearTimeout(metaRqTimeout);
metadataQueue[prefix + '/game.json'] = function(res) {
var about = {};
if(res.gameDB.default) about[DEFAULT_KEY] = res.gameDB.default.description;
if(res.gameDB.keys) for(var key in res.gameDB.keys) {
about[key] = res.gameDB.keys[key].description;
}
resolve({about: about});
};
metaRqTimeout = setTimeout(requestMetadata, 100);
});
}
loadAbout().then(create);
var keys = name === 'global'? allkeys.global : allkeys.local[prefix];
var page = new OO.ui.PageLayout(name, {
content: [new OO.ui.ProgressBarWidget()]
});
page.setupOutlineItem = function() {
this.outlineItem.setLabel(label);
};
return page;
}
function encode(key) {
return encodeURIComponent(key)
.replace(/\./g, '%2E')
.replace(/!/g, '%21')
.replace(/~/g, '%7E')
.replace(/\*/g, '%2A')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/_/g, '__')
.replace(/%/g, '_');
}
function decode(key) {
return decodeURIComponent(key.replace(/_(?=[a-zA-Z0-9]{2})/g, '%').replace(/__/g, '_'));
}
function keyList(keys, abouts, locs) {
function keyRow(key) {
var origin = keys[key];
var val = hybridStorage.getItem(origin);
var about = abouts && abouts[key];
var loc = locs && locs[key];
if(val === null) return;
var copy = new OO.ui.ButtonWidget({
classes: ['copy'],
label: '복사'
});
copy.$button.click(function() {
navigator.clipboard.writeText(val);
});
var remove = new OO.ui.ButtonInputWidget({
classes: ['remove'],
label: '제거',
flags: 'destructive'
});
remove.$button.click(function() {
remove.setDisabled(true);
remove.setFlags('pending');
hybridStorage.removeItem(origin).then(function() {
$element.addClass('removed');
$options.data('sort-value', 0);
remove.$element.addClass('hidden');
revert.$element.removeClass('hidden');
revert.setDisabled(false);
remove.setFlags({pending: false});
}, notifyApiError.bind(null, '항목을 제거하지 못했습니다.', null));
});
var revert = new OO.ui.ButtonInputWidget({
classes: ['revert', 'hidden'],
label: '복원',
flags: 'progressive',
disabled: true
});
revert.$button.click(function() {
revert.setDisabled(true);
revert.setFlags('pending');
hybridStorage.setItem(origin, val).then(function() {
keys[key] = origin;
$element.removeClass('removed');
revert.$element.addClass('hidden');
remove.$element.removeClass('hidden');
remove.setDisabled(false);
revert.setFlags({pending: false});
}, notifyApiError.bind(null, '항목을 복원하지 못했습니다.', null));
});
var $options = $('<td />', {'data-sort-value': 1}).append(copy.$element).append(remove.$element).append(revert.$element);
var $element = $('<tr />', {id: 'key-' + origin})
.append($('<td />').append(key === DEFAULT_KEY? $('<span />', {class: 'key key-default'}).text('기본키') : $('<span />', {class: 'key'}).text(key)))
.append($('<td />', {class: 'about'}).text(about))
.append($('<td />').html($('<pre />', {class: 'content'}).text(val)))
.append(loc && $('<td />', {class: 'location'}).html(loc))
.append($options);
return $element;
}
var rows = [];
if(DEFAULT_KEY in keys) rows.push(keyRow(DEFAULT_KEY));
for(var key in keys) rows.push(keyRow(key));
if(!rows.length) rows.push($('<tr />').append($('<td />', {
colspan: locs? 4 : 5,
class: 'key-empty'
}).text('(키 없음)')));
return $('<table class="wikitable sortable" width="100%" style="left:auto;"/>')
.append('<thead><tr><th>키</th><th>설명</th><th>값</th>' + (locs && '<th>사용처</th>') + '<th>옵션</th></tr></thead>')
.append($('<tbody />').append(rows))//.tablesorter();
}
var fromKey, toKey;
if(mw.user.isAnon()) {
fromKey = function(key) {
return key.slice(7);
};
toKey = function(key) {
return 'gamedb-' + key;
};
} else {
fromKey = function(key) {
return decode(key.slice(7));
};
toKey = function(key) {
return 'gamedb-' + encode(key);
};
}
var layout = new OO.ui.BookletLayout({
outlined: true
});
var DEFAULT_KEY = Symbol('defaultkey');
var allkey, scopes;
function getKeys() {
allkey = Object.getOwnPropertyNames(hybridStorage).filter(function(key) {
return key.startsWith('gamedb-');
}).map(fromKey);
var keys = {global:{}, local:{}};
scopes = new Set();
allkey.forEach(function(key) {
key = key.split('/')[0];
if(!key.startsWith('#') && key.includes(':')) scopes.add(key);
});
// global
allkey.filter(function(key) {
return key.startsWith('#');
}).forEach(function(key) {
keys.global[key.slice(1)] = toKey(key);
});
// local
scopes.forEach(function(scope) {
var current = {};
allkey.filter(function(key) {
return key.startsWith(scope);
}).forEach(function(key) {
key = key.length < scope.length + 1? DEFAULT_KEY : key.slice(scope.length + 1);
current[key] = toKey(key === DEFAULT_KEY? scope : scope + '/' + key);
});
keys.local[scope] = current;
});
return keys;
}
var allkeys = getKeys();
layout.addPages([newPage('global', '전역키')]);
layout.addPages(Array.from(scopes).map(function(prefix) {
return newPage('local-' + prefix, prefix, prefix);
}));
function handleReset(wrong) {
var rand = Math.floor(Math.random() * 999);
rand = '0'.repeat(3 - rand.toString().length) + rand;
OO.ui.prompt($('<p>' + (wrong? '잘못 입력하였습니다.' : 'DB2의 모든 데이터가 초기화됩니다.') + ' 계속하시려면 <b>' + rand + '</b>를 입력해 주세요.</p>'), {
textInput: {
validate: new RegExp(rand)
}
}).then(function(res) {
if(res === rand) layout.$element.find('.removeall').click();
else if(res !== null) handleReset(true);
})
}
$(function() {
try {
var reset = new OO.ui.ButtonInputWidget({
id: 'reset',
label: '전체 초기화',
icon: 'trash',
flags: 'destructive'
});
reset.$button.click(handleReset.bind(null, false));
$('#mw-content-text').html(new OO.ui.PanelLayout({
expanded: false,
framed: true,
content: [layout]
}).$element).prepend($('<div />', {id: 'globalbuttons'}).append(reset.$element));
} catch(err) {
$('#mw-content-text').html($('<span />', {
class: 'error'
}).text(err));
}
});