index.html 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <!--
  2. Copyright (C) 2024 Richard Hao Cao
  3. -->
  4. <!DOCTYPE html><html><head><meta charset="UTF-8">
  5. <style>
  6. html{
  7. height: 100%;
  8. overflow: hidden;
  9. }
  10. body{
  11. display: flex;
  12. flex-direction: column;
  13. height: 100%;
  14. }
  15. div.webviews{
  16. display: flex;
  17. flex-direction: column;
  18. flex-grow:1;
  19. }
  20. webview{display: none;width:100%;height:100%}
  21. .curWV{display: inherit !important;}
  22. </style>
  23. <script>
  24. const fs = require('fs');
  25. const path = require('path');
  26. const readline = require('readline');
  27. var iTab = 0;
  28. var tabs;
  29. var engines = {};
  30. var mapKeys = {};
  31. var closedUrls = [];
  32. var defaultSE = "https://www.bing.com/search?q=%s";
  33. let lastKeys;
  34. let lastKeys_millis = 0;
  35. fs.readFile(path.join(__dirname,'search.json'), 'utf8', (err, jsonString) => {
  36. if (err) return;
  37. initSearchEngines(jsonString,false);
  38. });
  39. fs.readFile(path.join(__dirname,'mapkeys.json'), 'utf8', (err, jsonStr) => {
  40. if (err) return;
  41. try {
  42. mapKeys = JSON.parse(jsonStr);
  43. }catch(e){}
  44. });
  45. appendAutoc_rec(path.join(__dirname,'default.autoc'),null);
  46. appendAutoc_rec(path.join(__dirname,'bookmark.rec'),' ');
  47. function initSearchEngines(jsonStr){
  48. try{
  49. let val1st;
  50. engines=JSON.parse(jsonStr, (key, value)=>{
  51. if(!val1st && !(/^\d+$/u.test(key))) val1st=value;
  52. return value;
  53. });
  54. if(val1st) defaultSE=val1st;
  55. }catch(e){}
  56. }
  57. function save(filePath, u8array){
  58. //alert(Object.prototype.toString.call(u8array))
  59. fs.writeFile (filePath, u8array, (err) => {
  60. if (err) {
  61. console.error (err);
  62. return;
  63. }
  64. });
  65. }
  66. function print2PDF(filePath, options){
  67. tabs.children[iTab].printToPDF(options)
  68. .then(u8array=>save(filePath,u8array));
  69. }
  70. function bookmark(args){//b [filenamestem] url title :bookmark
  71. let bmFileName = "bookmark.rec";
  72. let tab = tabs.children[iTab];
  73. let url = tab.getURL();
  74. if(args.length>1)
  75. bmFileName = args[1]+".rec";
  76. let title = tab.getTitle();
  77. let line = title + " " + url + "\n";
  78. fs.appendFile(path.join(__dirname,bmFileName), line, (err)=>{});
  79. }
  80. function switchTab(i){
  81. let tab = tabs.children[iTab];
  82. if(document.activeElement == tab) tab.blur();
  83. tab.classList.remove('curWV');
  84. iTab = i;
  85. tabs.children[iTab].classList.add('curWV');
  86. }
  87. function newTab(){
  88. var tab = document.createElement('webview');
  89. tab.allowpopups = true;
  90. tabs.appendChild(tab);
  91. }
  92. function tabInc(num){
  93. let nTabs = tabs.children.length;
  94. if(nTabs<2) return;
  95. let i = iTab +num;
  96. if(i>=nTabs) i=0;
  97. switchTab(i);
  98. }
  99. function tabDec(num){
  100. let nTabs = tabs.children.length;
  101. if(nTabs<2) return;
  102. let i = iTab +num;
  103. if(i<0) i=nTabs-1;
  104. switchTab(i);
  105. }
  106. function tabClose(){
  107. let nTabs = tabs.children.length;
  108. if(nTabs<2) return "";//no remain tab
  109. let tab = tabs.children[iTab];
  110. closedUrls.push(tab.getURL());
  111. if(document.activeElement == tab) tab.blur();
  112. tabs.removeChild(tab);
  113. nTabs--;
  114. if(iTab>=nTabs) iTab=iTab-1;
  115. tabs.children[iTab].classList.add('curWV');
  116. return getWinTitle();
  117. }
  118. function getWinTitle(){
  119. let t=tabs.children[iTab];
  120. let title = (iTab+1) + '/' + tabs.children.length;
  121. try{title=title+' '+t.getTitle()+' '+t.getURL()}catch(e){}
  122. return title
  123. }
  124. async function appendAutoc_rec(filename, delimit){
  125. try{
  126. const readInterface = readline.createInterface ({
  127. input: fs.createReadStream (filename, 'utf8'),
  128. });
  129. for await (const line of readInterface) {
  130. let opt = document.createElement('option');
  131. let iS;
  132. if(delimit && (iS=line.lastIndexOf(delimit))>0){
  133. opt.value = line.substring(iS+delimit.length);
  134. opt.textContent = line.substring(0,iS);
  135. }else
  136. opt.value = line;
  137. document.forms[0].children[0].appendChild(opt);
  138. }
  139. }catch(e){return;}
  140. }
  141. function keyPress(e){
  142. var inputE = document.forms[0].q;
  143. if (e.altKey||e.metaKey)
  144. return;
  145. var key = e.key;
  146. if(e.ctrlKey){
  147. switch(key){
  148. case "Home":
  149. tabs.children[iTab].src = "javascript:window.scrollTo(0,0)";
  150. return;
  151. case "End":
  152. tabs.children[iTab].src="javascript:window.scrollTo(0,document.body.scrollHeight)"
  153. return;
  154. }
  155. return;
  156. }
  157. switch(key){
  158. case "PageDown":
  159. tabs.children[iTab].src =
  160. "javascript:window.scrollBy(0,3*document.documentElement.clientHeight/4)";
  161. return;
  162. case "PageUp":
  163. tabs.children[iTab].src = "javascript:window.scrollBy(0,-3*document.documentElement.clientHeight/4)";
  164. return;
  165. case "ArrowDown":
  166. tabs.children[iTab].src="javascript:window.scrollBy(0,32)";
  167. return;
  168. case "ArrowUp":
  169. tabs.children[iTab].src="javascript:window.scrollBy(0,-32)";
  170. return;
  171. }
  172. if(inputE === document.activeElement){
  173. if (9===e.keyCode){//tab completion
  174. }
  175. return;
  176. }
  177. if(1!=key.length) return;
  178. var curMillis = Date.now();
  179. if(curMillis-lastKeys_millis>1000)
  180. lastKeys = null;
  181. lastKeys_millis = curMillis;
  182. switch (key) {
  183. case "!":
  184. inputE.value = ":";
  185. inputE.focus();
  186. lastKeys = null;
  187. return;
  188. case "/":
  189. case ":":
  190. inputE.value = "";
  191. inputE.focus();
  192. lastKeys = null;
  193. return;
  194. }
  195. lastKeys = !lastKeys ? key : lastKeys + key;
  196. let cmds = mapKeys[lastKeys];
  197. if(cmds){//try to run cmds
  198. let keyLen = lastKeys.length;
  199. setTimeout(()=>{
  200. if(lastKeys.length != keyLen) return;
  201. lastKeys = null;
  202. for(var cmd of cmds.split("\n"))
  203. handleQuery(cmd);
  204. }, 500);
  205. }
  206. }
  207. function getQ(){return document.forms[0].q.value;}
  208. function bang(query, iSpace){
  209. let se=defaultSE;
  210. if(iSpace>0){
  211. let name = query.slice(0,iSpace);
  212. let engine = engines[name];
  213. if(engine){
  214. se = engine;
  215. query = query.substring(iSpace+1);
  216. }
  217. }
  218. return se.replace('%s',query);
  219. }
  220. function coloncommand(q){
  221. document.title = q;
  222. }
  223. function coloncommand_render(cmd){
  224. args = cmd.substring(1).split(/\s+/);
  225. switch(args[0]){
  226. case "ac":
  227. autoc(args);
  228. return;
  229. case "b":
  230. bookmark(args);
  231. return;
  232. case "bml":
  233. bml(args);
  234. return;
  235. case "pdf":
  236. savePdf(args);
  237. return;
  238. }
  239. }
  240. function autoc(args){
  241. if(2!=args.length) return;
  242. appendAutoc_rec(path.join(__dirname,args[1]+".rec"),' ');
  243. }
  244. function bml(args){
  245. if(2!=args.length) return;
  246. let filename = args[1]+".js";
  247. fs.readFile(path.join(__dirname,filename), 'utf8', (err,str) => {
  248. if (err) return;
  249. tabs.children[iTab].executeJavaScript(str,false);
  250. });
  251. }
  252. function savePdf(args){
  253. let filename = "ebrowser.pdf";
  254. let options = {};
  255. if(1<args.length){
  256. let c0 = args[1].charCodeAt(0);
  257. let i = 1;
  258. if(123!=c0){//not '{' options then it is filename
  259. filename = args[1] + ".pdf";
  260. i = 2;
  261. }
  262. if(i==args.length-1){//:Pdf [filename] {...}
  263. if(2==args[i].length){// '{}'
  264. let width = document.body.clientWidth/96;
  265. tabs.children[iTab].executeJavaScript("document.documentElement.scrollHeight",
  266. false).then((h)=>{
  267. let opts = {
  268. printBackground:true,
  269. pageSize:{width:width,height:h/96}};
  270. print2PDF(filename,opts);
  271. });
  272. return;
  273. }else{
  274. try {
  275. options = JSON.parse(args[i]);
  276. }catch(e){};
  277. }
  278. }
  279. }
  280. print2PDF(filename,options);
  281. }
  282. function handleQuery(q){
  283. if(q.length>1){
  284. let c0=q.charCodeAt(0);
  285. switch(c0){
  286. case 47://"/"
  287. tabs.children[iTab].findInPage(q.substring(1));
  288. return;
  289. case 58://':'
  290. let c1=q.charCodeAt(1);
  291. if(c1>98 && 112!=c1)
  292. coloncommand(q);
  293. else
  294. coloncommand_render(q);
  295. return;
  296. }
  297. }
  298. var url=q;
  299. do {
  300. if(q.length>7){
  301. let c6 = q.charCodeAt(6);
  302. if(47==c6){// '/'
  303. let c5 = q.charCodeAt(5);
  304. if(47==c5 && 58==q.charCodeAt(4))//http/file urls
  305. break;
  306. if(58==c5 && 47==q.charCodeAt(7))//https://
  307. break;
  308. }else if(q.startsWith("javascript:")){
  309. tabs.children[iTab].executeJavaScript(q.substring(11),false);
  310. return;
  311. }
  312. }
  313. let iS = q.indexOf(' ');
  314. if(iS<0){
  315. if(q.length>5){
  316. if(58==q.charCodeAt(5))
  317. break;
  318. }
  319. if(q.indexOf('.')>0){
  320. url = 'https://'+q;
  321. break;
  322. }
  323. }
  324. url = bang(q, iS);
  325. }while(false);
  326. tabs.children[iTab].src=url;
  327. }
  328. </script>
  329. </head>
  330. <body>
  331. <form action="javascript:handleQuery(getQ())">
  332. <datalist id="autoc"></datalist>
  333. <input type="text" list="autoc" name=q style="width:100%" autofocus></form>
  334. <div class="webviews">
  335. <webview class="curWV" allowpopups></webview>
  336. </div>
  337. <script>
  338. tabs = document.body.children[1];
  339. document.addEventListener('keydown', keyPress);
  340. </script>
  341. </body></html>