Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
392 lines
9.1 KiB
JavaScript
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;
|
|
}
|
|
|
|
})();
|