options.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  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=version>'+(n.meta.version?'v'+n.meta.version:'')+'</span>'
  23. +'<span class=author></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(){
  52. function getSource(e){
  53. var o=e.target,p,i;
  54. for(p=o;p&&p.parentNode!=L;p=p.parentNode);
  55. i=Array.prototype.indexOf.call(L.childNodes,p);
  56. return [i,p,o];
  57. }
  58. function moveItem(e){
  59. var m=getSource(e);if(m[0]<0) return;
  60. if(m[0]>=0&&m[0]!=t) {
  61. e=m;m=e[1];if(e[0]>t) m=m.nextSibling;
  62. L.insertBefore(o[1],m);
  63. t=e[0];
  64. }
  65. }
  66. function movedItem(e){
  67. if(!moving) return;moving=false;
  68. o[1].classList.remove('moving');
  69. L.onmousemove=L.onmouseup=null;L.onmousedown=startMove;
  70. if(o[0]!=t) {
  71. chrome.runtime.sendMessage({cmd:'Move',data:{id:ids[o[0]],offset:t-o[0]}});
  72. var s=t>o[0]?1:-1,i=o[0],x=ids[i];
  73. for(;i!=t;i+=s) ids[i]=ids[i+s];
  74. ids[t]=x;
  75. }
  76. }
  77. function startMove(e){
  78. o=getSource(e);t=o[0];
  79. if(o[2].getAttribute('data')=='move') {
  80. if(moving) return;moving=true;
  81. e.preventDefault();
  82. o[1].classList.add('moving');
  83. L.onmousedown=null;
  84. L.onmousemove=moveItem;
  85. L.onmouseup=movedItem;
  86. }
  87. }
  88. var maps={
  89. edit:function(i){
  90. E.cur=map[ids[i]];
  91. chrome.runtime.sendMessage({cmd:'GetScript',data:ids[i]},gotScript);
  92. },
  93. enable:function(i,p,o){
  94. var e=map[ids[i]].obj;
  95. chrome.runtime.sendMessage({cmd:'UpdateMeta',data:{id:e.id,enabled:!e.enabled?1:0}});
  96. },
  97. remove:function(i,p){
  98. chrome.runtime.sendMessage({cmd:'RemoveScript',data:ids[i]});
  99. delete map[ids.splice(i,1)[0]];
  100. L.removeChild(p);
  101. },
  102. update:function(i){
  103. chrome.runtime.sendMessage({cmd:'CheckUpdate',data:ids[i]});
  104. }
  105. },o,t,moving=false;
  106. L.onmousedown=startMove;
  107. L.onclick=function(e){
  108. var o=getSource(e),d=o[2].getAttribute('data'),f=maps[d];
  109. if(f) {
  110. e.preventDefault();
  111. f.apply(this,o);
  112. }
  113. };
  114. })();
  115. $('bNew').onclick=function(){chrome.runtime.sendMessage({cmd:'NewScript'},function(o){
  116. E.cur=null;gotScript(o);
  117. });};
  118. $('bUpdate').onclick=function(){chrome.runtime.sendMessage({cmd:'CheckUpdateAll'});};
  119. $('cDetail').onchange=function(){L.classList.toggle('simple');chrome.runtime.sendMessage({cmd:'SetOption',data:{key:'showDetails',value:this.checked}});};
  120. var panel=null;
  121. function switchTo(D){
  122. if(panel) panel.classList.add('hide');
  123. D.classList.remove('hide');panel=D;
  124. }
  125. var dialogs=[];
  126. function showDialog(D,z){
  127. if(!dialogs.length) {
  128. O.classList.remove('hide');
  129. setTimeout(function(){O.classList.add('overlay');},1);
  130. }
  131. if(!z) z=dialogs.length?dialogs[dialogs.length-1].zIndex+1:1;
  132. dialogs.push(D);
  133. O.style.zIndex=D.style.zIndex=D.zIndex=z;
  134. D.classList.remove('hide');
  135. D.style.top=(window.innerHeight-D.offsetHeight)/2+'px';
  136. D.style.left=(window.innerWidth-D.offsetWidth)/2+'px';
  137. }
  138. function closeDialog(){
  139. dialogs.pop().classList.add('hide');
  140. if(dialogs.length) O.style.zIndex=dialogs.length>1?dialogs[dialogs.length-1]:1;
  141. else {
  142. O.classList.remove('overlay');
  143. setTimeout(function(){O.classList.add('hide');},500);
  144. }
  145. }
  146. O.onclick=function(){
  147. if(dialogs.length) (dialogs[dialogs.length-1].close||closeDialog)();
  148. };
  149. function confirmCancel(dirty){
  150. return !dirty||confirm(_('confirmNotSaved'));
  151. }
  152. initCSS();initI18n(function(){switchTo(N);});
  153. // Advanced
  154. var A=$('advanced');
  155. $('bAdvanced').onclick=function(){showDialog(A);};
  156. $('cUpdate').onchange=function(){chrome.runtime.sendMessage({cmd:'AutoUpdate',data:this.checked});};
  157. $('bDefSearch').onclick=function(){$('tSearch').value=_('defaultSearch');};
  158. $('tSearch').title=_('hintSearchLink');
  159. $('aExport').onclick=function(){showDialog(X);xLoad();};
  160. function importFile(e){
  161. zip.createReader(new zip.BlobReader(e.target.files[0]),function(r){
  162. r.getEntries(function(e){
  163. function getFiles(){
  164. var i=e.shift();
  165. if(i) i.getData(writer,function(t){
  166. var c={code:t};
  167. if(vm.scripts&&(v=vm.scripts[i.filename.slice(0,-8)])) {
  168. c.id=v.id;c.more=v;
  169. }
  170. chrome.runtime.sendMessage({cmd:'ParseScript',data:c});
  171. count++;
  172. getFiles();
  173. }); else {
  174. alert(_('msgImported',[count]));
  175. location.reload();
  176. }
  177. }
  178. var i,vm={},writer=new zip.TextWriter(),count=0;
  179. for(i=0;i<e.length;i++) if(e[i].filename=='ViolentMonkey') break;
  180. if(i<e.length) e.splice(i,1)[0].getData(writer,function(t){
  181. try{
  182. vm=JSON.parse(t);
  183. }catch(e){
  184. vm={};
  185. console.log('Error parsing ViolentMonkey configuration.');
  186. }
  187. if(vm.values) for(z in vm.values) chrome.runtime.sendMessage({cmd:'SetValue',data:{uri:z,values:vm.values[z]}});
  188. if(vm.settings) for(z in vm.settings) chrome.runtime.sendMessage({cmd:'SetOption',data:{key:z,value:vm.settings[z]}});
  189. getFiles();
  190. }); else getFiles();
  191. });
  192. },function(e){console.log(e);});
  193. }
  194. $('aImport').onclick=function(){
  195. var e=document.createEvent('MouseEvent'),iH=document.createElement('input');
  196. iH.setAttribute('type','file');iH.onchange=importFile;
  197. e.initMouseEvent('click',true,true,window,0,0,0,0,0,false,false,false,false,0,null);
  198. iH.dispatchEvent(e);
  199. };
  200. $('aVacuum').onclick=function(){
  201. this.disabled=true;
  202. this.innerHTML=_('buttonVacuuming');
  203. chrome.runtime.sendMessage({cmd:'Vacuum'});
  204. };
  205. $('aVacuum').title=_('hintVacuumData');
  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.scripts.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=_('buttonExporting');
  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. function markClean(){
  294. T.clearHistory();
  295. eS.disabled=eSC.disabled=true;
  296. }
  297. function gotScript(o){
  298. switchTo(E);E.scr=o;U.checked=o.update;
  299. T.setValueAndFocus(o.code);markClean();
  300. }
  301. function eSave(){
  302. chrome.runtime.sendMessage({
  303. cmd:'ParseScript',
  304. data:{
  305. id:E.scr.id,
  306. code:T.getValue(),
  307. message:'',
  308. more:{
  309. custom:E.scr.custom,
  310. update:E.scr.update=U.checked
  311. }
  312. }
  313. });
  314. markClean();
  315. }
  316. function eClose(){switchTo(N);}
  317. U.onchange=E.markDirty=function(){eS.disabled=eSC.disabled=false;};
  318. function metaChange(){M.dirty=true;}
  319. [mN,mH,mR,mU,mD,mI,mM,mE,cI,cM,cE].forEach(function(i){i.onchange=metaChange;});
  320. $('bcustom').onclick=function(){
  321. var e=[],c=E.scr.custom;M.dirty=false;
  322. showDialog(M,10);
  323. mN.value=c.name||'';
  324. mH.value=c.homepage||'';
  325. mU.value=c.updateURL||'';
  326. mD.value=c.downloadURL||'';
  327. switch(c['run-at']){
  328. case 'document-start':mR.value='start';break;
  329. case 'document-idle':mR.value='idle';break;
  330. case 'document-end':mR.value='end';break;
  331. default:mR.value='default';
  332. }
  333. cI.checked=c._include!=false;
  334. mI.value=(c.include||e).join('\n');
  335. cM.checked=c._match!=false;
  336. mM.value=(c.match||e).join('\n');
  337. cE.checked=c._exclude!=false;
  338. mE.value=(c.exclude||e).join('\n');
  339. };
  340. M.close=function(){if(confirmCancel(M.dirty)) closeDialog();};
  341. $('mCancel').onclick=closeDialog;
  342. $('mOK').onclick=function(){
  343. if(M.dirty) {
  344. var c=E.scr.custom;
  345. c.name=mN.value;
  346. c.homepage=mH.value;
  347. c.updateURL=mU.value;
  348. c.downloadURL=mD.value;
  349. switch(mR.value){
  350. case 'start':c['run-at']='document-start';break;
  351. case 'idle':c['run-at']='document-idle';break;
  352. case 'end':c['run-at']='document-end';break;
  353. default:delete c['run-at'];
  354. }
  355. c._include=cI.checked;
  356. c.include=split(mI.value);
  357. c._match=cM.checked;
  358. c.match=split(mM.value);
  359. c._exclude=cE.checked;
  360. c.exclude=split(mE.value);
  361. E.markDirty();
  362. }
  363. closeDialog();
  364. };
  365. eS.onclick=eSave;
  366. eSC.onclick=function(){eSave();eClose();};
  367. E.close=$('eClose').onclick=function(){if(confirmCancel(!eS.disabled)) eClose();};
  368. initEditor(function(o){T=o;},{save:eSave,exit:E.close,onchange:E.markDirty});
  369. // Load at last
  370. var ids,map,cache;
  371. function loadOptions(o){
  372. ids=[];map={};L.innerHTML='';
  373. cache=o.cache;
  374. o.scripts.forEach(function(i){
  375. ids.push(i.id);addItem(map[i.id]={obj:i});
  376. });
  377. $('cUpdate').checked=o.autoUpdate;
  378. $('tSearch').value=o.search;
  379. if(!($('cDetail').checked=o.showDetails)) L.classList.add('simple');
  380. xD.checked=o.withData;
  381. }
  382. function updateItem(r){
  383. if(!('id' in r)) return;
  384. var m=map[r.id];
  385. if(!m) map[r.id]=m={};
  386. if(r.obj) m.obj=r.obj;
  387. switch(r.status){
  388. case 0:loadItem(m,r);break;
  389. case 1:ids.push(r.id);addItem(m);break;
  390. default:modifyItem(m.div,r);
  391. }
  392. }
  393. chrome.runtime.sendMessage({cmd:'GetData'},loadOptions);
  394. chrome.runtime.onMessage.addListener(function(req,src){
  395. var maps={
  396. Vacuumed: function(){
  397. for(var i=0;i<ids.length;i++) map[ids[i]].obj.position=i+1;
  398. $('aVacuum').innerHTML=_('buttonVacuumed');
  399. },
  400. },f=maps[req.cmd];
  401. if(f) f(req.data,src);
  402. });
  403. var port=chrome.runtime.connect({name:'Options'});
  404. port.onMessage.addListener(updateItem);