VulpesOS/assets/webapps/music.gaiamobile.org/js/shims/device-storage.js
OverSu b38dea0597 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.
2026-09-30 23:13:03 +02:00

392 lines
9.1 KiB
JavaScript

/* global fetch */
(function() {
'use strict';
if (navigator.getDeviceStorages &&
navigator.getDeviceStorage) {
return;
}
const PRELOAD_FILES = [
'/media/1-01%20Whole%20Lotta%20Love.mp3',
'/media/1-02%20What%20Is%20and%20What%20Should%20Never%20Be.mp3',
'/media/1-03%20The%20Lemon%20Song.mp3',
'/media/1-04%20Thank%20You.mp3',
'/media/1-05%20Heartbreaker.mp3',
'/media/1-06%20Living%20Loving%20Maid%20(She\'s%20Just%20a%20Woman).mp3',
'/media/1-07%20Ramble%20On.mp3',
'/media/1-08%20Moby%20Dick.mp3',
'/media/1-09%20Bring%20It%20On%20Home.mp3',
'/media/soundgarden/1-01%20Let%20Me%20Drown.mp3',
'/media/soundgarden/1-02%20My%20Wave.mp3',
'/media/soundgarden/1-03%20Fell%20On%20Black%20Days.mp3',
'/media/soundgarden/1-04%20Mailman.mp3',
'/media/soundgarden/1-05%20Superunknown.mp3',
'/media/soundgarden/1-06%20Head%20Down.mp3',
'/media/soundgarden/1-07%20Black%20Hole%20Sun.mp3',
'/media/soundgarden/1-08%20Spoonman.mp3',
'/media/soundgarden/1-09%20Limo%20Wreck.mp3',
'/media/soundgarden/1-10%20The%20Day%20I%20Tried%20to%20Live.mp3',
'/media/soundgarden/1-11%20Kickstand.mp3',
'/media/soundgarden/1-12%20Fresh%20Tendrils.mp3',
'/media/soundgarden/1-13%204th%20of%20July.mp3',
'/media/soundgarden/1-14%20Half.mp3',
'/media/soundgarden/1-15%20Like%20Suicide.mp3',
'/media/soundgarden/1-16%20She%20Likes%20Surprises.mp3'
];
navigator.getDeviceStorages = navigator.getDeviceStorages || getDeviceStorages;
navigator.getDeviceStorage = navigator.getDeviceStorage || getDeviceStorage;
var deviceStorages = {};
function getDeviceStorages(storageName) {
return [getDeviceStorage(storageName)];
}
function getDeviceStorage(storageName) {
if (deviceStorages[storageName]) {
return deviceStorages[storageName];
}
return (deviceStorages[storageName] = new DeviceStorage(storageName));
}
function DeviceStorage(storageName) {
this.storageName = storageName;
this.canBeFormatted = false;
this.canBeMounted = false;
this.canBeShared = false;
this.default = true;
this.isRemovable = false;
this.onchange = null;
this.addEventListener('change', (evt) => {
if (typeof this.onchange === 'function') {
this.onchange(evt);
}
});
this._files = [];
var preloadNextFile = (function(index) {
if (index >= PRELOAD_FILES.length) {
return;
}
var path = PRELOAD_FILES[index];
fetch(path)
.then((result) => {
result.blob().then((file) => {
preloadNextFile(++index);
file.name = decodeURIComponent(path);
file.lastModifiedDate = new Date();
this._files.push(file);
this.dispatchEvent('change', {
reason: 'created',
path: file.name
});
this.dispatchEvent('change', {
reason: 'modified',
path: file.name
});
});
})
.catch((error) => {
preloadNextFile(++index);
console.log('Unable to retrieve preloaded file', path, error);
});
}).bind(this);
preloadNextFile(0);
}
DeviceStorage.prototype = new EventTarget();
DeviceStorage.prototype.constructor = DeviceStorage;
DeviceStorage.prototype.add = function(file) {
if (file instanceof Blob) {
return this.addNamed(file, '/' + this.storageName + '/' +
generateRandomFileName());
}
throw 'TypeError: Argument 1 of DeviceStorage.add does not implement ' +
'interface Blob.';
};
DeviceStorage.prototype.addNamed = function(file, name) {
if (file instanceof Blob) {
return new DOMRequest((success, error) => {
var exists = !!this._files.find(f => f.name === name);
if (exists) {
return error({ name: 'Unknown', message: '' });
}
file.lastModifiedDate = new Date();
file.name = name;
this._files.push(file);
this.dispatchEvent('change', {
reason: 'created',
path: file.name
});
this.dispatchEvent('change', {
reason: 'modified',
path: file.name
});
success(name);
});
}
throw 'TypeError: Argument 1 of DeviceStorage.addNamed does not implement '+
'interface Blob.';
};
DeviceStorage.prototype.appendNamed = function() {
};
DeviceStorage.prototype.available = function() {
return new DOMRequest(success => success('available'));
};
DeviceStorage.prototype.delete = function(fileName) {
return new DOMRequest((success, error) => {
var file = this._files.find(f => f.name === fileName);
if (file) {
this._files.splice(this._files.indexOf(file), 1);
this.dispatchEvent('change', {
reason: 'deleted',
path: file.name
});
success();
return;
}
error({ name: 'Unknown', message: '' });
});
};
DeviceStorage.prototype.enumerate = function(path, options) {
return new DOMRequest((success, error) => {
var files = [];
if (typeof path === 'object') {
options = path;
path = '';
}
if (!path) {
options = options || {};
path = '';
}
this._files.forEach((file) => {
if (options.since instanceof Date &&
file.lastModifiedDate < options.since) {
return;
}
if (file.name.startsWith(path)) {
files.push(file);
}
});
if (files.length === 0) {
return error({ name: 'Unknown', message: '' });
}
success(files);
});
};
DeviceStorage.prototype.enumerateEditable = function() {
// NOOP
};
DeviceStorage.prototype.format = function() {
return new DOMRequest(success => success('formatting'));
};
DeviceStorage.prototype.freeSpace = function() {
return new DOMRequest(success => success(1000000000));
};
DeviceStorage.prototype.get = function(fileName) {
return new DOMRequest((success, error) => {
var file = this._files.find(f => f.name === fileName);
if (file) {
success(file);
return;
}
error({ name: 'Unknown', message: '' });
});
};
DeviceStorage.prototype.getEditable = function() {
// NOOP
};
DeviceStorage.prototype.getRoot = function() {
// NOOP
};
DeviceStorage.prototype.mount = function() {
return new DOMRequest(success => success('mounting'));
};
DeviceStorage.prototype.storageStatus = function() {
return new DOMRequest(success => success('Mounted'));
};
DeviceStorage.prototype.unmount = function() {
return new DOMRequest(success => success('unmounting'));
};
DeviceStorage.prototype.usedSpace = function() {
return new DOMRequest(success => success(
this._files.reduce((a, b) => (a.size || a) + b.size))
);
};
function DOMRequest(callback) {
var success = (result) => {
this.readyState = 'done';
this.result = result;
if (typeof this.onsuccess === 'function') {
this.onsuccess();
}
};
var error = (error) => {
this.readyState = 'done';
this.error = error;
if (typeof this.onerror === 'function') {
this.onerror();
}
};
this.readyState = 'pending';
this.onsuccess = null;
this.onerror = null;
if (typeof callback === 'function') {
setTimeout(() => callback(success, error));
}
}
DOMRequest.prototype.constructor = DOMRequest;
function DOMCursor(callback) {
var success = (results) => {
this.readyState = 'done';
this._results = results;
this._index = -1;
this.continue();
};
var error = (error) => {
this.readyState = 'done';
this.error = error;
if (typeof this.onerror === 'function') {
this.onerror();
}
};
this.readyState = 'pending';
this.done = false;
this.onsuccess = null;
this.onerror = null;
if (typeof callback === 'function') {
setTimeout(() => callback(success, error));
}
}
DOMCursor.prototype = new DOMRequest();
DOMCursor.prototype.constructor = DOMCursor;
DOMCursor.prototype.continue = function() {
if (this._index >= this._results.length - 1) {
return;
}
this.result = this._results[++this._index];
if (this._index >= this._results.length - 1) {
this.done = true;
}
if (typeof this.onsuccess === 'function') {
this.onsuccess();
}
};
function EventTarget(object) {
if (typeof object !== 'object') {
return;
}
for (var property in object) {
this[property] = object[property];
}
}
EventTarget.prototype.constructor = EventTarget;
EventTarget.prototype.dispatchEvent = function(name, data) {
var events = this._events || {};
var listeners = events[name] || [];
listeners.forEach(listener => listener.call(this, data));
};
EventTarget.prototype.addEventListener = function(name, listener) {
var events = this._events = this._events || {};
var listeners = events[name] = events[name] || [];
if (listeners.find(fn => fn === listener)) {
return;
}
listeners.push(listener);
};
EventTarget.prototype.removeEventListener = function(name, listener) {
var events = this._events || {};
var listeners = events[name] || [];
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === listener) {
listeners.splice(i, 1);
return;
}
}
};
function generateRandomFileName() {
var fileName = '';
for (var i = 0; i < 8; i++) {
fileName += Math.floor((1 + Math.random()) * 0x10000)
.toString(16).substr(1);
}
return fileName;
}
})();