Arca base64 autodecoder

Arca.live Base64 auto decoder

当前为 2023-12-23 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Arca base64 autodecoder
  3. // @name:ko 아카라이브 Base64 자동 디코더
  4. // @version 1.203
  5. // @author Laria
  6. // @match https://arca.live/b/*/*
  7. // @description Arca.live Base64 auto decoder
  8. // @description:ko 아카라이브 Base64 자동 복호화 스크립트
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=arca.live
  10. // @license MIT
  11. // @encoding utf-8
  12. // @run-at document-end
  13. // @supportURL https://greasyfork.org/ko/scripts/482577-arca-base64-autodecoder
  14. // @namespace https://greasyfork.org/users/1235854
  15. // @grant GM.getValue
  16. // @grant GM.setValue
  17. // @grant GM.deleteValue
  18. // @grant GM.registerMenuCommand
  19. // @grant GM.unregisterMenuCommand
  20. // ==/UserScript==
  21.  
  22. /*
  23. * == Change log ==
  24. * 1.0 - Release
  25. * 1.1 - Invalid character update (replace -> replaceAll)
  26. * 1.11 - Improved show multiple links
  27. * 1.12 - Show Single links Bugfix
  28. * 1.13 - Bugfix 1.12
  29. * 1.14 - Base64 add padding func
  30. * 1.15 - Add annotation, display improvements
  31. * 1.16 - Display improved - CSS applied
  32. * 1.17 - var safe, max_iter defined (~7, def:3)
  33. * 1.18 - auto update check, log system
  34. * 1.20 - add menu(base64 depth, user-drag auto decoding, hide encoded link, update notify)
  35. * 1.201 - base64 depth extends - 11, temporary disable - drag auto decoding
  36. * 1.202 - improve encoded link click callback, feature block in edit mode, enable drag auto decoding
  37. * 1.203 - add menu(restore defaults)
  38. */
  39.  
  40. //base64 encoded(http:/*, https:/*) string prefix
  41. const regArr = [
  42. /(aHR0cDovL|aHR0cHM6Ly)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 1 time
  43. /(YUhSMGNEb3ZM|YUhSMGNITTZMe)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 2 time
  44. /(WVVoU01HTkViM1pN|WVVoU01HTklUVFpNZ)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 3 time
  45. /(V1ZWb1UwMUhUa1ZpTTFwT|V1ZWb1UwMUhUa2xVVkZwTl)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 4 time
  46. /(VjFaV2IxVXdNVWhVYTFacFRURndU|VjFaV2IxVXdNVWhVYTJ4VlZrWndUb)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 5 time
  47. /(VmpGYVYySXhWWGROVldoVllURmFjRlJVUm5kV|VmpGYVYySXhWWGROVldoVllUSjRWbFpyV25kVW)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 6 time
  48. /(Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVUm1GalJsSlZVbTVrV|Vm1wR1lWWXlTWGhXV0dST1ZsZG9WbGxVU2pSV2JGcHlWMjVrVl)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 7 time
  49. /(Vm0xd1IxbFdXWGxUV0doWFYwZFNUMVpzWkc5V2JHeFZVbTFHYWxKc1NsWlZiVFZyV|Vm0xd1IxbFdXWGxUV0doWFYwZFNUMVpzWkc5V2JHeFZVMnBTVjJKR2NIbFdNalZyVm)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 8 time
  50. /(Vm0weGQxSXhiRmRYV0d4VVYwZG9XRll3WkZOVU1WcHpXa2M1VjJKSGVGWlZiVEZIWVd4S2MxTnNXbFppVkZaeV|Vm0weGQxSXhiRmRYV0d4VVYwZG9XRll3WkZOVU1WcHpXa2M1VjJKSGVGWlZNbkJUVmpKS1IyTkliRmROYWxaeVZt)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 9 time
  51. /(Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFppVkVaSVdWZDRTMk14VG5OWGJGcHBWa1phZ|Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xVmpKS1NHVkdXbFpOYmtKVVZtcEtTMUl5VGtsaVJtUk9ZV3hhZVZad)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 10 time
  52. /(Vm0wd2QyVkhVWGhUV0docFVtMVNXVll3WkRSV1ZsbDNXa2M1V0ZKc2JETlhhMXBQVmxVeFYyTkljRmhoTWsweFZtcEtTMU5IVmtkWGJGcHBWa1ZhU1ZkV1pEUlRNazE0Vkc1T1dHSkdjSEJXYTFwaF|Vm0wd2QyVkhVWGhUV0docFVtMVNXVll3WkRSV1ZsbDNXa2M1V0ZKc2JETlhhMXBQVmxVeFYyTkljRmhoTWsweFZtcEtTMU5IVmtkWGJGcE9ZbXRLVlZadGNFdFRNVWw1Vkd0c2FWSnRVazlaVjNoaFpWWmFk)(\w|=|\+|\/)*(?=[^\+=\w\/])/g, //encoding 11 time
  53. ];
  54.  
  55. //regex prefix - drag
  56. const regInvalid = /[^\w\+\/=]/;
  57.  
  58. //update chk, fail->false
  59. let updateAvailble = true;
  60.  
  61. //auto drag decoding enable status
  62. let draggableActivated = false;
  63.  
  64. //encoded link list, [uuid]: [encoded link]
  65. let encodedList = {};
  66.  
  67. //total decode count
  68. let hindex = 0;
  69.  
  70. //drag function comparison
  71. let lastSelected = document;
  72. let lastSelectedTime = Date.now();
  73.  
  74. //logging prefix, param
  75. const sc_name = '['+GM.info.script.name+']';
  76. const sc_name_upd = '['+GM.info.script.name+'-UPD]';
  77. const sc_name_par = '['+GM.info.script.name+'-PAR]';
  78.  
  79. //script menu parameter, user changeable
  80. let localParameter = {
  81. 'basedepth': {
  82. 'param_name': 'basedepth',
  83. 'name': 'base64 깊이 조절하기 - 현재 값 : 알수없음',
  84. 'desc': '자동 base64 디코딩 깊이를 조절할 수 있습니다.',
  85. 'id': -1,
  86. 'func': menucmd_f_depth,
  87. 'value': 3,
  88. 'def_value': 3,
  89. 'visible': true,
  90. },
  91. 'enclinkhide': {
  92. 'param_name': 'enclinkhide',
  93. 'name': '인코딩된 링크 [보이기/숨기기]',
  94. 'desc': '자동 base64 디코딩 전 인코딩된 링크 표시 여부를 설정할 수 있습니다.',
  95. 'id': -1,
  96. 'func': menucmd_f_enchide,
  97. 'value': false,
  98. 'def_value': false,
  99. 'visible': true,
  100. },
  101. 'draggable': {
  102. 'param_name': 'draggable',
  103. 'name': '드래그 시 자동 디코딩 [켜기/끄기]',
  104. 'desc': '드래그 시 자동으로 드래그한 부분을 base64로 디코딩할지 설정할 수 있습니다.',
  105. 'id': -1,
  106. 'func': menucmd_f_drag,
  107. 'value': false,
  108. 'def_value': false,
  109. 'visible': true,
  110. },
  111. 'updatechk': {
  112. 'param_name': 'chkupd',
  113. 'name': '업데이트 알림 [켜기/끄기]',
  114. 'desc': '새 버전이 나올 시 업데이트 확인 알림을 띄울지 여부를 설정할 수 있습니다.',
  115. 'id': -1,
  116. 'func': menucmd_f_updchk,
  117. 'value': true,
  118. 'def_value': true,
  119. 'visible': true,
  120. },
  121. 'resetdefaults': {
  122. 'param_name': 'none',
  123. 'name': '스크립트 기본값 초기화',
  124. 'desc': '스크립트의 사용자 설정을 초기화하고 설치 상태로 되돌립니다.',
  125. 'id': -1,
  126. 'func': menucmd_f_reset_defaults,
  127. 'value': -1,
  128. 'def_value': -1,
  129. 'visible': true,
  130. },
  131. };
  132.  
  133. //auto add padding - add '=' padding in base64 encoded string
  134. function base64AddPadding(str) {
  135. return str + Array((4 - str.length % 4) % 4 + 1).join('=');
  136. }
  137.  
  138. //base64 decode
  139. const Base64 = {
  140. _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
  141. decode : function (input) {
  142. let output = "";
  143. let chr1, chr2, chr3;
  144. let enc1, enc2, enc3, enc4;
  145. let i = 0;
  146.  
  147. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  148.  
  149. while (i < input.length) {
  150. enc1 = this._keyStr.indexOf(input.charAt(i++));
  151. enc2 = this._keyStr.indexOf(input.charAt(i++));
  152. enc3 = this._keyStr.indexOf(input.charAt(i++));
  153. enc4 = this._keyStr.indexOf(input.charAt(i++));
  154.  
  155. chr1 = (enc1 << 2) | (enc2 >> 4);
  156. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  157. chr3 = ((enc3 & 3) << 6) | enc4;
  158.  
  159. //last bits
  160. output = output + String.fromCharCode(chr1);
  161. if (enc3 != 64) { //=
  162. output = output + String.fromCharCode(chr2);
  163. }
  164. if (enc4 != 64) { //==
  165. output = output + String.fromCharCode(chr3);
  166. }
  167. }
  168.  
  169. output = Base64._utf8_decode(output);
  170. return output;
  171. },
  172. // private method for UTF-8 decoding
  173. _utf8_decode : function (utftext) {
  174. let string = "";
  175. let i = 0;
  176. let c = 0;
  177. let c1 = 0;
  178. let c2 = 0;
  179. let c3 = 0;
  180.  
  181. while ( i < utftext.length ) {
  182. c = utftext.charCodeAt(i);
  183. if (c < 128) {
  184. string += String.fromCharCode(c);
  185. i++;
  186. }
  187. else if((c > 191) && (c < 224)) {
  188. c2 = utftext.charCodeAt(i+1);
  189. string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  190. i += 2;
  191. }
  192. else {
  193. c2 = utftext.charCodeAt(i+1);
  194. c3 = utftext.charCodeAt(i+2);
  195. string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  196. i += 3;
  197. }
  198. }
  199. return string;
  200. }
  201. };
  202.  
  203. //encoded link click callback
  204. function showEncodedLink(event) {
  205. const self = event.currentTarget;
  206. //check already clicked
  207. if (encodedList.hasOwnProperty(self.id)) {
  208. console.log(sc_name,'show encoded link -',encodedList[self.id]);
  209. self.innerHTML = encodedList[self.id];
  210. self.style.color = 'rgb(71 88 188)';
  211. self.title = '디코딩 전 인코딩된 링크입니다.';
  212. //delete encodedList[self.id];
  213. }
  214. return;
  215. }
  216.  
  217. //link area
  218. function createEncodedLink(src) {
  219. return '<span style="font-size: 87.5%;color: rgb(71 188 115);">[ ' + src.toString() + ' ]</span>';
  220. }
  221.  
  222. //encoded link element
  223. function createMaskEncodedLink(src) {
  224. const uuid = 'abad_'+self.crypto.randomUUID();
  225. encodedList[uuid] = src;
  226. return '<span id="' + uuid.toString() + '" title="클릭 시 디코딩 전 인코딩된 링크를 표시합니다.">' + '클릭 시 인코딩된 코드 보기' + '</span>';
  227. }
  228.  
  229. //link creation
  230. function createLink(src, index, url, depth, hidelink = false) {
  231. //n번째 링크 (base64 깊이: 0) [ ABCDEF= / 클릭시 원본~ ]
  232. return '<a href="'+url+'" title="'+url+' (새 창으로 열기)" target="_blank" rel="external nofollow noopener noreferrer">'+index.toString()+'번째 링크 (base64 깊이: '+depth.toString()+')</a> '+(hidelink?createEncodedLink(createMaskEncodedLink(src)):createEncodedLink(src))+'';
  233. }
  234.  
  235. //decode & generate
  236. function replacerGen(numIter) {
  237. return function(source) {
  238. try {
  239. let rstring = ""; //return msg
  240. console.log('\n'+sc_name,'No.',(hindex+1),'encoded link:\n', source.toString()); //source
  241.  
  242. //decode
  243. let converted = Base64.decode(base64AddPadding(source));
  244. //attempt to decode nested base64 encoded string
  245. for(let i=0; i<numIter; i++) {
  246. converted = Base64.decode(base64AddPadding(converted));
  247. }
  248. hindex++;
  249.  
  250. //remove invalid string - �
  251. converted = decodeURI(encodeURI(converted).replaceAll('%00', ''));
  252. console.log(sc_name,'No.',hindex,'decode completed (depth:',numIter+1,'):\n',converted.toString()); //converted
  253.  
  254. //split by new line
  255. converted = converted.split(/\r?\n/);
  256. //single component
  257. if (converted.length == 2 && converted[converted.length-1] == '') {
  258. rstring += createLink(source, hindex, converted[0], numIter+1, !localParameter.enclinkhide.value);
  259. //multiple component
  260. } else if (converted.length > 1) {
  261. rstring += createEncodedLink(localParameter.enclinkhide.value?source.toString():createMaskEncodedLink(source.toString()));
  262.  
  263. let nindex = 1;
  264. const hindex_org = hindex;
  265. converted.forEach(function(i) {
  266. if (i != '') {
  267. rstring += '<br>' + createLink('<span style="color: rgb(71 188 115);" title="자동으로 분할된 '+nindex.toString()+'번째 링크입니다.">링크 자동 분할 : '+nindex.toString()+'번째</span>', hindex, i, numIter+1);
  268. hindex++;
  269. nindex++;
  270. }
  271. });
  272. //apply last components
  273. hindex--;
  274. nindex--;
  275.  
  276. console.log(sc_name,'No.',hindex_org,'- splitted total :', nindex);
  277. rstring = '<span style="color: rgb(232 62 140);"><b><i>분할된 링크 총 '+nindex.toString()+'개</i></b></span> ' + rstring;
  278. } else rstring += createLink(source, hindex, converted, numIter+1, !localParameter.enclinkhide.value);
  279. return rstring;
  280. } catch(e) {
  281. console.warn('\n'+sc_name,'error occured during decoding:', e);
  282. console.warn(sc_name,'base64 decode fail:', source);
  283. }
  284. return '<span style="color: rgb(255 0 0);">[ base64 변환 실패: '+source.toString()+' ]</span>';
  285. };
  286. }
  287.  
  288. //user drag event
  289. function selClicked(event) {
  290. const sel = document.getSelection().toString();
  291. if (!sel.match(regInvalid) && sel.length >= 10 && lastSelectedTime + 200 < Date.now()) {
  292. try {
  293. console.log(sc_name,'live match -',sel.toString());
  294. let converted = decodeURI(encodeURI(Base64.decode(base64AddPadding(sel))).replaceAll('%00', ''));
  295. console.log(sc_name,'converted -',converted.toString());
  296. this.innerHTML = this.innerHTML.replace(sel, converted)+' ';
  297. } catch (e) {
  298. return;
  299. } finally {
  300. this.removeEventListener('click', selClicked);
  301. }
  302. }
  303. }
  304.  
  305. //user drag activate
  306. function activateDragDecoding() {
  307. if(draggableActivated) {
  308. console.log(sc_name,'USR-Drag already enabled.');
  309. return;
  310. }
  311. draggableActivated = true;
  312. console.log(sc_name,'USR-Drag enabled.');
  313. document.addEventListener('selectionchange', function() {
  314. let sel = document.getSelection().anchorNode;
  315. if(sel) {
  316. sel = sel.parentElement;
  317. if(sel != lastSelected) {
  318. lastSelected.removeEventListener('click', selClicked);
  319. sel.addEventListener('click', selClicked);
  320. lastSelected = sel;
  321. lastSelectedTime = Date.now();
  322. }
  323. }
  324. });
  325. }
  326.  
  327. //update check
  328. function checkForUpdate(){
  329. if (!updateAvailble || !localParameter.updatechk.value) {
  330. console.log(sc_name_upd,'updchk skipped.');
  331. return;
  332. }
  333. console.log(sc_name_upd,'checking for update...');
  334. const tar_sc = 'https://update.greasyfork.org/scripts/482577/Arca%20base64%20autodecoder.meta.js';
  335. fetch(tar_sc)
  336. .then(response => response.text())
  337. .then(data => {
  338. //extract version from greaskyfork script
  339. const match = data.match(/@version\s+(\d+\.\d+)/);
  340. if (match) {
  341. const tar_version = parseFloat(match[1]);
  342. const cur_version = parseFloat(GM.info.script.version);
  343. //new version detected
  344. if (tar_version > cur_version) {
  345. console.log(sc_name_upd,'new version available. ('+cur_version+' -> '+tar_version+')');
  346. //y/n dialog
  347. if (window.confirm(sc_name+'\n새로운 버전이 감지되었습니다. 업데이트를 권장합니다.\n( 기존버전 : '+cur_version+', 새로운 버전 : '+tar_version+' )\n(변경사항은 아카라이브 게시글을 참고해주세요.)\n\n취소를 누르면 앞으로 업데이트 알림을 띄우지 않습니다.')) {
  348. //get extension env
  349. if(!GM.info.scriptWillUpdate) {
  350. console.log(sc_name_upd,'extension not allowed auto update..');
  351. if (window.confirm(sc_name+'\n주의! 스크립트 내용 변경 등으로 인해 확장프로그램 내 자동 업데이트가 꺼져있는 것 같습니다.\n업데이트 시 기존 스크립트에 덮어쓰게 되어 기존 내용이 손실될 수 있습니다.\n이 점 확인 후 업데이트 바랍니다.\n\n(계속하려면 확인, 취소하려면 취소를 눌러주세요.)')) {
  352. console.log(sc_name_upd,'opening source url..');
  353. window.location.replace(tar_sc);
  354. window.alert(sc_name+'\n업데이트 후 새로고침해야 적용됩니다.');
  355. } else {
  356. console.log(sc_name_upd,"user canceled.");
  357. }
  358. } else {
  359. console.log(sc_name_upd,'opening source url..');
  360. window.location.replace(tar_sc);
  361. window.alert(sc_name+'\n업데이트 후 새로고침해야 적용됩니다.');
  362. }
  363. } else {
  364. console.log(sc_name_par,'updatechk change',true.toString(),'to',false.toString());
  365. localParameter.updatechk.value = false;
  366. try {
  367. GM.setValue('chkupd', false);
  368. console.log(sc_name_par,"updatechk change successful");
  369. menucmd_update();
  370. window.alert(sc_name+'\n앞으로 업데이트 알림을 띄우지 않습니다.');
  371. } catch(e) {
  372. localParameter.updatechk.value = true;
  373. console.error(sc_name_par,"updatechk change fail -", e);
  374. window.alert(sc_name+'\n파라미터 변경 중 문제 발생, 로그를 확인해주세요..');
  375. }
  376. }
  377. } else {
  378. console.log(sc_name_upd,'latest version', cur_version, 'detected. (eth:',tar_version,')');
  379. }
  380. } else {
  381. console.error(sc_name_upd,'unable to extract version..');
  382. }
  383. })
  384. .catch(error => {
  385. updateAvailble = false;
  386. console.error(sc_name_upd,'link unreachable.. -', error);
  387. });
  388. updateAvailble = false;
  389. }
  390.  
  391. function menucmd_update(fist_run = false) {
  392. //pre process
  393. localParameter.basedepth.value = localParameter.basedepth.value > regArr.length ? regArr.length : localParameter.basedepth.value;
  394.  
  395. //update menu name
  396. localParameter.basedepth.name = 'base64 깊이 조절하기 - 현재 값 : '+localParameter.basedepth.value+'회';
  397. localParameter.enclinkhide.name = '인코딩된 링크 '+(localParameter.enclinkhide.value?'숨기기':'보이기');
  398. localParameter.draggable.name = '드래그 시 자동 디코딩 '+(localParameter.draggable.value?'끄기':'켜기');
  399. localParameter.updatechk.name = '업데이트 알림 '+(localParameter.updatechk.value?'끄기':'켜기');
  400.  
  401. //remove exist menu cmd
  402. if (!fist_run) {
  403. Object.keys(localParameter).forEach(function(i){
  404. try {
  405. GM.unregisterMenuCommand(localParameter[i].id);
  406. } catch(_) {}
  407. });
  408. }
  409. //monkey menu cmd register
  410. try {
  411. Object.keys(localParameter).forEach(function(i){
  412. if (localParameter[i].visible) {
  413. localParameter[i].id = GM.registerMenuCommand(localParameter[i].name, localParameter[i].func, {title:localParameter[i].desc});
  414. } else {
  415. localParameter[i].value = localParameter[i].def_value;
  416. }
  417. });
  418. console.log(sc_name_par,'ext opt pannel',(fist_run?'registered':'reloaded'));
  419. } catch(e) {
  420. console.error(sc_name_par,'err - ext opt pannel',(fist_run?'register':'reload'),'- ', e);
  421. Object.keys(localParameter).forEach(function(i){
  422. try {
  423. GM.unregisterMenuCommand(localParameter[i].id);
  424. } catch(_) {}
  425. });
  426. }
  427. }
  428.  
  429. function menucmd_f_depth() {
  430. menucmd_update();
  431. const prev_value = localParameter.basedepth.value;
  432. const str_common_1 = ' ( 지정 가능한 범위: 1~'+regArr.length.toString()+' )';
  433. while(true) {
  434. const input = window.prompt(sc_name+'\nBase64 자동 디코딩 중첩 횟수를 얼마로 지정할까요?\n(인코딩을 인코딩한 것을 여러번 반복한걸 자동으로 풀어냅니다.)\n현재 값: '+prev_value.toString()+'회,'+(prev_value == 3 ? '' : ' 기본값: 3회,')+str_common_1+'\n\n(값을 너무 크게 지정하면 컴퓨터 성능에 영향을 줄 수 있습니다.)', prev_value);
  435. if (input == null) {
  436. console.log(sc_name,'basedepth change canceled.');
  437. break;
  438. }
  439. if(!isNaN(input)){
  440. const tar_value = parseInt(input);
  441. if(tar_value == prev_value) {
  442. window.alert(sc_name+'\n동일한 값을 입력했습니다, 현재 값: '+prev_value+'회');
  443. } else if(tar_value >= 1 && tar_value <= regArr.length) {
  444. console.log(sc_name_par,'basedepth change',prev_value.toString(),'to',tar_value.toString());
  445. localParameter.basedepth.value = tar_value;
  446. try {
  447. GM.setValue('basedepth', tar_value);
  448. console.log(sc_name_par,"basedepth change successful");
  449. window.alert(sc_name+'\n값이 '+prev_value.toString()+'에서 '+tar_value.toString()+'으로 변경이 완료되었습니다.\n\n(사이트를 새로고침해야 반영됩니다.)');
  450. } catch(e) {
  451. localParameter.basedepth.value = prev_value;
  452. console.error(sc_name_par,"basedepth change fail -", e);
  453. window.alert(sc_name+'\n파라미터 변경 중 문제 발생, 로그를 확인해주세요..');
  454. } finally {
  455. menucmd_update();
  456. }
  457. break;
  458. } else {
  459. window.alert(sc_name+'\n'+tar_value+'(으)로 설정할 수 없습니다.\n범위를 초과하였습니다..'+str_common_1);
  460. }
  461. } else {
  462. window.alert(sc_name+'\n'+input+'은(는)숫자가 아닙니다.\n숫자만 입력해주세요..'+str_common_1);
  463. }
  464. }
  465. }
  466.  
  467. function menucmd_f_enchide() {
  468. menucmd_update();
  469. const curr_state = localParameter.enclinkhide.value;
  470. if(window.confirm(sc_name+'\n디코딩 시 인코딩된 링크를 '+(curr_state?'숨기시':'표시하')+'겠습니까?\n\n(앞으로 디코딩 전 인코딩된 링크를\n"'+(curr_state?'클릭 시 기존링크 보기':'aHR0cHM6Ly9hcmNhLmx..')+'"와 같은 형태로 보여줍니다.)')) {
  471. const set_state = !curr_state;
  472. console.log(sc_name_par,'enchide change',curr_state.toString(),'to',set_state.toString());
  473. localParameter.enclinkhide.value = set_state;
  474. try {
  475. GM.setValue('enclinkhide', set_state);
  476. console.log(sc_name_par,"updatechk change successful");
  477. if(set_state) {
  478. window.alert(sc_name+'\n앞으로 인코딩된 링크를 표시합니다.\n\n(새로고침해야 적용됩니다.)');
  479. } else {
  480. window.alert(sc_name+'\n앞으로 인코딩된 링크를 숨깁니다.');
  481. }
  482. } catch(e) {
  483. localParameter.enclinkhide.value = curr_state;
  484. console.error(sc_name_par,"enchide change fail -", e);
  485. window.alert(sc_name+'\n파라미터 변경 중 문제 발생, 로그를 확인해주세요..');
  486. } finally {
  487. menucmd_update();
  488. }
  489. } else {
  490. console.log(sc_name,'enchide change canceled.');
  491. }
  492. }
  493.  
  494. function menucmd_f_drag() {
  495. menucmd_update();
  496. const curr_state = localParameter.draggable.value;
  497. if(window.confirm(sc_name+'\n드래그 시 자동 디코딩을 '+(curr_state?'비':'')+'활성화 하시겠습니까?\n\n(앞으로 인코딩된 부분을 드래그'+(curr_state?'해도 자동으로 디코딩되지 않습':' 시 Base64로 인코딩된것으로\n판단 되면 자동으로 디코딩을 시도합')+'니다.)\n\n(이 기능은 작동이 불안정할 수 있습니다.)')) {
  498. const set_state = !curr_state;
  499. console.log(sc_name_par,'draggable change',curr_state.toString(),'to',set_state.toString());
  500. localParameter.draggable.value = set_state;
  501. try {
  502. GM.setValue('draggable', set_state);
  503. console.log(sc_name_par,"draggable change successful");
  504. if(set_state) {
  505. try {
  506. activateDragDecoding();
  507. window.alert(sc_name+'\n앞으로 드래그 시 자동 디코딩을 진행합니다.');
  508. } catch(e) {
  509. console.error(sc_name,"draggable activate fail -", e);
  510. window.alert(sc_name+'\n드래그 시 자동 디코딩 활성화 중 문제가 발생했습니다.\n새로고침이 필요합니다..');
  511. }
  512. } else {
  513. window.alert(sc_name+'\n앞으로 드래그 해도 반응하지 않습니다.\n\n(새로고침해야 적용됩니다.)');
  514. }
  515. } catch(e) {
  516. localParameter.draggable.value = curr_state;
  517. console.error(sc_name_par,"draggable change fail -", e);
  518. window.alert(sc_name+'\n파라미터 변경 중 문제 발생, 로그를 확인해주세요..');
  519. } finally {
  520. menucmd_update();
  521. }
  522. } else {
  523. console.log(sc_name,'draggable change canceled.');
  524. }
  525. }
  526.  
  527. function menucmd_f_updchk() {
  528. menucmd_update();
  529. const curr_state = localParameter.updatechk.value;
  530. if(window.confirm(sc_name+'\n업데이트 알림을 '+(curr_state?'끄':'켜')+'시겠습니까?\n\n(앞으로 업데이트가 있'+(curr_state?'어도 알려주지 않습':'으면 자동으로 알려줍')+'니다.)')) {
  531. const set_state = !curr_state;
  532. console.log(sc_name_par,'updatechk change',curr_state.toString(),'to',set_state.toString());
  533. localParameter.updatechk.value = set_state;
  534. try {
  535. GM.setValue('chkupd', set_state);
  536. console.log(sc_name_par,"updatechk change successful");
  537. if(set_state) {
  538. window.alert(sc_name+'\n앞으로 업데이트가 존재하면 알림을 띄웁니다.');
  539. } else {
  540. window.alert(sc_name+'\n앞으로 업데이트 알림을 띄우지 않습니다.');
  541. }
  542. } catch(e) {
  543. localParameter.updatechk.value = curr_state;
  544. console.error(sc_name_par,"updatechk change fail -", e);
  545. window.alert(sc_name+'\n파라미터 변경 중 문제 발생, 로그를 확인해주세요..');
  546. } finally {
  547. menucmd_update();
  548. }
  549. } else {
  550. console.log(sc_name,'updatechk change canceled.');
  551. }
  552. }
  553.  
  554. function menucmd_f_reset_defaults() {
  555. menucmd_update();
  556. if(window.confirm(sc_name+'\n정말 스크립트 설정을 기본값으로 초기화하시겠습니까?\n\n(초기화 완료 후 자동으로 새로고침됩니다.)')) {
  557. try {
  558. console.log(sc_name_par, 'remove all settings..');
  559. for (const i of Object.keys(localParameter)) {
  560. GM.deleteValue(localParameter[i].param_name);
  561. }
  562. console.log(sc_name_par, 'all parameter removed.');
  563. window.alert(sc_name+'\n설정값이 모두 제거되었습니다.\n\n(확인 후 현재 창이 자동으로 새로고침됩니다.)');
  564. window.location.reload(true);
  565. } catch(e) {
  566. console.error(sc_name_par,'err - get sc parameter - ', e);
  567. window.alert(sc_name+'\n경고! 파라미터 초기화 도중 문제가 발생했습니다. 로그를 참고해주세요..');
  568. }
  569. } else {
  570. console.log(sc_name,'settings restore canceled.');
  571. }
  572. }
  573.  
  574. //main
  575. (async () => {
  576. 'use strict';
  577.  
  578. //chk browser env
  579. if (((navigator.language || navigator.userLanguage) != 'ko-KR')) console.warn('Warning! this script support only korean language..');
  580.  
  581. let url_tail = window.location.href.match(/([/][a-z0-9_-]*[\/]?)$/g);
  582. if(url_tail != null) {
  583. if(url_tail[0] == '/edit') {
  584. console.log(sc_name,'edit mode detected, function disabled.');
  585. try {GM.registerMenuCommand("Edit 모드에서는 작동하지 않음", ()=>{window.alert(sc_name+'\n작성 또는 수정모드에서는 작동하지 않습니다..');});} catch(_) {}
  586. return;
  587. }
  588. }
  589.  
  590. console.log(sc_name,'V',GM.info.script.version,'enabled');
  591.  
  592. //load parameter
  593. try {
  594. for (const i of Object.keys(localParameter)) {
  595. localParameter[i].value = await GM.getValue(localParameter[i].param_name, localParameter[i].def_value);
  596. }
  597. console.log(sc_name_par, 'parameter load successful.');
  598. } catch(e) {
  599. console.error(sc_name_par,'err - get sc parameter - ', e);
  600. }
  601.  
  602. //apply parameter and register monkey menu command
  603. menucmd_update(true);
  604.  
  605. //chk update
  606. await checkForUpdate();
  607.  
  608. //drag auto decoding
  609. if (localParameter.draggable.value) {
  610. activateDragDecoding();
  611. }
  612.  
  613. console.log(sc_name,'ready');
  614. //main procedure
  615.  
  616. //article
  617. let article = document.getElementsByClassName("article-content")[0];
  618. if (article != undefined) {
  619. for(let i=0; i<localParameter.basedepth.value; i++) {
  620. article.innerHTML = article.innerHTML.replaceAll(regArr[i], replacerGen(i));
  621. }
  622. } else console.warn(sc_name,'no article detected.');
  623.  
  624. //comment
  625. let comments = document.getElementsByClassName("list-area");
  626. if (article != undefined) {
  627. if (comments.length != 0) {
  628. for(let i=0; i<localParameter.basedepth.value; i++) {
  629. comments[0].innerHTML = comments[0].innerHTML.replaceAll(regArr[i], replacerGen(i));
  630. }
  631. }
  632. } else console.warn(sc_name,'no comments detected.');
  633. console.log(sc_name,'total',hindex,'link decode task completed.');
  634.  
  635. //add eventlistner - click, show original encoded link
  636. if (!localParameter.enclinkhide.value) {
  637. Object.keys(encodedList).forEach(function(i) {
  638. document.getElementById(i).addEventListener('click', showEncodedLink, { once : true } );
  639. });
  640. }
  641. })();