Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,217 @@
'use strict';
var utils = window.utils || {};
if (!utils.sdcard) {
var SdCard = utils.sdcard = {
NOT_INITIALIZED: 0,
NOT_AVAILABLE: 1,
AVAILABLE: 2,
SHARED: 3,
observers: {}
};
SdCard.status = SdCard.NOT_INITIALIZED;
SdCard.deviceStorages = (navigator.getDeviceStorages ? navigator.getDeviceStorages('sdcard') : []);
SdCard.deviceStorage = Array.isArray(SdCard.deviceStorages) &&
SdCard.deviceStorages[0];
SdCard._toStatus = function toStatus(state) {
switch (state) {
case 'available':
SdCard.status = SdCard.AVAILABLE;
break;
// NOTE: The 'SHARED' state is equivalent to NOT_AVAILABLE, since we
// could have inconsistencies if we allow changing the sdcard content
// meanwhile exporting/importing.
// It's needed to have it's own state solely for showing the proper
// message when needed
case 'shared':
SdCard.status = SdCard.SHARED;
break;
case 'unavailable':
case 'deleted':
SdCard.status = SdCard.NOT_AVAILABLE;
break;
}
};
SdCard.updateStorageState = function sd_updateStorageState(state) {
SdCard._toStatus(state);
Object.keys(this.observers).forEach(function onObserver(name) {
if (typeof(SdCard.observers[name]) === 'function') {
SdCard.observers[name].call(null, state);
}
});
};
if (SdCard.deviceStorage) {
SdCard.deviceStorage
.addEventListener('change', function sd_deviceStorageChangeHandler(e) {
SdCard.updateStorageState(e.reason);
});
if (SdCard.status === SdCard.NOT_INITIALIZED) {
SdCard.deviceStorage.available().onsuccess = (function(e) {
SdCard.updateStorageState(e.target.result);
});
}
}
/**
* Check whether there is a SD card inserted in the device.
* @return {Boolean} true if sdcard available, false otherwise.
*/
SdCard.checkStorageCard = function sd_checkStorageCard() {
return SdCard.status === SdCard.AVAILABLE;
};
SdCard.getStatus = function(cb) {
if (!SdCard.deviceStorage) {
SdCard._toStatus('unavailable');
cb(SdCard.status);
return;
}
var req = SdCard.deviceStorage.available();
req.onsuccess = function() {
SdCard._toStatus(req.result);
cb(SdCard.status);
};
req.onerror = function() {
console.error('Error while determining SD Status', req.error.name);
cb(SdCard.status);
};
};
/**
* Retrieves files that adjust to the kind specified in the
* parameters from the SD card inserted in the device.
*
* @param {String[]} mimes Possible mime types of the files to be found.
* @param {String[]} exts Possible file extensions of the files to be found.
* @param {Function} cb Callback.
*/
SdCard.retrieveFiles = function retrieveFilesContent(mimes, exts, cb) {
var fileArray = [];
var forbiddenDirectories = ['.Trash'];
// getDeviceStorages('sdcard') can return more than one storage unit
// in different order, depends on the default media storage setting,
// so we need to iterate though them to be sure we checked the proper
// SD Card
var storages = (navigator.getDeviceStorages ? navigator.getDeviceStorages('sdcard') : []);
var numberOfStorages = storages.length;
var currentStorage = 0;
var cursorOnError = function() {
cb(this.error.name);
};
// RegExp that checks if a string contains any of the extensions.
var reExt = new RegExp('.(' + exts.join('|') + ')$');
var cursorOnSuccess = function(e) {
var file = e.target.result;
// If we don't have any more files on the storage...
if (!file) {
// ... but we have more storages...
if (++currentStorage < numberOfStorages) {
// ... check the next one
cursor = storages[currentStorage].enumerate();
cursor.onsuccess = cursorOnSuccess;
cursor.onerror = cursorOnError;
} else {
cb(null, fileArray);
}
} else {
if ((mimes.indexOf(file.type) === -1) && !reExt.test(file.name)) {
cursor.continue();
} else {
if (forbiddenDirectories.indexOf(file.name) === -1) {
fileArray.push(file);
cursor.continue();
}
}
}
};
var cursor = storages[currentStorage].enumerate();
cursor.onsuccess = cursorOnSuccess;
cursor.onerror = cursorOnError;
};
/**
* Incrementally extracts vcard text from a file.
*
* @param {Object} file File Object.
* @param {Function} cb Function to call for each chunk of vcards retrieved
* from the file.
*/
SdCard.getTextFromFile = function(file, cb) {
if (!file) {
return Promise.reject();
}
return new Promise((resolve, reject) => {
const END_VCARD = 'end:vcard';
const CHUNK_SIZE = 1 * 1024 * 1024; // 1Mb
var fileReader = new FileReader();
var result = 0;
(function readChunk(offset, buffer) {
if (offset >= file.size) {
resolve(result);
return;
}
var chunk = file.slice(offset, offset + CHUNK_SIZE);
offset += CHUNK_SIZE;
fileReader.readAsText(chunk);
fileReader.onload = function() {
buffer += fileReader.result;
if (!buffer.match(/end:vcard/gi)) {
readChunk(offset, buffer);
return;
}
var lastContactInChunk =
buffer.toLowerCase().lastIndexOf(END_VCARD) + END_VCARD.length;
cb(buffer.substr(0, lastContactInChunk)).then(partialResult => {
result += partialResult;
readChunk(offset, buffer.substr(lastContactInChunk + 1));
}).catch(error => {
console.warn(error);
});
};
})(0 /* initial offset */, '' /* initial empty buffer */);
});
};
// Subscribe a callback for sdcard changes. A name is needed
// to refer to this observer.
// @param {String} identifier for the observer
// @param {Function} the callback to be invoqued on a change
// @param {Boolean} if the callback exists, override it
SdCard.subscribeToChanges = function(name, func, force) {
if (this.observers[name] !== undefined && !force) {
return false;
}
this.observers[name] = func;
return true;
};
// Remove the subscription to listen to changes for the specific
// identifier
// @param {String} the identifier
SdCard.unsubscribeToChanges = function(name) {
if (this.observers[name]) {
delete this.observers[name];
return true;
} else {
return false;
}
};
}