We could not find the archive ' + lastSelectedArchive + '!
Please select its location...
'; $('#alertModal').off('hide.bs.modal'); $('#alertModal').on('hide.bs.modal', function () { displayFileSelect(); }); $('#alertModal').modal({ backdrop: 'static', keyboard: true }); if (document.getElementById('configuration').style.display == 'none') document.getElementById('btnConfigure').click(); } } } else { uiUtil.systemAlert("Welcome to Kiwix! This application needs at least a ZIM file in your SD-card (or internal storage). Please download one and put it on the device (see About section). Also check that your device is not connected to a computer through USB device storage (which often locks the SD-card content)"); $("#btnAbout").click(); var isAndroid = navigator.userAgent.indexOf("Android") !== -1; if (isAndroid) { alert("You seem to be using an Android device. Be aware that there is a bug on Firefox, that prevents finding Wikipedia archives in a SD-card (at least on some devices. See about section). Please put the archive in the internal storage if the application can't find it."); } } } /** * Sets the localArchive from the selected archive in the drop-down list */ function setLocalArchiveFromArchiveList(archiveDirectory) { params.rescan = false; archiveDirectory = archiveDirectory || $('#archiveList').val(); if (archiveDirectory && archiveDirectory.length > 0) { // Now, try to find which DeviceStorage has been selected by the user // It is the prefix of the archive directory var regexpStorageName = /^\/([^\/]+)\//; var regexpResults = regexpStorageName.exec(archiveDirectory); var selectedStorage = null; if (regexpResults && regexpResults.length > 0) { var selectedStorageName = regexpResults[1]; for (var i = 0; i < storages.length; i++) { var storage = storages[i]; if (selectedStorageName === storage.storageName) { // We found the selected storage selectedStorage = storage; } } if (selectedStorage === null) { uiUtil.systemAlert("Unable to find which device storage corresponds to directory " + archiveDirectory); } } else { // This happens when the archiveDirectory is not prefixed by the name of the storage // (in the Simulator, or with FxOs 1.0, or probably on devices that only have one device storage) // In this case, we use the first storage of the list (there should be only one) if (storages.length === 1) { selectedStorage = storages[0]; } else { //IT'S NOT FREAKIN FFOS!!!!!!!!!! //Patched for UWP support: if (!params.pickedFile && params.pickedFolder && typeof MSApp !== 'undefined') { var query = params.pickedFolder.createFileQuery(); query.getFilesAsync().done(function (files) { var file; if (files) { for (var i = 0; i < files.length; i++) { if (files[i].name == archiveDirectory) { file = files[i]; break; } } if (file) { var fileset = []; if (/\.zim\w\w$/i.test(file.name)) { var genericFileName = file.name.replace(/(.*)\.zim\w\w$/i, "$1"); var testFileName = new RegExp(genericFileName + '\\.zim\\w\\w$'); for (var i = 0; i < files.length; i++) { if (testFileName.test(files[i].name)) { //This converts a UWP storage file object into a standard JavaScript web file object fileset.push(MSApp.createFileFromStorageFile(files[i])); } } } else { //This converts a UWP storage file object into a standard JavaScript web file object fileset = [MSApp.createFileFromStorageFile(file)]; } } } if (fileset && fileset.length) { setLocalArchiveFromFileList(fileset); } else { console.error("The picked file could not be found in the selected folder!"); var archiveList = []; for (var i = 0; i < files.length; i++) { if (/\.zima?a?$/i.test(files[i].name)) { archiveList.push(files[i].name); } } populateDropDownListOfArchives(archiveList); document.getElementById('btnConfigure').click(); } }); return; } else { //Check if user previously picked a specific file rather than a folder if (params.pickedFile && typeof MSApp !== 'undefined') { try { selectedStorage = MSApp.createFileFromStorageFile(params.pickedFile); setLocalArchiveFromFileList([selectedStorage]); return; } catch (err){ // Probably user has moved or deleted the previously selected file uiUtil.systemAlert("The previously picked archive can no longer be found!"); console.error("Picked archive not found: " + err); } } } //There was no picked file or folder, so we'll try setting the default localStorage //if (!params.pickedFolder) { //This gets called, for example, if the picked folder or picked file are in FutureAccessList but now are //no longer accessible. There will be a (handled) error in cosole log, and params.pickedFolder and params.pickedFile will be blank params.rescan = true; if (params.localStorage) { scanUWPFolderforArchives(params.localStorage); } else { document.getElementById('btnConfigure').click(); } return; //} } } // Reset the cssDirEntryCache and cssBlobCache. Must be done when archive changes. if (cssBlobCache) cssBlobCache = new Map(); //if (cssDirEntryCache) // cssDirEntryCache = new Map(); state.selectedArchive = zimArchiveLoader.loadArchiveFromDeviceStorage(selectedStorage, archiveDirectory, function (archive) { cookies.setItem("lastSelectedArchive", archiveDirectory, Infinity); // The archive is set : go back to home page to start searching if (params.rescan) { document.getElementById('btnConfigure').click(); params.rescan = false; } else { $('#openLocalFiles').hide(); document.getElementById('btnHome').click(); } }); } } /** * Displays the zone to select files from the archive */ function displayFileSelect() { document.getElementById('openLocalFiles').style.display = 'block'; // Set the main drop zone scrollBoxDropZone.addEventListener('dragover', handleGlobalDragover); scrollBoxDropZone.addEventListener('dragleave', function(e) { configDropZone.style.border = ''; }); // Also set a global drop zone (allows us to ensure Config is always displayed for the file drop) globalDropZone.addEventListener('dragover', function (e) { e.preventDefault(); if (configDropZone.style.display === 'none') document.getElementById('btnConfigure').click(); e.dataTransfer.dropEffect = 'link'; }); globalDropZone.addEventListener('drop', handleFileDrop); // This handles use of the file picker document.getElementById('archiveFiles').addEventListener('change', setLocalArchiveFromFileSelect); } function handleGlobalDragover(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'link'; configDropZone.style.border = '3px dotted red'; } function handleIframeDragover(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'link'; document.getElementById('btnConfigure').click(); } function handleIframeDrop(e) { e.stopPropagation(); e.preventDefault(); return; } function handleFileDrop(packet) { packet.stopPropagation(); packet.preventDefault(); configDropZone.style.border = ''; var files = packet.dataTransfer.files; document.getElementById('openLocalFiles').style.display = 'none'; setLocalArchiveFromFileList(files); // This clears the display of any previously picked archive in the file selector document.getElementById('archiveFiles').value = null; } function pickFileUWP() { //Support UWP FilePicker [kiwix-js-windows #3] // Create the picker object and set options var filePicker = new Windows.Storage.Pickers.FileOpenPicker; filePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.downloads; // Filter folder contents filePicker.fileTypeFilter.replaceAll([".zim"]); filePicker.pickSingleFileAsync().then(processPickedFileUWP); } function processPickedFileUWP(file) { if (file) { // Cache file so the contents can be accessed at a later time Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.addOrReplace(params.falFileToken, file); params.pickedFile = file; if (params.pickedFolder) Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.remove(params.falFolderToken); params.pickedFolder = ""; cookies.setItem("lastSelectedArchive", file.name, Infinity); params.storedFile = file.name; // Since we've explicitly picked a file, we should jump to it params.rescan = false; document.getElementById('openLocalFiles').style.display = "none"; populateDropDownListOfArchives([file.name]); } else { // The picker was dismissed with no selected file console.log("User closed folder picker without picking a file"); } } function pickFolderUWP() { //Support UWP FilePicker [kiwix-js-windows #3] var folderPicker = new Windows.Storage.Pickers.FolderPicker; folderPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.downloads; folderPicker.fileTypeFilter.replaceAll([".zim", ".dat", ".idx", ".txt", ".zimaa"]); folderPicker.pickSingleFolderAsync().done(function (folder) { if (folder) { scanUWPFolderforArchives(folder); } }); } function scanUWPFolderforArchives(folder) { if (folder) { // Application now has read/write access to all contents in the picked folder (including sub-folder contents) // Cache folder so the contents can be accessed at a later time Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.addOrReplace(params.falFolderToken, folder); params.pickedFolder = folder; // Query the folder. var query = folder.createFileQuery(); query.getFilesAsync().done(function (files) { // Display file list var archiveDisplay = document.getElementById('chooseArchiveFromLocalStorage'); if (files) { var archiveList = []; files.forEach(function (file) { if (file.fileType == ".zim" || file.fileType == ".zimaa") { archiveList.push(file.name); } }); if (archiveList.length) { document.getElementById('noZIMFound').style.display = "none"; populateDropDownListOfArchives(archiveList); return; } } archiveDisplay.style.display = "inline"; document.getElementById('noZIMFound').style.display = "block"; document.getElementById('openLocalFiles').style.display = "none"; document.getElementById('rescanStorage').style.display = "block"; document.getElementById('archiveList').options.length = 0; document.getElementById('archiveList').size = 0; document.getElementById('archiveNumber').innerHTML = '0 Archives found in local storage (tap "Select storage" to select an archive location)'; params.pickedFolder = ""; Windows.Storage.AccessCache.StorageApplicationPermissions.futureAccessList.remove(params.falFolderToken); return; }); } else { // The picker was dismissed with no selected file console.log("User closed folder picker without picking a file"); } } function setLocalArchiveFromFileList(files) { // Check for usable file types for (var i = files.length; i--;) { // DEV: you can support other file types by adding (e.g.) '|dat|idx' after 'zim\w{0,2}' if (!/\.(?:zim\w{0,2})$/i.test(files[i].name)) { uiUtil.systemAlert("One or more files does not appear to be a ZIM file!"); return; } } // Check that user hasn't picked just part of split ZIM if (files.length == 1 && /\.zim\w\w/i.test(files[0].name)) { document.getElementById('alert-content').innerHTML = 'You have picked only part of a split archive!
Please select its folder in Config, or drag and drop all of its parts into Config.
'; $('#alertModal').off('hide.bs.modal'); $('#alertModal').on('hide.bs.modal', function () { if (document.getElementById('configuration').style.display == 'none') document.getElementById('btnConfigure').click(); displayFileSelect(); }); $('#alertModal').modal({ backdrop: 'static', keyboard: true }); } // If the file name is already in the archive list, try to select it in the list var listOfArchives = document.getElementById('archiveList'); if (listOfArchives) listOfArchives.value = files[0].name; // Reset the cssDirEntryCache and cssBlobCache. Must be done when archive changes. if (cssBlobCache) cssBlobCache = new Map(); //if (cssDirEntryCache) // cssDirEntryCache = new Map(); state.selectedArchive = zimArchiveLoader.loadArchiveFromFiles(files, function (archive) { // The archive is set : go back to home page to start searching params.storedFile = archive._file._files[0].name; cookies.setItem("lastSelectedArchive", params.storedFile, Infinity); var reloadLink = document.getElementById("reloadPackagedArchive"); if (reloadLink) { if (params.packagedFile != params.storedFile) { reloadLink.style.display = "inline"; reloadLink.removeEventListener("click", loadPackagedArchive); reloadLink.addEventListener("click", loadPackagedArchive); document.getElementById("moreInfo").style.display = "none"; } else { reloadLink.style.display = "none"; document.getElementById('currentArchive').style.display = "none"; document.getElementById("moreInfo").style.display = "inline"; } } //This ensures the correct icon is set for the newly loaded archive cssUIThemeGetOrSet(params.cssUITheme); if (params.rescan) { document.getElementById('btnConfigure').click(); document.getElementById('btnConfigure').click(); params.rescan = false; } else { $('#openLocalFiles').hide(); if (params.rememberLastPage && ~params.lastPageVisit.indexOf(params.storedFile)) { var lastPage = decodeURIComponent(params.lastPageVisit.replace(/@kiwixKey@.+/, "")); goToArticle(lastPage); } else { // The archive has changed, so we must blank the last page in case the Home page of the new archive // has the same title as the previous archive (possible if it is, for example, "index") params.lastPageVisit = ""; document.getElementById('btnHome').click(); } } }); } function loadPackagedArchive() { // Reload any ZIM files in local storage (whcih the user can't otherwise select with the filepicker) if (params.localStorage) { params.storedFile = params.packagedFile || ''; params.pickedFolder = params.localStorage; scanUWPFolderforArchives(params.localStorage); if (!params.rescan) setLocalArchiveFromArchiveList(params.storedFile); } } /** * Sets the localArchive from the File selects populated by user */ function setLocalArchiveFromFileSelect() { setLocalArchiveFromFileList(document.getElementById('archiveFilesLegacy').files); } /** * Reads a remote archive with given URL, and returns the response in a Promise. * This function is used by setRemoteArchives below, for UI tests * * @param url The URL of the archive to read * @returns {Promise} */ function readRemoteArchive(url) { var deferred = q.defer(); var request = new XMLHttpRequest(); request.open("GET", url, true); request.responseType = "blob"; request.onreadystatechange = function () { if (request.readyState === XMLHttpRequest.DONE) { if ((request.status >= 200 && request.status < 300) || request.status === 0) { // Hack to make this look similar to a file request.response.name = url; deferred.resolve(request.response); } else { deferred.reject("HTTP status " + request.status + " when reading " + url); } } }; request.onabort = function (e) { deferred.reject(e); }; request.send(null); return deferred.promise; } /** * This is used in the testing interface to inject remote archives */ window.setRemoteArchives = function () { var readRequests = []; var i; for (i = 0; i < arguments.length; i++) { readRequests[i] = readRemoteArchive(arguments[i]); } return q.all(readRequests).then(function (arrayOfArchives) { setLocalArchiveFromFileList(arrayOfArchives); }); }; /** * Handle key input in the prefix input zone * @param {Event} evt */ function onKeyUpPrefix(evt) { // Use a timeout, so that very quick typing does not cause a lot of overhead // It is also necessary for the words suggestions to work inside Firefox OS if (window.timeoutKeyUpPrefix) { window.clearTimeout(window.timeoutKeyUpPrefix); } window.timeoutKeyUpPrefix = window.setTimeout(function () { var prefix = $("#prefix").val(); if (prefix && prefix.length > 0) { document.getElementById('searchArticles').click(); } }, 500); } /** * Search the index for DirEntries with title that start with the given prefix (implemented * with a binary search inside the index file) * @param {String} prefix */ function searchDirEntriesFromPrefix(prefix) { if (state.selectedArchive !== null && state.selectedArchive.isReady()) { $('#activeContent').alert('close'); if (!prefix || /^\s/.test(prefix)) { var sel = prefix ? prefix.replace(/^\s(.*)/, '$1') : ''; if (sel.length) { sel = sel.replace(/^(.)(.*)/, function(p0, p1, p2) { return p1.toUpperCase() + p2; }); } showZIMIndex(null, sel); } else { state.selectedArchive.findDirEntriesWithPrefix(prefix.trim(), MAX_SEARCH_RESULT_SIZE, populateListOfArticles); } } else { $('#searchingArticles').hide(); // We have to remove the focus from the search field, // so that the keyboard does not stay above the message $('#searchArticles').focus(); uiUtil.systemAlert("Archive not set : please select an archive"); document.getElementById('btnConfigure').click(); } } /** * Extracts and displays in htmlArticle the first MAX_SEARCH_RESULT_SIZE articles beginning with start * @param {String} start Optional index number to begin the list with * @param {String} prefix Optional search prefix from which to start an alphabetical search */ function showZIMIndex(start, prefix) { // If we're searching by title index number (other than 0 or null), we should ignore any prefix if (isNaN(start)) { prefix = prefix || ''; } else { prefix = start > 0 ? '' : prefix; } if (state.selectedArchive !== null && state.selectedArchive.isReady()) { state.selectedArchive.findDirEntriesWithPrefixCaseSensitive(prefix, MAX_SEARCH_RESULT_SIZE, function(dirEntryArray, nextStart) { var docBody = document.getElementById('largeModal'); var newHtml = ""; for (var i = 0; i < dirEntryArray.length; i++) { var dirEntry = dirEntryArray[i]; newHtml += "\n" + (dirEntry.getTitleOrUrl()) + ""; } start = start ? start : 0; var back = start ? '<< Previous ' + MAX_SEARCH_RESULT_SIZE + '' : ''; var next = dirEntryArray.length === MAX_SEARCH_RESULT_SIZE ? 'Next ' + MAX_SEARCH_RESULT_SIZE + ' >>' : ''; var backNext = back ? next ? back + ' | ' + next : back : next; backNext = '