| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 | /** * @method DataMan * @public * @constructor * @param {File|Blob|ArrayBuffer|Uint8Array|String} data The data that you want to manipulate. * @param {String} [type] The data content (MIME) type, if known. Required if the first argument is an ArrayBuffer, Uint8Array, or URL */DataMan = function DataMan(data, type) {  var self = this;  if (!data) {    throw new Error("DataMan constructor requires a data argument");  }  // The end result of all this is that we will have one of the following set:  // - self.blob  // - self.url  // Unless we already have in-memory data, we don't load anything into memory  // and instead rely on obtaining a read stream when the time comes.  if (typeof File !== "undefined" && data instanceof File) {    self.blob = data; // File inherits from Blob so this is OK    self._type = data.type;  } else if (typeof Blob !== "undefined" && data instanceof Blob) {    self.blob = data;    self._type = data.type;  } else if (typeof ArrayBuffer !== "undefined" && data instanceof ArrayBuffer || EJSON.isBinary(data)) {    if (typeof Blob === "undefined") {      throw new Error("Browser must support Blobs to handle an ArrayBuffer or Uint8Array");    }    if (!type) {      throw new Error("DataMan constructor requires a type argument when passed an ArrayBuffer or Uint8Array");    }    self.blob = new Blob([data], {type: type});    self._type = type;  } else if (typeof data === "string") {    if (data.slice(0, 5) === "data:") {      self._type = data.slice(5, data.indexOf(';'));      self.blob = dataURItoBlob(data, self._type);    } else if (data.slice(0, 5) === "http:" || data.slice(0, 6) === "https:") {      if (!type) {        throw new Error("DataMan constructor requires a type argument when passed a URL");      }      self.url = data;      self._type = type;    } else {      throw new Error("DataMan constructor received unrecognized data string");    }  } else {    throw new Error("DataMan constructor received data that it doesn't support");  }};/** * @method DataMan.prototype.getBlob * @public * @param {Function} [callback] - callback(error, blob) * @returns {undefined|Blob} * * Passes a Blob representing this data to a callback or returns * the Blob if no callback is provided. A callback is required * if getting a Blob for a URL. */DataMan.prototype.getBlob = function dataManGetBlob(callback) {  var self = this;  if (callback) {    if (self.blob) {      callback(null, self.blob);    } else if (self.url) {      var xhr = new XMLHttpRequest();      xhr.open('GET', self.url, true);      xhr.responseType = "blob";      xhr.onload = function(data) {        self.blob = xhr.response;        callback(null, self.blob);      };      xhr.onerror = function(err) {        callback(err);      };      xhr.send();    }  } else {    if (self.url)      throw new Error('DataMan.getBlob requires a callback when managing a URL');    return self.blob;  }};/** * @method DataMan.prototype.getBinary * @public * @param {Number} [start] - First byte position to read. * @param {Number} [end] - Last byte position to read. * @param {Function} callback - callback(error, binaryData) * @returns {undefined} * * Passes a Uint8Array representing this data to a callback. */DataMan.prototype.getBinary = function dataManGetBinary(start, end, callback) {  var self = this;  if (typeof start === "function") {    callback = start;  }  callback = callback || defaultCallback;  function read(blob) {    if (typeof FileReader === "undefined") {      callback(new Error("Browser does not support FileReader"));      return;    }    var reader = new FileReader();    reader.onload = function(evt) {      callback(null, new Uint8Array(evt.target.result));    };    reader.onerror = function(err) {      callback(err);    };    reader.readAsArrayBuffer(blob);  }  self.getBlob(function (error, blob) {    if (error) {      callback(error);    } else {      if (typeof start === "number" && typeof end === "number") {        var size = blob.size;        // Return the requested chunk of binary data        if (start >= size) {          callback(new Error("DataMan.getBinary: start position beyond end of data (" + size + ")"));          return;        }        end = Math.min(size, end);        var slice = blob.slice || blob.webkitSlice || blob.mozSlice;        if (typeof slice === 'undefined') {          callback(new Error('Browser does not support File.slice'));          return;        }        read(slice.call(blob, start, end, self._type));      } else {        // Return the entire binary data        read(blob);      }    }  });};/** @method DataMan.prototype.saveAs * @public * @param {String} [filename] * @return {undefined} * * Tells the browser to save the data like a normal downloaded file, * using the provided filename. * */DataMan.prototype.saveAs = function dataManSaveAs(filename) {  var self = this;  if (typeof window === "undefined")    throw new Error("window must be defined to use saveLocal");  if (!window.saveAs) {    console.warn('DataMan.saveAs: window.saveAs not supported by this browser - add cfs-filesaver package');    return;  }  self.getBlob(function (error, blob) {    if (error) {      throw error;    } else {      window.saveAs(blob, filename);    }  });};/** * @method DataMan.prototype.getDataUri * @public * @param {function} callback callback(err, dataUri) */DataMan.prototype.getDataUri = function dataManGetDataUri(callback) {  // XXX: We could consider using: URL.createObjectURL(blob);  // This will create a reference to the blob data instead of a clone  // This is part of the File API - as the rest - Not sure how to generally  // support from IE10, FF26, Chrome 31, safari 7, opera 19, ios 6, android 4  var self = this;  if (typeof callback !== 'function')    throw new Error("getDataUri requires callback function");  if (typeof FileReader === "undefined") {    callback(new Error("Browser does not support FileReader"));    return;  }  var fileReader = new FileReader();  fileReader.onload = function(event) {    var dataUri = event.target.result;    callback(null, dataUri);  };  fileReader.onerror = function(err) {    callback(err);  };  self.getBlob(function (error, blob) {    if (error) {      callback(error);    } else {      fileReader.readAsDataURL(blob);    }  });};/** * @method DataMan.prototype.size * @public * @param {function} [callback] callback(err, size) * * Passes the size of the data to the callback, if provided, * or returns it. A callback is required to get the size of a URL on the client. */DataMan.prototype.size = function dataManSize(callback) {  var self = this;  if (callback) {    if (typeof self._size === "number") {      callback(null, self._size);    } else {      self.getBlob(function (error, blob) {        if (error) {          callback(error);        } else {          self._size = blob.size;          callback(null, self._size);        }      });    }  } else {    if (self.url) {      throw new Error("On the client, DataMan.size requires a callback when getting size for a URL on the client");    } else if (typeof self._size === "number") {      return self._size;    } else {      var blob = self.getBlob();      self._size = blob.size;      return self._size;    }  }};/** * @method DataMan.prototype.type * @public * * Returns the type of the data. */DataMan.prototype.type = function dataManType() {  return this._type;};/** * @method dataURItoBlob * @private * @param {String} dataURI The data URI * @param {String} dataTYPE The content type * @returns {Blob} A new Blob instance * * Converts a data URI to a Blob. */function dataURItoBlob(dataURI, dataTYPE) {  var str = atob(dataURI.split(',')[1]), array = [];  for(var i = 0; i < str.length; i++) array.push(str.charCodeAt(i));  return new Blob([new Uint8Array(array)], {type: dataTYPE});}/** * @method defaultCallback * @private * @param {Error} [err] * @returns {undefined} * * Can be used as a default callback for client methods that need a callback. * Simply throws the provided error if there is one. */function defaultCallback(err) {  if (err) {    // Show gentle error if Meteor error    if (err instanceof Meteor.Error) {      console.error(err.message);    } else {      // Normal error, just throw error      throw err;    }  }}
 |