index.html 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="apple-mobile-web-app-capable" content="yes" />
  6. <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
  7. <meta name="theme-color" content="#4b75ff">
  8. <link rel="stylesheet" href="../script/semantic/semantic.min.css">
  9. <script src="../script/jquery.min.js"></script>
  10. <script src="../script/ao_module.js"></script>
  11. <script src="../script/semantic/semantic.min.js"></script>
  12. <link rel="icon" type="image/png" href="img/module_icon.png">
  13. <link rel="manifest" crossorigin="use-credentials" href="manifest.json">
  14. <title>Browser</title>
  15. <style>
  16. body{
  17. overflow: hidden;
  18. }
  19. #urlbar{
  20. padding-top: 0.2em;
  21. padding-bottom: 0.2em;
  22. padding-left: 1.2em;
  23. padding-right: 0.3em;
  24. line-height: 1em;
  25. }
  26. .menuitem{
  27. margin-top: 0.2em;
  28. padding:0.2em !important;
  29. border: 0px !important;
  30. }
  31. .menuitem button{
  32. background-color: white !important;
  33. }
  34. .rightMenuItem{
  35. margin-top: 0.3em;
  36. padding:0.1em !important;
  37. border: 0px !important;
  38. }
  39. .rightMenuItem button:not(.inverted){
  40. background-color: white !important;
  41. }
  42. #starBtn{
  43. padding-right: 0.9em;
  44. cursor: pointer !important;
  45. background-color: white !important;
  46. }
  47. #starBtn:hover{
  48. opacity: 0.7;
  49. }
  50. #xframe{
  51. width: 100%;
  52. height: calc(100% - 43px);
  53. border: 0px solid transparent;
  54. }
  55. #notvdiWarning{
  56. position: fixed;
  57. bottom: 0px;
  58. left: 0px;
  59. width: 100%;
  60. padding: 0.4em;
  61. display: none;
  62. }
  63. #toolbar.proxy{
  64. border-bottom: 2px solid #41e8e5;
  65. }
  66. #bookmarkbar{
  67. position: fixed;
  68. right: 0px;
  69. top: 42px;
  70. background-color: white;
  71. height: calc(100% - 42px);
  72. width: 25em;
  73. padding: 1.2em;
  74. border: 1px solid #dedede;
  75. box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
  76. -webkit-box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
  77. -moz-box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
  78. }
  79. </style>
  80. </head>
  81. <body>
  82. <div id="toolbar" class="ui top small attached menu" style="background-color: #eceef2; padding-left: 12px; padding-right: 12px;">
  83. <div class="ui menuitem">
  84. <button class="ui circular tiny icon button" onclick="undoPage();">
  85. <i class="arrow left icon"></i>
  86. </button>
  87. </div>
  88. <div class="ui menuitem">
  89. <button class="ui circular tiny icon button" onclick="redoPage();">
  90. <i class="arrow right icon"></i>
  91. </button>
  92. </div>
  93. <div class="ui menuitem">
  94. <button class="ui circular tiny icon button" onclick="refreshPage();">
  95. <i class="green refresh icon"></i>
  96. </button>
  97. </div>
  98. <div id="urlbar" class="ui tiny action fluid input">
  99. <input id="urlText" type="text" value="about:blank" onkeydown="handleURLKeydown(event);">
  100. <button id="starBtn" class="ui icon basic circular tiny button" onclick="addBookMark();">
  101. <i class="star icon"></i>
  102. </button>
  103. </div>
  104. <div class="right menu">
  105. <div class="ui rightMenuItem">
  106. <button class="ui tiny icon button" onclick="toggleBookmark();">
  107. <i class="blue bookmark icon"></i>
  108. </button>
  109. </div>
  110. <div class="ui rightMenuItem">
  111. <button class="ui tiny icon button" onclick="loadWebsite('about:blank');">
  112. <i class="home icon"></i>
  113. </button>
  114. </div>
  115. <div class="ui rightMenuItem">
  116. <button class="ui tiny icon button" title="Open in new Window" onclick="openInNewWindow();">
  117. <i class="grey external icon"></i>
  118. </button>
  119. </div>
  120. </div>
  121. </div>
  122. <iframe id="xframe" src="./blank.html" allow="fullscreen" referrerpolicy="no-referrer">
  123. </iframe>
  124. <div id="bookmarkbar" style="display:none;">
  125. <div class="ui container">
  126. <div id="bookmarklist" class="ui middle aligned divided list">
  127. </div>
  128. </div>
  129. </div>
  130. <div id="notvdiWarning">
  131. <div class="ui yellow message">
  132. <i class="close icon"></i>
  133. <div class="header">
  134. <i class="remove icon"></i>Not Recommended Way of Usage
  135. </div>
  136. <p>Please use your native browser windows instead of this iframe browser for maximum website compatibility.</p>
  137. </div>
  138. </div>
  139. <script>
  140. let historyStack = [];
  141. let historyPopStack = [];
  142. let currentURL = "about:blank";
  143. let currentTitle = "";
  144. let bookmarkBuffer = [];
  145. let titleBuffer = {};
  146. //Check if currently under vdi mode
  147. if (ao_module_virtualDesktop == false){
  148. $("#notvdiWarning").show();
  149. }
  150. $('.message .close').on('click', function() {
  151. $(this).closest('.message').transition('fade');
  152. });
  153. function toggleBookmark(){
  154. $("#bookmarkbar").fadeToggle('fast');
  155. }
  156. //Perform window resize element size calculation
  157. $(window).on("resize", function(){
  158. updateResizeElements();
  159. });
  160. function updateResizeElements(){
  161. let buttonWidths = 0;
  162. $(".menuitem").each(function(){
  163. buttonWidths+= $(this).width();
  164. });
  165. let urlbarWidth = window.innerWidth - buttonWidths - 20;
  166. $("#urlbar").css("width", urlbarWidth + "px");
  167. }
  168. updateResizeElements();
  169. function handleURLKeydown(e){
  170. if (e.keyCode == 13){
  171. let url = $("#urlText").val();
  172. loadWebsite(url);
  173. }
  174. }
  175. function refreshPage(){
  176. loadWebsite(currentURL);
  177. }
  178. function undoPage(){
  179. //Push current page into the history pop stack
  180. if (historyStack.length == 0){
  181. return;
  182. }
  183. let currentBackupURL = currentURL;
  184. historyPopStack.push(currentBackupURL);
  185. let targetReturnURL = historyStack.pop();
  186. loadWebsite(targetReturnURL, false);
  187. }
  188. function redoPage(){
  189. if (historyPopStack.length == 0){
  190. return;
  191. }
  192. restorePage = historyPopStack.pop();
  193. historyStack.push(currentURL);
  194. loadWebsite(restorePage, false);
  195. }
  196. function openInNewWindow(){
  197. window.open(currentURL);
  198. }
  199. function getTitleFromURL(targetURL){
  200. var title = targetURL;
  201. if (targetURL.includes("//")){
  202. title = targetURL.substr(targetURL.indexOf("/") + 2, targetURL.length);
  203. }
  204. return title;
  205. }
  206. function loadWebsite(targetURL, writeRestoreRecord = true){
  207. if (writeRestoreRecord && currentURL != targetURL){
  208. historyPopStack = [];
  209. }
  210. //Handle special case
  211. if (targetURL == "about:blank"){
  212. $("#xframe").removeAttr("srcdoc");
  213. $("#xframe").attr("src", "blank.html");
  214. $("#urlText").val(targetURL);
  215. $("#toolbar").removeClass("proxy");
  216. if (writeRestoreRecord && currentURL != targetURL){
  217. historyStack.push(JSON.parse(JSON.stringify(currentURL)));
  218. }
  219. currentURL = targetURL;
  220. return;
  221. }
  222. //Remove the tailing / if exists
  223. if (targetURL.substr(targetURL.length - 1, targetURL.length) == "/"){
  224. targetURL = targetURL.substr(0, targetURL.length - 1);
  225. }
  226. $("#xframe").removeAttr("srcdoc");
  227. $("#xframe").attr("src", "loading.html");
  228. //Filter the URL if required
  229. if (targetURL.substr(0,4) != "http"){
  230. if (location.protocol !== "https:"){
  231. //This page is currently loaded in http mode. Add http:// to it
  232. targetURL = "http://" + targetURL;
  233. }else{
  234. targetURL = "https://" + targetURL;
  235. }
  236. }
  237. $("#urlText").val(targetURL);
  238. if (writeRestoreRecord && currentURL != targetURL){
  239. historyStack.push(JSON.parse(JSON.stringify(currentURL)));
  240. }
  241. currentURL = targetURL;
  242. //Check if the website allow iframe
  243. checkIfAllowIframing(targetURL, function(allowIframe, redirectTarget){
  244. if (allowIframe == null){
  245. $("#xframe").attr("src", "notfound.html#" + targetURL);
  246. }else{
  247. if (allowIframe == true){
  248. $("#xframe").removeAttr("srcdoc");
  249. $("#xframe").attr("src", targetURL);
  250. $("#toolbar").removeClass("proxy");
  251. $("#xframe").on("load", function(){
  252. //Get the page title
  253. ao_module_agirun("Browser/functions/getTitle.js", {url: targetURL}, function(data){
  254. if (data == ""){
  255. let title = getTitleFromURL(targetURL);
  256. ao_module_setWindowTitle(title);
  257. currentTitle = title;
  258. }else{
  259. ao_module_setWindowTitle(data);
  260. currentTitle = data;
  261. }
  262. });
  263. $("#xframe").off("load");
  264. });
  265. }else{
  266. proxyWebContent(targetURL, function(content){
  267. $("#xframe").attr("src", "");
  268. $("#xframe").attr("srcdoc", content);
  269. $("#toolbar").addClass("proxy");
  270. //Extract the title
  271. var matches = content.match(/<title>(.*?)<\/title>/);
  272. if (matches == null){
  273. let title = getTitleFromURL(targetURL);
  274. ao_module_setWindowTitle(title);
  275. currentTitle = title;
  276. }else{
  277. var title = matches[0].replace(/(<([^>]+)>)/gi, "");
  278. ao_module_setWindowTitle(title);
  279. currentTitle = title;
  280. }
  281. });
  282. //alert("Target website do not allow embedding");
  283. }
  284. if (redirectTarget != undefined && redirectTarget != ""){
  285. $("#urlText").val(redirectTarget);
  286. }
  287. }
  288. });
  289. updateBookmarkButtonColor();
  290. }
  291. function updateBookmarkButtonColor(){
  292. if (insideBookmark(currentURL)){
  293. $("#starBtn").addClass("yellow");
  294. }else{
  295. $("#starBtn").removeClass("yellow");
  296. }
  297. }
  298. function initbookmark(){
  299. ao_module_agirun("Browser/functions/bookmark.js", {opr: "read"}, function(bookmarkData){
  300. bookmarkBuffer = bookmarkData;
  301. console.log("BOOKMARK DATA", bookmarkData);
  302. ao_module_agirun("Browser/functions/bookmark.js", {rtype: "titles", opr: "read"}, function(data){
  303. titleBuffer = data;
  304. $("#bookmarklist").html("");
  305. bookmarkBuffer.forEach(bookmark => {
  306. let matchingTitle = titleBuffer[bookmark];
  307. if (matchingTitle == undefined){
  308. matchingTitle = getTitleFromURL(bookmark);
  309. }
  310. //Render the bookmark table
  311. $("#bookmarklist").append(`
  312. <div class="item">
  313. <div class="right floated content">
  314. <div class="ui mini icon basic blue circular button" onclick="loadWebsite('${bookmark}');"><i class="ui linkify icon"></i></div>
  315. </div>
  316. <div class="content">
  317. ${matchingTitle}
  318. </div>
  319. </div>`);
  320. });
  321. if (bookmarkBuffer.length == 0){
  322. $("#bookmarklist").append(`<div class="item">
  323. <div class="content">
  324. <i class="ui bookmark icon"></i> No bookmark saved
  325. </div>
  326. </div>`);
  327. }
  328. });
  329. });
  330. }
  331. initbookmark();
  332. function addBookMark(){
  333. if (bookmarkBuffer.includes(currentURL)){
  334. //Remove bookmark
  335. bookmarkBuffer = bookmarkBuffer.filter(e => e !== currentURL);
  336. delete(titleBuffer[currentURL]);
  337. }else{
  338. //Add bookmark
  339. bookmarkBuffer.push(currentURL);
  340. //Remove array in the table
  341. bookmarkBuffer = bookmarkBuffer.filter(function(item, pos, self) {
  342. return self.indexOf(item) == pos;
  343. });
  344. titleBuffer[currentURL] = currentTitle;
  345. }
  346. ao_module_agirun("Browser/functions/bookmark.js", {"opr": "write", "newBookmarkArray": JSON.stringify(bookmarkBuffer)}, function(data){
  347. ao_module_agirun("Browser/functions/bookmark.js", {"rtype": "titles", "opr": "write", "newTitleArray": JSON.stringify(titleBuffer)}, function(data){
  348. console.log(data);
  349. updateBookmarkButtonColor();
  350. initbookmark();
  351. });
  352. });
  353. }
  354. function insideBookmark(url){
  355. return bookmarkBuffer.includes(url);
  356. }
  357. function proxyWebContent(url, callback){
  358. ao_module_agirun("Browser/functions/proxy.js", {
  359. url: url,
  360. }, function(data){
  361. callback(data);
  362. });
  363. }
  364. //Check if a website can be directly embedded as iframe, can return true / false /null (site not exists)
  365. function checkIfAllowIframing(url, callback){
  366. ao_module_agirun("Browser/functions/getHeader.js", {
  367. url: url
  368. }, function(data){
  369. let xFrameOptions = JSON.parse(data);
  370. let header = "";
  371. if (xFrameOptions.header == null){
  372. xFrameOptions.header = "deny";
  373. }else{
  374. header = xFrameOptions.header.toLowerCase().trim();
  375. }
  376. let location = xFrameOptions.location;
  377. if (header == "null"){
  378. //Site not exists
  379. callback(null);
  380. }
  381. if (header == "sameorigin" || header == "deny"){
  382. //This webpage do not allow iframeing
  383. callback(false, location);
  384. }else{
  385. //This webpage allow iframing. Show it
  386. callback(true, location);
  387. }
  388. }, undefined, 5000)
  389. }
  390. </script>
  391. </body>
  392. </html>