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:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
392
gaia/apps/music/js/shims/device-storage.js
Executable file
392
gaia/apps/music/js/shims/device-storage.js
Executable file
|
|
@ -0,0 +1,392 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
})();
|
||||
Loading…
Reference in a new issue