123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <meta name="apple-mobile-web-app-capable" content="yes" />
- <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
- <meta name="theme-color" content="#4b75ff">
- <link rel="stylesheet" href="../script/semantic/semantic.min.css">
- <script src="../script/jquery.min.js"></script>
- <script src="../script/ao_module.js"></script>
- <script src="../script/semantic/semantic.min.js"></script>
- <link rel="icon" type="image/png" href="img/module_icon.png">
- <link rel="manifest" crossorigin="use-credentials" href="manifest.json">
- <title>Browser</title>
- <style>
- body{
- overflow: hidden;
- }
- #urlbar{
- padding-top: 0.2em;
- padding-bottom: 0.2em;
- padding-left: 1.2em;
- padding-right: 0.3em;
- line-height: 1em;
- }
- .menuitem{
- margin-top: 0.2em;
- padding:0.2em !important;
- border: 0px !important;
- }
- .menuitem button{
- background-color: white !important;
- }
- .rightMenuItem{
- margin-top: 0.3em;
- padding:0.1em !important;
- border: 0px !important;
- }
- .rightMenuItem button:not(.inverted):not(.sidebarToggle.selected){
- background-color: white !important;
- }
- #starBtn{
- padding-right: 0.9em;
- cursor: pointer !important;
- background-color: white !important;
- }
- #starBtn:hover{
- opacity: 0.7;
- }
- #xframe{
- width: 100%;
- height: calc(100% - 43px);
- border: 0px solid transparent;
- }
- #notvdiWarning{
- position: fixed;
- bottom: 0px;
- left: 0px;
- width: 100%;
- padding: 0.4em;
- display: none;
- }
- #toolbar.proxy{
- border-bottom: 2px solid #41e8e5;
- }
- .sidebar{
- position: fixed;
- right: 0px;
- top: 42px;
- background-color: white;
- height: calc(100% - 42px);
- width: 25em;
- padding: 1.2em;
- border: 1px solid #dedede;
- box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
- -webkit-box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
- -moz-box-shadow: -10px 1px 11px -5px rgba(0,0,0,0.15);
- }
- .sidebarToggle.selected{
- background-color: #e2e2e2 !important;
- }
- #downloadDropper{
- border: 1px solid grey;
- width: 100%;
- height: 200px;
- cursor: move; /* fallback if grab cursor is unsupported */
- cursor: grab;
- padding: 1em;
- text-align: center;
- }
- </style>
- </head>
- <body>
- <div id="toolbar" class="ui top small attached menu" style="background-color: #eceef2; padding-left: 12px; padding-right: 12px;">
- <div class="ui menuitem">
- <button class="ui circular tiny icon button" onclick="undoPage();">
- <i class="arrow left icon"></i>
- </button>
- </div>
- <div class="ui menuitem">
- <button class="ui circular tiny icon button" onclick="redoPage();">
- <i class="arrow right icon"></i>
- </button>
- </div>
- <div class="ui menuitem">
- <button class="ui circular tiny icon button" onclick="refreshPage();">
- <i class="green refresh icon"></i>
- </button>
- </div>
- <div id="urlbar" class="ui tiny action fluid input">
- <input id="urlText" type="text" value="about:blank" onkeydown="handleURLKeydown(event);">
- <button id="starBtn" class="ui icon basic circular tiny button" onclick="addBookMark();">
- <i class="star icon"></i>
- </button>
- </div>
- <div class="right menu">
- <div class="ui rightMenuItem">
- <button class="ui tiny icon button sidebarToggle" onclick="toggleBookmark(this);">
- <i class="blue bookmark icon"></i>
- </button>
- </div>
- <div class="ui rightMenuItem">
- <button class="ui tiny icon button" onclick="loadWebsite('about:blank');">
- <i class="home icon"></i>
- </button>
- </div>
- <div class="ui rightMenuItem">
- <button class="ui tiny icon button" title="Open in new Window" onclick="openInNewWindow();">
- <i class="grey external icon"></i>
- </button>
- </div>
- <div class="ui rightMenuItem">
- <button class="ui tiny icon button sidebarToggle" title="Download Manager" onclick="toggleDownloadManager(this);">
- <i class="grey download icon"></i>
- </button>
- </div>
- </div>
- </div>
- <iframe id="xframe" src="./blank.html" allow="fullscreen" referrerpolicy="no-referrer">
- </iframe>
- <div id="bookmarkbar" class="sidebar" style="display:none;">
- <div class="ui container">
- <div id="bookmarklist" class="ui middle aligned divided list">
-
- </div>
- </div>
- </div>
- <div id="downloadManager" class="sidebar" style="display:none;">
- <div class="ui container">
- <div id="downloadDropper" allowdrop="true" ondrop="drop(event)" ondragstart="return false;" onclick="event.preventDefault(); openURLEnter();" ondblclick="event.preventDefault();event.stopImmediatePropagation();" ondragover="allowDrop(event);" draggable="false">
- <p style="margin-top: 3em;">Drop a link, image into this area; or click this area to enter a URL for download</p>
- </div>
- <div class="ui action fluid input" id="iframeMode" style="display:none;">
- <input type="text" id="manualInputURL" placeholder="Copy link here to download">
- <button class="ui icon green basic button" onclick="downloadFromManualInputURL();"><i class="ui download icon"></i></button>
- </div>
- </div>
-
- <div class="ui divider"></div>
- <p>Downloaded Files</p>
- <div class="ui list" id="downloadList" style="min-height: 300px;">
-
- </div>
- </div>
- <div id="notvdiWarning">
- <div class="ui yellow message">
- <i class="close icon"></i>
- <div class="header">
- <i class="remove icon"></i>Not Recommended Way of Usage
- </div>
- <p>Please use your native browser windows instead of this iframe browser for maximum website compatibility.</p>
- </div>
- </div>
- <script>
- let historyStack = [];
- let historyPopStack = [];
- let currentURL = "about:blank";
- let downloadPendingURL = "newfile.txt";
- let preferDownloadLocation = "user:/Desktop";
- let currentTitle = "";
- let bookmarkBuffer = [];
- let titleBuffer = {};
- //Drag drop download function
- function drop(ev) {
- ev.preventDefault();
- ev.stopImmediatePropagation();
- var target = $(ev.target);
- if (!(target.is("div") || target.is("a"))) {
- target = $(target).parent();
- }
- var linkObject = ev.dataTransfer.getData('text/html');
- window.debug = ev.dataTransfer;
- var remoteLink = ($(linkObject).find("a").length>0 || $(linkObject).is("a"))? ($(linkObject).find("a").attr("href")|| $(linkObject).attr("href")):($(linkObject).find("img").length>0?$(linkObject).find('img').attr("src"):($(linkObject).attr('src')!=""?$(linkObject).attr('src'):undefined));
- console.log(ev.dataTransfer, linkObject, remoteLink);
- if (remoteLink == undefined){
- return;
- }
- downloadToFile(remoteLink);
- }
- function allowDrop(ev) {
- ev.preventDefault();
- }
- function openURLEnter(){
- let url = prompt("Enter the URL to be downloaded", "http://");
- url = url.trim();
- if (url == null || url == undefined || url == "http://" || url == "https://"){
- return;
- }
- downloadToFile(url);
- }
- function downloadFromManualInputURL(){
- var url = $("#manualInputURL").val();
- downloadToFile(url);
- }
- function downloadToFile(downloadURL){
- downloadPendingURL = downloadURL;
- selectSaveLocation();
- }
- //save location selector
- function selectSaveLocation() {
- var filename = decodeURI(downloadPendingURL.split("/").pop().split("?").shift());
- option = {
- defaultName: filename //Default filename used in new operation
- }
- ao_module_openFileSelector(fileSelected, preferDownloadLocation, "new", false, option);
- }
- function updateDownloadProg(elementId){
- if ($("#" + elementId).attr("status") == "downloading"){
- //This task is still downloading. Get its current size and
- //arrange next progress update check
- ao_module_agirun("Browser/functions/sizechk.js", {
- filepath: $("#" + elementId).attr('fpath')
- }, function(data){
- console.log(data);
- if (data == -1){
- $("#" + elementId).find(".downproc").text("[Starting]");
- }else{
- $("#" + elementId).find(".downproc").text("[" + ao_module_utils.formatBytes(data, 1) + "]");
- }
-
- setTimeout(function(){
- updateDownloadProg(elementId);
- }, 1000);
- });
-
- }else{
- $("#" + elementId).find(".downproc").text("");
- }
- }
- function fileSelected(filedata) {
- var fileFullpath = filedata[0].filepath;
- var pathchunk = fileFullpath.split("/");
- var filename = pathchunk.pop();
- var filepath = pathchunk.join("/");
- preferDownloadLocation = filepath;
-
- //Append a download pending item to the list
- var thisDownloadUUID = "download_" + Date.now();
- $("#downloadList").append(`
- <div class="item downloadTask" id="${thisDownloadUUID}" fpath="${fileFullpath}" status="downloading">
- <i class="download icon mainicon"></i>
- <a class="content" onclick="openFileLocation('${filepath}', '${filename}');">
- ${filename} <span class="downproc"></span>
- </a>
- </div>
- `);
- updateDownloadProg(thisDownloadUUID);
- //Call AGI to download the file
- ao_module_agirun("Browser/functions/download.js", {
- link: downloadPendingURL,
- filename: filename,
- filepath: filepath
- }, function(data){
- console.log(data);
- $("#" + thisDownloadUUID).find(".mainicon").attr("class", "green checkmark icon mainicon");
- $("#" + thisDownloadUUID).attr("status", "done");
- }, function(){
- //Error callback
- $("#" + thisDownloadUUID).find(".mainicon").attr("class", "red remove icon mainicon");
- $("#" + thisDownloadUUID).attr("status", "error");
- })
- }
- function openFileLocation(filepath, filename){
- ao_module_openPath(filepath, filename);
- }
- function allowDragAndDrop(enable=false){
- if(enable){
- $("#downloadDropper").show();
- $("#iframeMode").hide();
- }else{
- $("#downloadDropper").hide();
- $("#iframeMode").show();
- }
- }
- //Check if currently under vdi mode
- if (ao_module_virtualDesktop == false){
- $("#notvdiWarning").show();
- }
- $('.message .close').on('click', function() {
- $(this).closest('.message').transition('fade');
- });
- function toggleBookmark(object){
- $(".sidebar:not(#bookmarkbar)").hide();
- var isVisable = $("#bookmarkbar").is(":visible")
- $("#bookmarkbar").fadeToggle('fast');
- $(".sidebarToggle.selected").removeClass("selected");
- if (!isVisable){
- $(object).addClass('selected');
- }
- }
- function toggleDownloadManager(object){
- $(".sidebar:not(#downloadManager)").hide();
- var isVisable = $("#downloadManager").is(":visible")
- $("#downloadManager").fadeToggle('fast');
- $(".sidebarToggle.selected").removeClass("selected");
- if (!isVisable){
- $(object).addClass('selected');
- }
- }
- //Perform window resize element size calculation
- $(window).on("resize", function(){
- updateResizeElements();
- });
- function updateResizeElements(){
- let buttonWidths = 0;
- $(".menuitem").each(function(){
- buttonWidths+= $(this).width();
- });
- let urlbarWidth = window.innerWidth - buttonWidths - 20;
- $("#urlbar").css("width", urlbarWidth + "px");
- }
- updateResizeElements();
- function handleURLKeydown(e){
- if (e.keyCode == 13){
- let url = $("#urlText").val();
- loadWebsite(url);
- }
- }
- function refreshPage(){
- loadWebsite(currentURL);
- }
- function undoPage(){
- //Push current page into the history pop stack
- if (historyStack.length == 0){
- return;
- }
- let currentBackupURL = currentURL;
- historyPopStack.push(currentBackupURL);
- let targetReturnURL = historyStack.pop();
- loadWebsite(targetReturnURL, false);
- }
- function redoPage(){
- if (historyPopStack.length == 0){
- return;
- }
- restorePage = historyPopStack.pop();
- historyStack.push(currentURL);
- loadWebsite(restorePage, false);
- }
- function openInNewWindow(){
- window.open(currentURL);
- }
- function getTitleFromURL(targetURL){
- var title = targetURL;
- if (targetURL.includes("//")){
- title = targetURL.substr(targetURL.indexOf("/") + 2, targetURL.length);
- }
-
- return title;
- }
- function loadWebsite(targetURL, writeRestoreRecord = true){
- if (writeRestoreRecord && currentURL != targetURL){
- historyPopStack = [];
- }
-
- //Handle special case
- if (targetURL == "about:blank"){
- $("#xframe").removeAttr("srcdoc");
- $("#xframe").attr("src", "blank.html");
- $("#urlText").val(targetURL);
- $("#toolbar").removeClass("proxy");
- if (writeRestoreRecord && currentURL != targetURL){
- historyStack.push(JSON.parse(JSON.stringify(currentURL)));
- }
- currentURL = targetURL;
- allowDragAndDrop(false);
- return;
- }
- //Remove the tailing / if exists
- if (targetURL.substr(targetURL.length - 1, targetURL.length) == "/"){
- targetURL = targetURL.substr(0, targetURL.length - 1);
- }
- $("#xframe").removeAttr("srcdoc");
- $("#xframe").attr("src", "loading.html");
- //Filter the URL if required
- if (targetURL.substr(0,4) != "http"){
- if (location.protocol !== "https:"){
- //This page is currently loaded in http mode. Add http:// to it
- targetURL = "http://" + targetURL;
- }else{
- targetURL = "https://" + targetURL;
- }
- }
- $("#urlText").val(targetURL);
- if (writeRestoreRecord && currentURL != targetURL){
- historyStack.push(JSON.parse(JSON.stringify(currentURL)));
- }
- currentURL = targetURL;
-
- //Check if the website allow iframe
- checkIfAllowIframing(targetURL, function(allowIframe, redirectTarget){
- if (allowIframe == null){
- $("#xframe").attr("src", "notfound.html#" + targetURL);
- }else{
- if (allowIframe == true){
- allowDragAndDrop(false);
- $("#xframe").removeAttr("srcdoc");
- $("#xframe").attr("src", targetURL);
- $("#toolbar").removeClass("proxy");
- $("#xframe").on("load", function(){
- //Get the page title
- ao_module_agirun("Browser/functions/getTitle.js", {url: targetURL}, function(data){
- if (data == ""){
- let title = getTitleFromURL(targetURL);
- ao_module_setWindowTitle(title);
- currentTitle = title;
- }else{
- ao_module_setWindowTitle(data);
- currentTitle = data;
- }
- });
- $("#xframe").off("load");
- });
- }else{
- allowDragAndDrop(true);
- proxyWebContent(targetURL, function(content){
- $("#xframe").attr("src", "");
- $("#xframe").attr("srcdoc", content);
- $("#toolbar").addClass("proxy");
- //Extract the title
- var matches = content.match(/<title>(.*?)<\/title>/);
- if (matches == null){
- let title = getTitleFromURL(targetURL);
- ao_module_setWindowTitle(title);
- currentTitle = title;
- }else{
- var title = matches[0].replace(/(<([^>]+)>)/gi, "");
- ao_module_setWindowTitle(title);
- currentTitle = title;
- }
-
- });
- //alert("Target website do not allow embedding");
- }
- if (redirectTarget != undefined && redirectTarget != ""){
- $("#urlText").val(redirectTarget);
- }
- }
- });
- updateBookmarkButtonColor();
- }
- function updateBookmarkButtonColor(){
- if (insideBookmark(currentURL)){
- $("#starBtn").addClass("yellow");
- }else{
- $("#starBtn").removeClass("yellow");
- }
- }
- function initbookmark(){
- ao_module_agirun("Browser/functions/bookmark.js", {opr: "read"}, function(bookmarkData){
- bookmarkBuffer = bookmarkData;
- console.log("BOOKMARK DATA", bookmarkData);
- ao_module_agirun("Browser/functions/bookmark.js", {rtype: "titles", opr: "read"}, function(data){
- titleBuffer = data;
- $("#bookmarklist").html("");
- bookmarkBuffer.forEach(bookmark => {
- let matchingTitle = titleBuffer[bookmark];
- if (matchingTitle == undefined){
- matchingTitle = getTitleFromURL(bookmark);
- }
- //Render the bookmark table
- $("#bookmarklist").append(`
- <div class="item">
- <div class="right floated content">
- <div class="ui mini icon basic blue circular button" onclick="loadWebsite('${bookmark}');"><i class="ui linkify icon"></i></div>
- </div>
- <div class="content">
- ${matchingTitle}
- </div>
- </div>`);
- });
- if (bookmarkBuffer.length == 0){
- $("#bookmarklist").append(`<div class="item">
- <div class="content">
- <i class="ui bookmark icon"></i> No bookmark saved
- </div>
- </div>`);
- }
-
- });
- });
-
- }
- initbookmark();
- function addBookMark(){
- if (bookmarkBuffer.includes(currentURL)){
- //Remove bookmark
- bookmarkBuffer = bookmarkBuffer.filter(e => e !== currentURL);
- delete(titleBuffer[currentURL]);
- }else{
- //Add bookmark
- bookmarkBuffer.push(currentURL);
- //Remove array in the table
- bookmarkBuffer = bookmarkBuffer.filter(function(item, pos, self) {
- return self.indexOf(item) == pos;
- });
- titleBuffer[currentURL] = currentTitle;
- }
-
- ao_module_agirun("Browser/functions/bookmark.js", {"opr": "write", "newBookmarkArray": JSON.stringify(bookmarkBuffer)}, function(data){
- ao_module_agirun("Browser/functions/bookmark.js", {"rtype": "titles", "opr": "write", "newTitleArray": JSON.stringify(titleBuffer)}, function(data){
- console.log(data);
- updateBookmarkButtonColor();
- initbookmark();
- });
- });
- }
- function insideBookmark(url){
- return bookmarkBuffer.includes(url);
- }
- function proxyWebContent(url, callback){
- ao_module_agirun("Browser/functions/proxy.js", {
- url: url,
- }, function(data){
- callback(data);
- });
- }
- //Check if a website can be directly embedded as iframe, can return true / false /null (site not exists)
- function checkIfAllowIframing(url, callback){
- ao_module_agirun("Browser/functions/getHeader.js", {
- url: url
- }, function(data){
- let xFrameOptions = JSON.parse(data);
- let header = "";
- if (xFrameOptions.header == null){
- xFrameOptions.header = "deny";
- }else{
- header = xFrameOptions.header.toLowerCase().trim();
- }
-
- let location = xFrameOptions.location;
- if (header == "null"){
- //Site not exists
- callback(null);
- }
-
- if (header == "sameorigin" || header == "deny"){
- //This webpage do not allow iframeing
- callback(false, location);
- }else{
- //This webpage allow iframing. Show it
- callback(true, location);
- }
- }, undefined, 5000)
- }
- </script>
- </body>
- </html>
|