UNPKG

allume

Version:

A cross-platform package bootloader for javascript.

780 lines (700 loc) 33.7 kB
///////////////////////////////////////////////////////////////////////////////////// // // module 'cc.io.0.2.0/' // ///////////////////////////////////////////////////////////////////////////////////// (function(using, require) { define.parameters = {}; define.parameters.wrapped = true; define.parameters.system = "pkx"; define.parameters.id = "cc.io.0.2.0/"; define.parameters.pkx = { "name": "cc.io", "version": "0.2.0", "title": "IO Library", "description": "Library for reading and writing data.", "license": "Apache-2.0", "pkx": { "main": "io.js", "dependencies": [ "cc.type.0.2", "cc.string.0.2", "cc.event.0.2" ] } }; define.parameters.dependencies = [ "pkx", "module", "configuration", "requirer" ]; define.parameters.dependencies[0] = define.parameters.pkx; define.parameters.dependencies.push(define.cache.get("cc.type.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.string.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.event.0.2/")); using = define.getUsing(define.parameters.id); require = define.getRequire(define.parameters.id, require); ///////////////////////////////////////////////////////////////////////////////////////////// // // cc.io // // Library for reading and writing data. // // License // Apache License Version 2.0 // // Copyright Nick Verlinden (info@createconform.com) // ///////////////////////////////////////////////////////////////////////////////////////////// (function() { function IO(pkx, module) { var self = this; var type, event, string; type = require("./cc.type"); event = require("./cc.event"); string = require("./cc.string"); this.FORMAT_URI = "uri"; this.FORMAT_PATH = "path"; this.DIRECTORY = "io-directory"; this.FILE = "io-file"; this.ACCESS_READ = "io-access-read"; this.ACCESS_OVERWRITE = "io-access-overwrite"; this.ACCESS_MODIFY = "io-access-modify"; this.VOLUME_SCOPE_LOCAL = "io-volume-scope-local"; this.VOLUME_SCOPE_REMOTE = "io-volume-scope-remote"; this.VOLUME_CLASS_PERSISTENT = "io-volume-class-persistent"; this.VOLUME_CLASS_TEMPORARY = "io-volume-class-temporary"; this.VOLUME_SPEED_UNKOWN = "io-volume-speed-unknown"; this.VOLUME_SPEED_SLOW = "io-volume-speed-slow"; this.VOLUME_SPEED_REASONABLE = "io-volume-speed-reasonable"; this.VOLUME_SPEED_FAST = "io-volume-speed-fast"; this.VOLUME_SPEED_INSTANT = "io-volume-speed-instant"; this.VOLUME_STATE_INITIALIZING = "io-volume-state-initializing"; this.VOLUME_STATE_READY = "io-volume-state-ready"; this.VOLUME_STATE_ERROR = "io-volume-state-error"; this.VOLUME_TYPE_FIXED = "io-volume-type-fixed"; this.VOLUME_TYPE_REMOVABLE = "io-volume-type-removable"; this.OPERATION_STREAM_OPEN = "io-operation-stream-open"; this.OPERATION_STREAM_CLOSE = "io-operation-stream-close"; this.OPERATION_STREAM_GET_LENGTH = "io-operation-stream-get-length"; this.OPERATION_STREAM_READ = "io-operation-stream-read"; this.OPERATION_STREAM_WRITE = "io-operation-stream-write"; this.OPERATION_VOLUME_INITIALIZATION = "io-operation-volume-initialization"; this.SEEKORIGIN_BEGIN = 0; this.SEEKORIGIN_CURRENT = 1; this.SEEKORIGIN_END = 2; this.EVENT_PROTOCOL_ADDED = "io-event-protocol-added"; this.EVENT_FORMAT_ADDED = "io-event-format-added"; this.EVENT_VOLUME_ADDED = "io-event-volume-added"; this.EVENT_VOLUME_REMOVED = "io-event-volume-removed"; this.EVENT_VOLUME_STATE_CHANGED = "io-event-volume-state-changed"; this.EVENT_VOLUME_INITIALIZATION_PROGRESS = "io-event-volume-initialization-progress"; this.EVENT_STREAM_READ_PROGRESS = "io-event-stream-read-progress"; this.ERROR_UNKNOWN_PROTOCOL = "io-error-unknown-protocol"; this.ERROR_UNSUPPORTED_URI_FORMAT = "io-error-unsupported-uri-format"; this.ERROR_UNSUPPORTED_OPERATION = "io-error-unsupported-operation"; this.ERROR_UNSUPPORTED_STREAM = "io-error-unsupported-stream"; this.ERROR_FILE_NOT_FOUND = "io-error-file-not-found"; this.ERROR_VOLUME_NOT_READY = "io-error-volume-not-ready"; this.ERROR_VOLUME_NOT_FOUND = "io-error-volume-not-found"; this.ERROR_VOLUME_ALREADY_REGISTERED = "io-error-volume-already-registered"; this.ERROR_STREAM_CLOSED = "io-error-stream-closed"; this.ERROR_ACCESS_DENIED = "io-error-access-denied"; this.ERROR_URI_PARSE = "io-error-uri-parse"; this.ERROR_RUNTIME = "io-error-runtime"; this.ERROR_INVALID_URI = "io-error-invalid-uri"; this.ERROR_INVALID_LENGTH = "io-error-invalid-length"; this.ERROR_INVALID_JSON_DATA = "io-error-invalid-json-data"; var RE_URI = new RegExp( /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/ ); var RE_URI_AUTHORITY = new RegExp( /^(?!.*\n.*)(?:([^:]*)(?::(.*?))?@)?([^:]*)(?::([^:]*?))?$/ ); var protocols = {}; var formats = {}; var volumes = []; function unsupported() { return new Promise(function (resolve, refuse) { refuse(new Error(self.ERROR_UNSUPPORTED_OPERATION, "")); }); } this.Stream = { objectURL : null, getAccess : unsupported, getName : unsupported, getLength : unsupported, read : unsupported, readAsString : function () { var own = this; return new Promise(function (resolve, refuse) { own.read(null, 0).then(function (bytes) { try { resolve(bytes.toString()); } catch (e) { refuse(e); } }, refuse); }); }, readAsJSON : function () { var own = this; return new Promise(function (resolve, refuse) { own.readAsString().then(function (str) { try { resolve(JSON.parse(str)); } catch (e) { refuse(new Error(self.ERROR_INVALID_JSON_DATA, e)); } }, refuse); }); }, copyTo : function (stream) { var own = this; return new Promise(function (resolve, refuse) { own.read(null, 0).then(function (bytes) { stream.write(bytes, 0).then(resolve, refuse); }, refuse); }); }, write : unsupported, getMimeType : function () { var own = this; return new Promise(function (resolve, refuse) { own.read(4, 0).then(function (arr) { try { var header = ""; for (var i = 0; i < arr.length; i++) { header += arr[i].toString(16); } //TODO - IMPLEMENT MORE TYPES, GOOD START BELOW //https://github.com/sindresorhus/file-type/blob/master/index.js //http://www.garykessler.net/library/file_sigs.html switch (header) { case "89504e47": resolve("image/png"); break; case "47494638": resolve("image/gif"); break; case "ffd8ffe0": case "ffd8ffe1": case "ffd8ffe2": resolve("image/jpeg"); break; default: //try file extension var name = own.getName(); if (name) { var ext = name.substr(name.lastIndexOf(".")); switch (ext) { case ".svg": resolve("image/svg+xml"); break; default: resolve("unknown"); } } break; } } catch (e) { refuse(e); } }, refuse); }); }, createObjectURL : function () { var own = this; return new Promise(function (resolve, refuse) { if (own.objectUrl) { resolve(own.objectUrl); return; } if (typeof URL == "undefined" && typeof btoa == "undefined") { refuse(new Error(self.ERROR_UNSUPPORTED_OPERATION, "The environment does not support creating an object url.")); return; } own.read(null, 0).then(function (arr) { own.getMimeType().then(function (mimeType) { try { if (typeof URL !== "undefined" && type.isFunction(URL.createObjectURL)) { var blob = new Blob([arr], {"type": mimeType}); own.objectUrl = URL.createObjectURL(blob); } else { //UNCOMMENT IF BROKEN LATER -> replaced by type.toString() prototype //code below duplicated from toString() method, for optimizing speed /*var str = ""; var len = bytes.length;//bytes.byteLength; for (var i = 0; i < len; i++) { str += String.fromCharCode(bytes[i]); }*/ own.objectUrl = "data:" + mimeType + ";base64," + btoa(bytes.toString()); } resolve(own.objectUrl); } catch (e) { refuse(e); } }, refuse); }, refuse); }); }, close : unsupported }; this.BufferedStream = function (buffer, name) { var own = this; var closed = false; //TODO - implement EVENT_STREAM_READ_PROGRESS this.getName = function () { return name; }; this.getLength = function () { return new Promise(function (resolve, refuse) { closed ? refuse(new Error(self.ERROR_STREAM_CLOSED, "")) : resolve(buffer.length); }); }; this.read = function (len, position) { if (!position) { position = 0; } return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } if (len == null) { own.getLength().then(function (length) { doRead(length - position); }, refuse); } else { doRead(len); } function doRead(len) { if (len == buffer.length) { resolve(buffer); } else { var nBuf = buffer.subarray(position, position + len); position += len; resolve(nBuf); } } }); }; this.write = function (data, position) { if (!position) { position = 0; } return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } if (data == null) { resolve(); return; } if (!((typeof Buffer != "undefined" && data instanceof Buffer) || (typeof Uint8Array != "undefined" && data instanceof Uint8Array) || type.isString(data))) { refuse(new Error("Mandatory parameter 'data' should be of type 'Buffer', 'Uint8Array' or 'String'.")); return; } if (!type.isFunction(data.toString)) { refuse(new Error("Mandatory parameter 'data' should have the toString() function.")); return; } var newData; var newB; var newLength; newData = data instanceof Uint8Array ? data : data.toUint8Array(); newLength = position + newData.length; newB = new Uint8Array(newLength); newB.set(buffer, 0); newB.set(newData, position); buffer = newB; resolve(); }); }; this.close = function () { return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } buffer = null; closed = true; resolve(); }); }; this.events = new event.Emitter(this); }; this.BufferedStream.prototype = self.Stream; this.SubStream = function (stream, offset, size, name) { var own = this; var closed = false; //TODO - implement EVENT_STREAM_READ_PROGRESS this.getName = function () { return name; }; this.getLength = function () { return new Promise(function (resolve, refuse) { closed ? refuse(new Error(self.ERROR_STREAM_CLOSED, "")) : resolve(size); }); }; this.read = function (len, position) { if (!position) { position = 0; } return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } if (len == null) { len = size; } if (offset + position + len > offset + size) { len = (offset + size) - (offset + position); } stream.read(len, offset + position).then(resolve, refuse); }); }; this.write = function (data, position) { if (!position) { position = 0; } return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } stream.write(data, offset + position).then(resolve, refuse); }); }; this.close = function () { return new Promise(function (resolve, refuse) { if (closed) { refuse(new Error(self.ERROR_STREAM_CLOSED, "")); return; } closed = true; resolve(); }); }; this.events = new event.Emitter(this); }; this.SubStream.prototype = self.Stream; this.URI = function (scheme, authority, path, query, fragment, module) { var own = this; var parts; if (arguments.length <= 2 && type.isString(scheme) && type.isObject(authority)) { // rfc compliant - https://tools.ietf.org/html/rfc3986#appendix-B parts = RE_URI.exec(scheme); module = authority; } if (scheme instanceof self.URI) { authority = new self.URIAuthority(scheme.authority.userInfo, scheme.authority.host, scheme.authority.port); path = scheme.path; query = scheme.query; fragment = scheme.fragment; scheme = scheme.scheme; } this.scheme = parts ? parts[2] : scheme; this.authority = parts ? parts[4] : authority; this.path = parts ? parts[5] : path; this.query = parts ? parts[7] : query; this.fragment = parts ? parts[9] : fragment; var initialScheme = own.scheme; // check if path starts with slash if (this.path && this.path.length > 1 && this.path.substr(0, 1) != "/") { throw new Error(self.ERROR_INVALID_URI, "Path '" + this.path + "' does not start with '/'."); } // parse authority data if (!(this.authority instanceof self.URIAuthority)) { this.authority = new self.URIAuthority(this.authority); } var UNKNOWN_PROTOCOL_ERROR = "Unknown protocol '" + this.scheme + "://'."; function getModule() { // module is specified from the parse function. this is a fail-safe if (!module || own.scheme != initialScheme) { if (protocols[own.scheme]) { module = protocols[own.scheme]; initialScheme = own.scheme; } } return module; } // returns promise with first parameter stream object when fulfilled this.open = function (opt_access, opt_create) { var module = getModule(); if (!module) { throw new Error(self.ERROR_UNKNOWN_PROTOCOL, UNKNOWN_PROTOCOL_ERROR); } return new Promise(function (resolve, reject) { try { module.uri.open(own, opt_access, opt_create).then(function (stream) { resolve(stream); }, reject); } catch (e) { reject(e); } }); }; this.exists = function () { var module = getModule(); if (!module) { throw new Error(self.ERROR_UNKNOWN_PROTOCOL, UNKNOWN_PROTOCOL_ERROR); } return new Promise(function (resolve, reject) { try { module.uri.exists(own).then(function (type) { resolve(type); }, reject); } catch (e) { reject(e); } }); }; this.delete = function () { var module = getModule(); if (!module) { throw new Error(self.ERROR_UNKNOWN_PROTOCOL, UNKNOWN_PROTOCOL_ERROR); } return new Promise(function (resolve, reject) { try { module.uri.delete(own).then(function (type) { resolve(type); }, reject); } catch (e) { reject(e); } }); }; this.toString = function (opt_format, opt_param) { var module = getModule(); // rfc compliant - https://tools.ietf.org/html/rfc3986#section-5.3 if (!opt_format) { return (own.scheme ? own.scheme + ":" : "") + (own.authority ? "//" + own.authority : "") + (own.path ? own.path : "") + (own.query ? "?" + own.query : "") + (own.fragment ? "#" + own.fragment : ""); } try { if (opt_format) { if (opt_format == self.FORMAT_PATH) { if (type.isString(opt_param)) { return own.path.replace(/\//g, opt_param); } else { return own.path; } } if (!formats[opt_format]) { return ""; } return formats[opt_format].uri.toString(own, opt_format); } else if (module) { return module.uri.toString(own); } } catch (e) { //toString() must never fail return ""; } }; }; this.URIAuthority = function (userInfo, host, port) { var parts; if (arguments.length == 1) { parts = RE_URI_AUTHORITY.exec(userInfo); } this.userInfo = parts ? (parts[1] ? parts[1] + (parts[2] ? ":" + parts[2] : "") : null) : userInfo; this.host = parts ? parts[3] : host; this.port = parts ? parts[4] : port; }; this.URIAuthority.prototype.toString = function () { return (this.userInfo ? this.userInfo + "@" : "") + (this.host ? this.host : "") + (this.port ? ":" + this.port : ""); }; this.URI.parse = function (uri, checkSupport) { if (string.isURL(uri)) { for (var p in protocols) { if (uri.substr(0, p.length + 3).toLowerCase() == p.toLowerCase() + "://") { return new self.URI(uri, protocols[p]); } } // unknown protocol, but valid uri if (!checkSupport) { return new self.URI(uri); } } // probe the given uri for other formats for (var f in formats) { var u = formats[f].uri.parse(uri); if (u) { return new self.URI(u.scheme, u.authority, u.path, u.query, u.fragment, formats[f]); } } throw new Error(self.ERROR_UNSUPPORTED_URI_FORMAT, "Unsupported URI format '" + uri + "', no parser was found."); }; this.URI.open = function (uri, opt_access, opt_create) { return self.URI.parse(uri).open(opt_access, opt_create); }; this.URI.exists = function (uri) { return self.URI.parse(uri).exists(); }; this.URI.delete = function (uri) { return self.URI.parse(uri).delete(); }; this.Volume = { id : null, err : [], name : null, protocol : null, size : null, description : null, type : null, class : null, scope : null, bandwidth : null, latency : { "read": { "random": null, "sequential": null }, "write": { "random": null, "sequential": null } }, readOnly : null, speed : null, then : unsupported, open : unsupported, delete : unsupported, exists : unsupported, query : unsupported, getBytesUsed : unsupported, getBytesAvailable : unsupported, close : unsupported, // if implemented, needs to be overwritten events : new event.Emitter() }; this.volumes = {}; this.volumes.get = function (opt_localId, opt_name, opt_type, opt_scope, opt_class, opt_access, opt_size) { //TODO - add support for other parameters var found = []; for (var v in volumes) { if (opt_localId && volumes[v].localId == opt_localId) { found.push(volumes[v]); } } return found; }; this.volumes.register = function (volume) { //TODO - add volume check //console.warn(self.EVENT_VOLUME_ADDED, volume); var exists = false; for (var v in volumes) { if (volumes[v] == volume) { exists = true; break; } } if (exists) { throw new Error(self.ERROR_VOLUME_ALREADY_REGISTERED, "Volume '" + volume.name + "' is already registered.", volume); } volumes.push(volume); self.Volume.events.fire(self.EVENT_VOLUME_ADDED, volume); }; this.volumes.unRegister = function (volume) { //TODO - add volume check //console.warn(self.EVENT_VOLUME_REMOVED, volume); for (var v in volumes) { if (volumes[v] == volume) { volumes.splice(v, 1); self.Volume.events.fire(self.EVENT_VOLUME_REMOVED, volume); return; } } throw new Error(self.ERROR_VOLUME_NOT_FOUND, "Volume '" + volume.name + "' was not registered.", volume); }; this.volumes.events = new event.Emitter(this); this.protocols = {}; this.protocols.register = function (config) { //check required parameters if (!config) { throw "Invalid configuration 'null'."; } if (typeof config.protocol === "undefined") { throw "Inavlid configuration. The 'protocol' parameter is missing."; } if (protocols[config.protocol]) { throw "Protocol '" + config.protocol + "://' is already registered."; } if (typeof config.module === "undefined") { throw "Inavlid configuration. The 'module' parameter is missing."; } if (typeof config.formats !== "undefined") { if (!type.isArray(config.formats)) { throw "Inavlid configuration. The 'formats' parameter should be of type 'Array'."; } for (var i = 0; i < config.formats.length; i++) { if (!type.isString(config.formats[i])) { throw "Inavlid configuration. The 'formats' parameter should be of type 'Array' that contains object of type 'String'."; } if (formats[config.formats[i]]) { throw "Format '" + config.formats[i] + "' is already registered."; } } } //add protocol to list protocols[config.protocol] = config.module; //add any formats if (typeof config.formats !== "undefined") { for (var f = 0; f < config.formats.length; f++) { formats[config.formats[f]] = config.module; } } //after everything is registered, fire events self.events.fire(self.EVENT_PROTOCOL_ADDED, config.protocol); if (typeof config.formats !== "undefined") { for (f = 0; f < config.formats.length; f++) { self.events.fire(self.EVENT_FORMAT_ADDED, config.formats[f]); } } }; this.events = new event.Emitter(this); // node.js polyfill if (typeof Buffer != "undefined" && typeof Buffer.prototype.subarray == "undefined") { Buffer.prototype.subarray = function (begin, end) { return this.slice(begin, end); }; } } var singleton; (function (obj, factory) { var supported = false; if (typeof define === "function" && (define.amd || define.using)) { define(factory); supported = true; } if (typeof module === "object" && module.exports && typeof require != "undefined" && typeof require.main != "undefined" && require.main !== module) { module.exports = factory(); supported = true; } if (!supported) { obj.returnExports = factory(); } }(this, function() { if (singleton) { return singleton; } singleton = new (Function.prototype.bind.apply(IO, arguments)); return singleton; })); })(); })(typeof using != "undefined"? using : null, typeof require != "undefined"? require : null);