diff --git a/src/assets/init.js b/src/assets/init.js index 5b1575a..a51f5f5 100644 --- a/src/assets/init.js +++ b/src/assets/init.js @@ -22,6 +22,24 @@ var _base64ToArrayBuffer = function (base64) { return bytes.buffer; }; + +var set_favicon = function(href) { + var favicon = document.createElement("link"); + favicon.setAttribute('rel', 'shortcut icon'); + href = normalize_path(href); + const file = window.global_context.file_tree[href]; + if (file.mime_type == 'image/svg+xml') { + favicon.setAttribute('href', 'data:' + file.mime_type + ';charset=utf-8;base64,' + btoa(file.data)); + favicon.setAttribute('type', file.mime_type); + } else { + if (file.base64encoded) { + favicon.setAttribute('href', 'data:' + file.mime_type + ';base64,' + file.data); + } + } + document.head.appendChild(favicon); +}; + + var createIframe = function() { var iframe = document.getElementById(iFrameId); if (iframe) { iframe.remove() }; @@ -46,6 +64,35 @@ var load_virtual_page = (function (path, get_params, anchor) { window.history.pushState({path, get_params, anchor}, '', '#'); }); + +var normalize_path = function(path) { + // TODO remove redundant definition of this function (in inject.js) + // make relative paths absolute + var result = window.global_context.current_path; + result = result.split('/'); + result.pop(); + result = result.concat(path.split('/')); + + // resolve relative directories + var array = []; + Array.from(result).forEach( component => { + if (component == '..') { + if (array) { + array.pop(); + } + } else if (component == '.') { + } else { + if (component) { array.push(component); } + } + }); + + result = array.join('/'); + // console.log(`Normalized path: ${path} -> ${result} (@${window.global_context.current_path})`); + return result; +}; + + + window.onload = function() { // Set up the virtual file tree var FT = window.global_context.file_tree; @@ -61,7 +108,8 @@ window.onload = function() { if (evnt.data.action == 'set_title') { // iframe has finished loading and sent us its title // parent sets the title and responds with the global_context object - window.document.title = evnt.data.argument; + window.document.title = evnt.data.argument.title; + set_favicon(evnt.data.argument.favicon); var iframe = document.getElementById(iFrameId); iframe.contentWindow.postMessage({ action: "set_data", diff --git a/src/assets/inject.js b/src/assets/inject.js index 8cb2b34..9090756 100644 --- a/src/assets/inject.js +++ b/src/assets/inject.js @@ -228,5 +228,8 @@ const observer = new MutationObserver((mutationList) => { // Set parent window title and trigger data transmission window.parent.postMessage({ action: "set_title", - argument: window.document.querySelector('head>title').innerText + argument: { + title: window.document.querySelector('head>title').innerText, + favicon: document.querySelector("link[rel*='icon']").getAttribute('href') + } }, '*'); diff --git a/src/embed.py b/src/embed.py index 8b5cafb..1cad94e 100644 --- a/src/embed.py +++ b/src/embed.py @@ -108,6 +108,7 @@ def prepare_file(filename, before, after): ext = ext.lower()[1:] data = open(filename, 'rb').read() mime_type = mime_type_from_bytes(data) + base64encoded = False if ext == 'css': # assuming all CSS files have names ending in '.css' @@ -118,6 +119,7 @@ def prepare_file(filename, before, after): ]: # JSON doesn't allow binary data data = base64.b64encode(data) + base64encoded = True elif ext in ['html', 'htm']: data = embed_html_resources( @@ -138,6 +140,7 @@ def prepare_file(filename, before, after): result = { 'data': data, 'mime_type': mime_type, + 'base64encoded': base64encoded, } return result