options.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. var $=document.getElementById.bind(document),
  2. N=$('main'),L=$('sList'),O=$('overlay');
  3. zip.workerScriptsPath='lib/zip.js/';
  4. function split(t){return t.replace(/^\s+|\s+$/g,'').split(/\s*\n\s*/).filter(function(e){return e;});}
  5. // Main options
  6. function allowUpdate(n){return n.update&&(n.custom.updateURL||n.meta.updateURL);}
  7. function setIcon(n,d){
  8. d.src=cache[n.meta.icon]||'images/icon48.png';
  9. }
  10. function modifyItem(d,r){
  11. if(r) {
  12. if(r.message) d.querySelector('.message').innerHTML=r.message;
  13. with(d.querySelector('.update'))
  14. if(r.hideUpdate) classList.add('hide');
  15. else classList.remove('hide');
  16. }
  17. }
  18. function loadItem(o,r){
  19. var d=o.div,n=o.obj;
  20. d.innerHTML='<img class=icon>'
  21. +'<a class="name ellipsis" target=_blank></a>'
  22. +'<span class=author></span>'
  23. +'<span class=version>'+(n.meta.version?'v'+n.meta.version:'')+'</span>'
  24. +'<div class=panelT>'
  25. +(allowUpdate(n)?'<a data=update class=update href=#>'+_('anchorUpdate')+'</a> ':'')
  26. +'<span class=move data=move>&equiv;</span>'
  27. +'</div>'
  28. +'<div class="descrip ellipsis"></div>'
  29. +'<span class=message></span>'
  30. +'<div class=panelB>'
  31. +'<button data=edit>'+_('buttonEdit')+'</button> '
  32. +'<button data=enable>'+_(n.enabled?'buttonDisable':'buttonEnable')+'</button> '
  33. +'<button data=remove>'+_('buttonRemove')+'</button>'
  34. +'</div>';
  35. d.className=n.enabled?'':'disabled';
  36. setIcon(n,d.querySelector('.icon'));
  37. var a=d.querySelector('.name'),b=n.custom.name||n.meta.name;
  38. a.title=b||'';
  39. a.innerHTML=b?b.replace(/&/g,'&amp;').replace(/</g,'&lt;'):'<em>'+_('labelNoName')+'</em>';
  40. if(b=n.custom.homepage||n.meta.homepage) a.href=b;
  41. if(n.meta.author) d.querySelector('.author').innerText=_('labelAuthor')+n.meta.author;
  42. a=d.querySelector('.descrip');
  43. a.innerText=a.title=n.meta.description||'';
  44. modifyItem(d,r);
  45. }
  46. function addItem(o){
  47. o.div=document.createElement('div');
  48. loadItem(o);
  49. L.appendChild(o.div);
  50. }
  51. function moveUp(i,p){
  52. var x=ids[i];
  53. ids[i]=ids[i-1];
  54. ids[i-1]=x;
  55. L.insertBefore(p,p.previousSibling);
  56. }
  57. function getSource(e){
  58. var o=e.target,p,i;
  59. for(p=o;p&&p.parentNode!=L;p=p.parentNode);
  60. i=Array.prototype.indexOf.call(L.childNodes,p);
  61. return [i,p,o];
  62. }
  63. L.onmousedown=function(e){ // for ordering scripts
  64. function moveItem(e){
  65. var m=getSource(e);if(m[0]<0) return;
  66. if(m[0]>=0&&m[0]!=t) {
  67. c=m;m=c[1];if(c[0]>t) m=m.nextSibling;
  68. L.insertBefore(o[1],m);
  69. t=c[0];
  70. }
  71. }
  72. function movedItem(e){
  73. o[1].classList.remove('moving');
  74. L.onmousemove=L.onmouseup=null;
  75. if(o[0]!=t) {
  76. chrome.runtime.sendMessage({cmd:'Move',data:{id:ids[o[0]],offset:t-o[0]}});
  77. var s=t>o[0]?1:-1,i=o[0],x=ids[i];
  78. for(;i!=t;i+=s) ids[i]=ids[i+s];
  79. ids[t]=x;
  80. }
  81. }
  82. var o=getSource(e),d=o[2].getAttribute('data'),c=null,t=o[0];
  83. if(d=='move') {
  84. e.preventDefault();
  85. o[1].classList.add('moving');
  86. L.onmousemove=moveItem;
  87. L.onmouseup=movedItem;
  88. }
  89. };
  90. L.onclick=function(e){
  91. var o=getSource(e),maps={
  92. edit:function(i){
  93. E.cur=map[ids[i]];
  94. chrome.runtime.sendMessage({cmd:'GetScript',data:E.cur.obj.id},gotScript);
  95. },
  96. enable:function(i,p,o){
  97. var e=map[ids[i]].obj;
  98. if(e.enabled=!e.enabled) {
  99. p.classList.remove('disabled');
  100. o.innerText=_('buttonDisable');
  101. } else {
  102. p.classList.add('disabled');
  103. o.innerText=_('buttonEnable');
  104. }
  105. chrome.runtime.sendMessage({cmd:'EnableScript',data:{id:e.id,data:e.enabled}});
  106. },
  107. remove:function(i,p){
  108. chrome.runtime.sendMessage({cmd:'RemoveScript',data:ids[i]});
  109. delete map[ids.splice(i,1)[0]];
  110. L.removeChild(p);
  111. if(i==L.childNodes.length) i--;
  112. },
  113. update:function(i){
  114. chrome.runtime.sendMessage({cmd:'CheckUpdate',data:ids[i]});
  115. }
  116. },d=o[2].getAttribute('data'),f=maps[d];
  117. if(f) {
  118. e.preventDefault();
  119. f.apply(this,o);
  120. }
  121. };
  122. $('bNew').onclick=function(){chrome.runtime.sendMessage({cmd:'NewScript'});};
  123. $('bUpdate').onclick=function(){chrome.runtime.sendMessage({cmd:'CheckUpdateAll'});};
  124. $('cDetail').onchange=function(){L.classList.toggle('simple');chrome.runtime.sendMessage({cmd:'SetOption',data:{key:'showDetails',value:this.checked}});};
  125. var panel=N;
  126. function switchTo(D){
  127. panel.classList.add('hide');D.classList.remove('hide');panel=D;
  128. }
  129. var dialogs=[];
  130. function showDialog(D,z){
  131. if(!dialogs.length) {
  132. O.classList.remove('hide');
  133. setTimeout(function(){O.classList.add('overlay');},1);
  134. }
  135. if(!z) z=dialogs.length?dialogs[dialogs.length-1].zIndex+1:1;
  136. dialogs.push(D);
  137. O.style.zIndex=D.style.zIndex=D.zIndex=z;
  138. D.classList.remove('hide');
  139. D.style.top=(window.innerHeight-D.offsetHeight)/2+'px';
  140. D.style.left=(window.innerWidth-D.offsetWidth)/2+'px';
  141. }
  142. function closeDialog(){
  143. dialogs.pop().classList.add('hide');
  144. if(dialogs.length) O.style.zIndex=dialogs.length>1?dialogs[dialogs.length-1]:1;
  145. else {
  146. O.classList.remove('overlay');
  147. setTimeout(function(){O.classList.add('hide');},500);
  148. }
  149. }
  150. O.onclick=function(){
  151. if(dialogs.length) (dialogs[dialogs.length-1].close||closeDialog)();
  152. };
  153. function confirmCancel(dirty){
  154. return !dirty||confirm(_('confirmNotSaved'));
  155. }
  156. initCSS();initI18n();
  157. // Advanced
  158. var A=$('advanced');
  159. $('bAdvanced').onclick=function(){showDialog(A);};
  160. $('cUpdate').onchange=function(){chrome.runtime.sendMessage({cmd:'AutoUpdate',data:this.checked});};
  161. $('bDefSearch').onclick=function(){$('tSearch').value=_('defaultSearch');};
  162. $('aExport').onclick=function(){showDialog(X);xLoad();};
  163. function importFile(e){
  164. zip.createReader(new zip.BlobReader(e.target.files[0]),function(r){
  165. r.getEntries(function(e){
  166. function getFiles(){
  167. var i=e.shift();
  168. if(i) i.getData(writer,function(t){
  169. var c={code:t};
  170. if(vm.scripts&&(v=vm.scripts[i.filename.slice(0,-8)])) {
  171. c.id=v.id;c.more=v;
  172. }
  173. chrome.runtime.sendMessage({cmd:'ParseScript',data:c});
  174. count++;
  175. getFiles();
  176. }); else {
  177. alert(_('msgImported',[count]));
  178. location.reload();
  179. }
  180. }
  181. var i,vm={},writer=new zip.TextWriter(),count=0;
  182. for(i=0;i<e.length;i++) if(e[i].filename=='ViolentMonkey') break;
  183. if(i<e.length) e.splice(i,1)[0].getData(writer,function(t){
  184. try{
  185. vm=JSON.parse(t);
  186. }catch(e){
  187. vm={};
  188. console.log('Error parsing ViolentMonkey configuration.');
  189. }
  190. getFiles();
  191. }); else getFiles();
  192. });
  193. },function(e){console.log(e);});
  194. }
  195. $('aImport').onclick=function(){
  196. var e=document.createEvent('MouseEvent'),iH=document.createElement('input');
  197. iH.setAttribute('type','file');iH.onchange=importFile;
  198. e.initMouseEvent('click',true,true,window,0,0,0,0,0,false,false,false,false,0,null);
  199. iH.dispatchEvent(e);
  200. };
  201. $('aVacuum').onclick=function(){
  202. this.disabled=true;
  203. this.innerHTML=_('buttonVacuuming');
  204. chrome.runtime.sendMessage({cmd:'Vacuum'});
  205. };
  206. A.close=$('aClose').onclick=function(){
  207. chrome.runtime.sendMessage({cmd:'SetOption',data:{key:'search',value:$('tSearch').value}});
  208. closeDialog();
  209. };
  210. // Export
  211. var X=$('export'),xL=$('xList'),xE=$('bExport'),xC=$('cCompress'),xD=$('cWithData');
  212. function xLoad() {
  213. xL.innerHTML='';xE.disabled=false;xE.innerHTML=_('buttonExport');
  214. ids.forEach(function(i){
  215. var d=document.createElement('div');
  216. d.className='ellipsis';
  217. d.innerText=d.title=map[i].obj.meta.name;
  218. xL.appendChild(d);
  219. });
  220. }
  221. xD.onchange=function(){chrome.runtime.sendMessage({cmd:'SetOption',data:{key:'withData',value:this.checked}});};
  222. xL.onclick=function(e){
  223. var t=e.target;
  224. if(t.parentNode!=this) return;
  225. t.classList.toggle('selected');
  226. };
  227. $('bSelect').onclick=function(){
  228. var c=xL.childNodes,v,i;
  229. for(i=0;i<c.length;i++) if(!c[i].classList.contains('selected')) break;
  230. v=i<c.length;
  231. for(i=0;i<c.length;i++) if(v) c[i].classList.add('selected'); else c[i].classList.remove('selected');
  232. };
  233. X.close=$('bClose').onclick=closeDialog;
  234. function exported(o){
  235. function addFiles(){
  236. adding=true;
  237. if(!writer) { // create writer
  238. zip.createWriter(new zip.BlobWriter(),function(w){writer=w;addFiles();});
  239. return;
  240. }
  241. var i=files.shift();
  242. if(i) {
  243. if(i.name) { // add file
  244. writer.add(i.name,new zip.TextReader(i.content),addFiles);
  245. return;
  246. } else // finished
  247. writer.close(function(b){
  248. var u=URL.createObjectURL(b),e=document.createEvent('MouseEvent'),xH=document.createElement('a');
  249. e.initMouseEvent('click',true,true,window,0,0,0,0,0,false,false,false,false,0,null);
  250. xH.href=u;
  251. xH.download='scripts.zip';
  252. xH.dispatchEvent(e);
  253. writer=null;
  254. X.close();
  255. URL.revokeObjectURL(u);
  256. });
  257. }
  258. adding=false;
  259. }
  260. function addFile(o){
  261. files.push(o);
  262. if(!adding) addFiles();
  263. }
  264. var writer=null,files=[],adding=false,
  265. n,_n,names={},vm={scripts:{},settings:o.settings};
  266. if(xD.checked) vm.values={};
  267. o.data.forEach(function(c){
  268. var j=0;
  269. n=_n=c.custom.name||c.meta.name||'Noname';
  270. while(names[n]) n=_n+'_'+(++j);names[n]=1;
  271. addFile({name:n+'.user.js',content:c.code});
  272. vm.scripts[n]={id:c.id,custom:c.custom,enabled:c.enabled,update:c.update};
  273. if(xD.checked&&(n=o.values[c.uri])) vm.values[c.uri]=n;
  274. });
  275. addFile({name:'ViolentMonkey',content:JSON.stringify(vm)});
  276. addFile({}); // finish adding files
  277. }
  278. xE.onclick=function(e){
  279. e.preventDefault();
  280. this.disabled=true;this.innerHTML=_('labelExporting');
  281. var i,c=[];
  282. for(i=0;i<ids.length;i++)
  283. if(xL.childNodes[i].classList.contains('selected')) c.push(ids[i]);
  284. chrome.runtime.sendMessage({cmd:'ExportZip',data:{values:xD.checked,data:c}},exported);
  285. };
  286. // Script Editor
  287. var E=$('editor'),U=$('eUpdate'),M=$('meta'),
  288. mN=$('mName'),mH=$('mHomepage'),mR=$('mRunAt'),
  289. mU=$('mUpdateURL'),mD=$('mDownloadURL'),
  290. mI=$('mInclude'),mE=$('mExclude'),mM=$('mMatch'),
  291. cI=$('cInclude'),cE=$('cExclude'),cM=$('cMatch'),
  292. eS=$('eSave'),eSC=$('eSaveClose'),T;
  293. CodeMirror.keyMap.vm={
  294. 'Esc':'close',
  295. 'Ctrl-S':'save',
  296. 'fallthrough':'default'
  297. };
  298. T=CodeMirror.fromTextArea($('eCode'),{
  299. lineNumbers:true,
  300. matchBrackets:true,
  301. mode:'text/typescript',
  302. lineWrapping:true,
  303. indentUnit:4,
  304. indentWithTabs:true,
  305. extraKeys:{"Enter":"newlineAndIndentContinueComment"},
  306. keyMap:'vm'
  307. });
  308. T.on('change',function(){eS.disabled=eSC.disabled=T.isClean();});
  309. function gotScript(o){
  310. switchTo(E);E.scr=o;U.checked=o.update;
  311. T.setValue(o.code);T.markClean();T.getDoc().clearHistory();
  312. eS.disabled=eSC.disabled=true;T.focus();
  313. }
  314. function eSave(){
  315. chrome.runtime.sendMessage({
  316. cmd:'ParseScript',
  317. data:{
  318. id:E.scr.id,
  319. code:T.getValue(),
  320. message:'',
  321. more:{
  322. update:E.scr.update=U.checked
  323. }
  324. }
  325. });
  326. T.markClean();eS.disabled=eSC.disabled=true;
  327. }
  328. function eClose(){T.setValue('');switchTo(N);}
  329. U.onchange=E.markDirty=function(){eS.disabled=eSC.disabled=false;};
  330. function metaChange(){M.dirty=true;}
  331. [mN,mH,mR,mU,mD,mI,mM,mE,cI,cM,cE].forEach(function(i){i.onchange=metaChange;});
  332. $('bcustom').onclick=function(){
  333. var e=[],c=E.scr.custom;M.dirty=false;
  334. showDialog(M,10);
  335. mN.value=c.name||'';
  336. mH.value=c.homepage||'';
  337. mU.value=c.updateURL||'';
  338. mD.value=c.downloadURL||'';
  339. switch(c['run-at']){
  340. case 'document-start':mR.value='start';break;
  341. case 'document-body':mR.value='body';break;
  342. case 'document-end':mR.value='end';break;
  343. default:mR.value='default';
  344. }
  345. cI.checked=c._include!=false;
  346. mI.value=(c.include||e).join('\n');
  347. cM.checked=c._match!=false;
  348. mM.value=(c.match||e).join('\n');
  349. cE.checked=c._exclude!=false;
  350. mE.value=(c.exclude||e).join('\n');
  351. };
  352. M.close=function(){if(confirmCancel(M.dirty)) closeDialog();};
  353. $('mCancel').onclick=closeDialog;
  354. $('mOK').onclick=function(){
  355. if(M.dirty) {
  356. var c=E.scr.custom;
  357. c.name=mN.value;
  358. c.homepage=mH.value;
  359. c.updateURL=mU.value;
  360. c.downloadURL=mD.value;
  361. switch(mR.value){
  362. case 'start':c['run-at']='document-start';break;
  363. case 'body':c['run-at']='document-body';break;
  364. case 'end':c['run-at']='document-end';break;
  365. default:delete c['run-at'];
  366. }
  367. c._include=cI.checked;
  368. c.include=split(mI.value);
  369. c._match=cM.checked;
  370. c.match=split(mM.value);
  371. c._exclude=cE.checked;
  372. c.exclude=split(mE.value);
  373. loadItem(E.cur,E.scr);
  374. chrome.runtime.sendMessage({cmd:'SaveScript',data:E.scr});
  375. }
  376. closeDialog();
  377. };
  378. eS.onclick=eSave;
  379. eSC.onclick=function(){eSave();eClose();};
  380. CodeMirror.commands.save=function(){if(!eS.disabled) setTimeout(eSave,0);};
  381. CodeMirror.commands.close=E.close=$('eClose').onclick=function(){if(confirmCancel(!eS.disabled)) eClose();};
  382. // Load at last
  383. var ids=[],map={},cache;
  384. function loadOptions(o){
  385. cache=o.cache;
  386. o.scripts.forEach(function(i){
  387. ids.push(i.id);addItem(map[i.id]={obj:i});
  388. });
  389. $('cUpdate').checked=o.autoUpdate;
  390. $('tSearch').value=o.search;
  391. if(!($('cDetail').checked=o.showDetails)) L.classList.add('simple');
  392. xD.checked=o.withData;
  393. }
  394. function updateItem(r){
  395. if(!('id' in r)) return;
  396. var m=map[r.id];
  397. if(!m) map[r.id]=m={};
  398. if(r.obj) m.obj=r.obj;
  399. switch(r.status){
  400. case 0:loadItem(m,r);break;
  401. case 1:ids.push(r.id);addItem(m);break;
  402. default:modifyItem(m.div,r);
  403. }
  404. }
  405. chrome.runtime.sendMessage({cmd:'GetData'},loadOptions);
  406. chrome.runtime.onMessage.addListener(function(req,src){
  407. var maps={
  408. NewScript: function(o){
  409. ids.push(o.id);addItem(map[o.id]={obj:o});o=map[o.id].div;
  410. },
  411. Vacuumed: function(){$('aVacuum').innerHTML=_('buttonVacuumed');},
  412. },f=maps[req.cmd];
  413. if(f) f(req.data,src);
  414. });
  415. var port=chrome.runtime.connect({name:'Options'});
  416. port.onMessage.addListener(updateItem);