diff --git a/index.html b/index.html new file mode 100644 index 00000000..8f95c463 --- /dev/null +++ b/index.html @@ -0,0 +1,8 @@ + + + + +Redirection to index.html + + + \ No newline at end of file diff --git a/main.js b/main.js index 44f4dc5d..68e81f10 100644 --- a/main.js +++ b/main.js @@ -11,7 +11,7 @@ protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { standard: true, - secure: true, + //secure: true, allowServiceWorkers: true, supportFetchAPI: true } @@ -25,16 +25,22 @@ function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({ width: 800, - height: 600 - //, webPreferences: { - // preload: path.join(__dirname, 'preload.js') - // } + height: 600, + webPreferences: { + // nodeIntegration: false, + // contextIsolation: true, + preload: path.join(__dirname, 'preload.js') + // preload: __dirname + '/www/preload.js' + , webSecurity: false + // preload: 'app://www/preload.js' + } }); // and load the index.html of the app. mainWindow.loadURL('app://www/index.html'); // DEV: If you need Service Worker more than you need document.cookie, load app like this: - // mainWindow.loadFile('www/index.html'); + // mainWindow.loadFile('index.html'); + // DEV: Enable code below to check cookies saved by app in console log // mainWindow.webContents.on('did-finish-load', function() { // mainWindow.webContents.session.cookies.get({}, (error, cookies) => { @@ -54,19 +60,29 @@ function createWindow() { }); } +// let dirnameParts = __dirname.match(/[^\/\\]+(?:[\/\\]|$)/g); + // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. + app.on('ready', () => { protocol.registerFileProtocol('app', (request, callback) => { //protocol.registerHttpProtocol('app', (request, callback) => { - const url = request.url.substr(6); + let url = request.url.replace(/^app:\/\/([./]*)(.*)$/, function(_p0, relPath, linkUrl) { + // // @TODO: Complete routine to recognize relative links below (../) + // let i = 0; + // let parsedPath = relPath.replace(/\.\.\//g, function(p0) { + // i++; + // }); + return relPath + linkUrl; + }); callback({ path: path.normalize(`${__dirname}/${url}`) // url: 'file://' + path.normalize(`${__dirname}/${url}`), // method: 'GET' }); - console.log(path.normalize(`${__dirname}/${url}` + ':' + url)); + // console.log(path.normalize(`${__dirname}/${url}` + ':' + url)); }, (error) => { if (error) console.error('Failed to register protocol'); }); diff --git a/preload.js b/preload.js index b4619e1d..9ce68f92 100644 --- a/preload.js +++ b/preload.js @@ -1,12 +1,36 @@ // All of the Node.js APIs are available in the preload process. // It has the same sandbox as a Chrome extension. -window.addEventListener('DOMContentLoaded', () => { - const replaceText = (selector, text) => { - const element = document.getElementById(selector) - if (element) element.innerText = text; - } + +'use strict'; + +// const { remote } = require('electron'); +// var win = remote.getCurrentWebContents(); +const { open, read, close, stat } = require('fs'); + +window.fs = { + open: open, + read: read, + close: close, + stat: stat +}; +// window.Buffer = Buffer; + +// console.log(win.session.cookies); + +// win.session.cookies.get({}, (error, cookies) => { +// console.log('Cookies:' + cookies); +// }); + +console.log("Looks like preload launched..."); + +// window.addEventListener('DOMContentLoaded', () => { +// const replaceText = (selector, text) => { +// const element = document.getElementById(selector) +// if (element) element.innerText = text; +// } - for (const type of ['chrome', 'node', 'electron']) { - replaceText(`${type}-version`, process.versions[type]); - } - }); \ No newline at end of file +// for (const type of ['chrome', 'node', 'electron']) { +// replaceText(`${type}-version`, process.versions[type]); +// } +// }); + diff --git a/render.js b/render.js deleted file mode 100644 index fd8a13b5..00000000 --- a/render.js +++ /dev/null @@ -1,3 +0,0 @@ -// This file is required by the index.html file and will -// be executed in the renderer process for that window. -// All of the Node.js APIs are available in this process. \ No newline at end of file diff --git a/www/index.html b/www/index.html index d9ca505a..a7e99eb2 100644 --- a/www/index.html +++ b/www/index.html @@ -403,9 +403,12 @@

Configuration

+

+ Packaged ZIM archive: + +

-

This application needs a ZIM archive to work.
- Download one from the Archive Library.

+

Get more archives (in other languages)

For more information see About tab (top right)

@@ -469,9 +472,9 @@

Download links

-

- Files can be very large, do not attempt to download using mobile data! -

+

+ Files can be very large, do not attempt to download using mobile data! +

@@ -583,9 +586,9 @@ + + - -
diff --git a/www/js/app.js b/www/js/app.js index d105e610..a1744ebe 100644 --- a/www/js/app.js +++ b/www/js/app.js @@ -98,7 +98,7 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki } $(document).ready(resizeIFrame); $(window).resize(function() { - resizeIFrame; + resizeIFrame(); // We need to load any images exposed by the resize var scrollFunc = document.getElementById('articleContent').contentWindow; scrollFunc = scrollFunc ? scrollFunc.onscroll : null; @@ -1351,17 +1351,31 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki return new abstractFilesystemAccess.StorageFirefoxOS(s); }); } - if (storages !== null && storages.length > 0 || - typeof Windows !== 'undefined' && typeof Windows.Storage !== 'undefined') { + typeof Windows !== 'undefined' && typeof Windows.Storage !== 'undefined' || + typeof window.fs !== 'undefined') { // Make a fake first access to device storage, in order to ask the user for confirmation if necessary. // This way, it is only done once at this moment, instead of being done several times in callbacks // After that, we can start looking for archives //storages[0].get("fake-file-to-read").then(searchForArchivesInPreferencesOrStorage, if (!params.pickedFile) { searchForArchivesInPreferencesOrStorage(); - } else { + } else if (typeof window.fs === 'undefined') { processPickedFileUWP(params.pickedFile); + } else { + // We're in an Electron app with a packaged file that we need to read from the node File System + console.log("Loading packaged ZIM for Electron..."); + // Create a fake File object (this avoids extensive patching of later code) + var file = {}; + file.name = params.packagedFile; + // @TODO: Use fs.stat to query the file size + file.size = 824209647; + // @TODO: Create a params.filePath in init.js + file.path = 'archives'; + file.readMode = 'electron'; + setLocalArchiveFromFileList([file]); + params.pickedFile = file; + document.getElementById('hideFileSelectors').style.display = params.showFileSelectors ? 'inline' : 'none'; } } else { // If DeviceStorage is not available, we display the file select components @@ -1799,6 +1813,13 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki params.pickedFolder = params.localStorage; scanUWPFolderforArchives(params.localStorage); if (!params.rescan) setLocalArchiveFromArchiveList(params.storedFile); + } else if (typeof window.fs !== 'undefined') { + // We're in an Electron packaged app + params.storedFile = params.packagedFile || ''; + if (!params.rescan) { + if (params.showFileSelectors) $('input:checkbox[name=displayFileSelectors]').click(); + } + setLocalArchiveFromFileList([params.pickedFile]); } } @@ -1807,6 +1828,7 @@ define(['jquery', 'zimArchiveLoader', 'uiUtil', 'util', 'utf8', 'images', 'cooki */ function setLocalArchiveFromFileSelect() { setLocalArchiveFromFileList(document.getElementById('archiveFilesLegacy').files); + params.rescan = false; } /** diff --git a/www/js/lib/util.js b/www/js/lib/util.js index 9bdaf873..710ce171 100644 --- a/www/js/lib/util.js +++ b/www/js/lib/util.js @@ -20,6 +20,7 @@ * along with Kiwix (file LICENSE-GPLv3.txt). If not, see */ 'use strict'; + define(['q'], function (q) { /** @@ -204,21 +205,37 @@ define(['q'], function (q) { /** * Reads a Uint8Array from the given file starting at byte offset begin and * for given size. - * @param {File} file - * @param {Integer} begin - * @param {Integer} size + * @param {File} file The file to read + * @param {Integer} begin The first byte in the file to read + * @param {Integer} size The number of bytes to read * @returns {Promise} Promise */ function readFileSlice(file, begin, size) { var deferred = q.defer(); - var reader = new FileReader(); - reader.onload = function (e) { - deferred.resolve(new Uint8Array(e.target.result)); - }; - reader.onerror = reader.onabort = function (e) { - deferred.reject(e); - }; - reader.readAsArrayBuffer(file.slice(begin, begin + size)); + if (file.readMode === 'electron') { + // We are reading a packaged file and have to use Electron fs.read (so we don't have to pick the file) + var buffer = new Uint8Array(size); + // @TODO: Add errorchecking to electron routine + fs.open(file.path + '/' + file.name, 'r', function (err, fd) { + fs.read(fd, buffer, 0, size, begin, function (err, bytesRead, data) { + deferred.resolve(data); + // console.log(buffer.toString('utf8')); + fs.close(fd, function (err) { + if (err) console.log('Could not close file...', err); + }); + }); + }); + } else { + // We are reading a picked file, so use vanilla JS methods + var reader = new FileReader(); + reader.onload = function (e) { + deferred.resolve(new Uint8Array(e.target.result)); + }; + reader.onerror = reader.onabort = function (e) { + deferred.reject(e); + }; + reader.readAsArrayBuffer(file.slice(begin, begin + size)); + } return deferred.promise; }