options.js 12 KB

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