diff --git a/.gitignore b/.gitignore
index 318b19e..bea75e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,5 @@ coverage
test/test-bundle.js
npm-debug.log
dist
+doc
+api.md
diff --git a/.jshintrc b/.jshintrc
index 8b98e1a..6fa9ba0 100755
--- a/.jshintrc
+++ b/.jshintrc
@@ -15,7 +15,7 @@
"globals": { "Pouch": true},
"white": true,
"indent": 2,
- "maxlen": 100,
+ "maxlen": 120,
"predef": [
"process",
"global",
diff --git a/README.md b/README.md
index 52a39d5..1423d0c 100644
--- a/README.md
+++ b/README.md
@@ -3,21 +3,26 @@ blob-util
[](https://travis-ci.org/nolanlawson/blob-util)
-You know what's cool? [HTML5 Blobs](https://developer.mozilla.org/en-US/docs/Web/API/Blob?redirectlocale=en-US&redirectslug=DOM%2FBlob).
+`blob-util` is a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob?redirectlocale=en-US&redirectslug=DOM%2FBlob) library for busy people.
-You know what's hard to work with? Yeah, you guessed it.
+If you want an easy way to work with binary data in the browser, or you don't even know what a Blob is, then this is the library for you.
-If you just want to work with binary data in the browser and not pull your hair out, then this is the library for you.
+`blob-util` offers cross-browser utilities for translating Blobs to and from different formats:
-`blob-util` offers various utilities for transforming Blobs between different formats (base 64, data URL, image), and it works
-cross-browser.
+* `` tags
+* base 64 strings
+* binary strings
+* ArrayBuffers
+* data URLs
It's also a good pairing with the attachment API in [PouchDB](http://pouchdb.com).
-Usage
+`blob-util` is a browser library. If you need to work with binary data in Node.js, see [Buffers](http://nodejs.org/api/buffer.html).
+
+Install
------
-Grab it from the `dist/` folder above, or npm:
+Grab it from the `dist/` folder above, or use NPM:
```
npm install blob-util
@@ -39,19 +44,222 @@ Now you have a `window.blobUtil` object that contains the API. Or if you don't l
globals, you can use Browserify.
-Building
+Quick Start
+--------
+
+Here's Kirby. He's a famous little Blob.
+
+
+
+So let's fulfill his manifest destiny, and convert him to a real `Blob` object.
+
+```js
+var img = document.getElementById('kirby');
+
+blobUtil.imgSrcToBlob(img.src).then(function (blob) {
+ // ladies and gents, we have a blob
+}).catch(function (err) {
+ // image failed to load
+})
+```
+
+(Don't worry, this won't download the image twice, because browsers are smart.)
+
+Now that we have a `Blob`, we can convert it to a URL and use that as the source for another `` tag:
+
+```js
+var blobURL = blobUtil.createObjectURL(blob);
+
+var newImg = document.createElement('img');
+
+newImg.src = blobURL;
+```
+
+So now we have to Kirbys - one with a normal URL, and the other with a blob URL. Neato!
+
+API
+-------
+
+Warning: this API uses [Promises](https://promisesaplus.com/), because it's not 2009 anymore.
+
+###Overview
+
+* [createBlob(parts, options)](#createBlob)
+* [createObjectURL(blob)](#createObjectURL)
+* [revokeObjectURL(url)](#revokeObjectURL)
+* [blobToBinaryString(blob)](#blobToBinaryString)
+* [base64StringToBlob(base64, type)](#base64StringToBlob)
+* [binaryStringToBlob(binary, type)](#binaryStringToBlob)
+* [blobToBase64String(blob)](#blobToBase64String)
+* [dataURLToBlob(dataURL)](#dataURLToBlob)
+* [imgSrcToDataURL(src, type)](#imgSrcToDataURL)
+* [canvasToBlob(canvas, type)](#canvasToBlob)
+* [imgSrcToBlob(src, type)](#imgSrcToBlob)
+* [arrayBufferToBlob(buffer, type)](#arrayBufferToBlob)
+* [blobToArrayBuffer(blob)](#blobToArrayBuffer)
+
+
+###createBlob(parts, options)
+Shim for
+[new Blob()](https://developer.mozilla.org/en-US/docs/Web/API/Blob.Blob)
+to support
+[older browsers that use the deprecated
BlobBuilder API](http://caniuse.com/blob).
+
+**Params**
+
+- parts `Array` - content of the Blob
+- options `Object` - usually just {type: myContentType}
+
+**Returns**: `Blob`
+
+###createObjectURL(blob)
+Shim for
+[URL.createObjectURL()](https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL)
+to support browsers that only have the prefixed
+webkitURL (e.g. Android <4.4).
+
+**Params**
+
+- blob `Blob`
+
+**Returns**: `string` - url
+
+###revokeObjectURL(url)
+Shim for
+[URL.revokeObjectURL()](https://developer.mozilla.org/en-US/docs/Web/API/URL.revokeObjectURL)
+to support browsers that only have the prefixed
+webkitURL (e.g. Android <4.4).
+
+**Params**
+
+- url `string`
+
+
+###blobToBinaryString(blob)
+Convert a Blob to a binary string. Returns a Promise.
+
+**Params**
+
+- blob `Blob`
+
+**Returns**: `Promise` - Promise that resolves with the binary string
+
+###base64StringToBlob(base64, type)
+Convert a base64-encoded string to a Blob. Returns a Promise.
+
+**Params**
+
+- base64 `string`
+- type `string` | `undefined` - the content type (optional)
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###binaryStringToBlob(binary, type)
+Convert a binary string to a Blob. Returns a Promise.
+
+**Params**
+
+- binary `string`
+- type `string` | `undefined` - the content type (optional)
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###blobToBase64String(blob)
+Convert a Blob to a binary string. Returns a Promise.
+
+**Params**
+
+- blob `Blob`
+
+**Returns**: `Promise` - Promise that resolves with the binary string
+
+###dataURLToBlob(dataURL)
+Convert a data URL string
+(e.g. 'data:image/png;base64,iVBORw0KG...')
+to a Blob. Returns a Promise.
+
+**Params**
+
+- dataURL `string`
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###imgSrcToDataURL(src, type)
+Convert an image's src URL to a data URL by loading the image and painting
+it to a canvas. Returns a Promise.
+
+
canvas to a Blob. Returns a Promise.
+
+**Params**
+
+- canvas `string`
+- type `string` | `undefined` - the content type (optional, defaults to 'image/png')
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###imgSrcToBlob(src, type)
+Convert an image's src URL to a Blob by loading the image and painting
+it to a canvas. Returns a Promise.
+
+Note: this will coerce the image to the desired content type, and it
+will only paint the first frame of an animated GIF.
+
+**Params**
+
+- src `string`
+- type `string` | `undefined` - the content type (optional, defaults to 'image/png')
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###arrayBufferToBlob(buffer, type)
+Convert an ArrayBuffer to a Blob. Returns a Promise.
+
+**Params**
+
+- buffer `ArrayBuffer`
+- type `string` | `undefined` - the content type (optional)
+
+**Returns**: `Promise` - Promise that resolves with the Blob
+
+###blobToArrayBuffer(blob)
+Convert a Blob to an ArrayBuffer. Returns a Promise.
+
+**Params**
+
+- blob `Blob`
+
+**Returns**: `Promise` - Promise that resolves with the ArrayBuffer
+
+
+
+
+
+Building the library
----
npm install
npm run build
Your plugin is now located at `dist/pouchdb.mypluginname.js` and `dist/pouchdb.mypluginname.min.js` and is ready for distribution.
+To generate documentation:
+
+ npm run jsdoc
-Testing
+Testing the library
----
-
### In the browser
Run `npm run dev` and then point your favorite browser to [http://127.0.0.1:8001/test/index.html](http://127.0.0.1:8001/test/index.html).
diff --git a/index.js b/index.js
index a6c9646..fc396ab 100644
--- a/index.js
+++ b/index.js
@@ -1,26 +1,23 @@
'use strict';
var utils = require('./utils');
-var blob = require('blob');
+/* jshint -W079 */
+var Blob = require('blob');
var Promise = utils.Promise;
-function createBlob(parts, opts) {
- opts = opts || {};
- if (typeof opts === 'string') {
- opts = {type: opts}; // do you a solid here
- }
- return new blob(parts, opts);
-}
+//
+// PRIVATE
+//
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
// encode-decode-image-with-base64-breaks-image (2013-04-21)
-function binaryStringToArrayBuffer(bin) {
- var length = bin.length;
+function binaryStringToArrayBuffer(binary) {
+ var length = binary.length;
var buf = new ArrayBuffer(length);
var arr = new Uint8Array(buf);
var i = -1;
while (++i < length) {
- arr[i] = bin.charCodeAt(i);
+ arr[i] = binary.charCodeAt(i);
}
return buf;
}
@@ -39,46 +36,8 @@ function arrayBufferToBinaryString(buffer) {
return binary;
}
-// shim for browsers that don't support it
-function blobToBinaryString(blob) {
- return new Promise(function (resolve, reject) {
- var reader = new FileReader();
- var hasBinaryString = typeof reader.readAsBinaryString === 'function';
- reader.onloadend = function (e) {
- var result = e.target.result || '';
- if (hasBinaryString) {
- return resolve(result);
- }
- resolve(arrayBufferToBinaryString(result));
- };
- reader.onerror = reject;
- if (hasBinaryString) {
- reader.readAsBinaryString(blob);
- } else {
- reader.readAsArrayBuffer(blob);
- }
- });
-}
-
-function base64StringToBlob(base64, type) {
- return Promise.resolve().then(function () {
- var parts = [binaryStringToArrayBuffer(atob(base64))];
- return type ? createBlob(parts, {type: type}) : createBlob(parts);
- });
-}
-
-function binaryStringToBlob(binary, type) {
- return Promise.resolve().then(function () {
- return base64StringToBlob(btoa(binary), type);
- });
-}
-
-function blobToBase64String(blob) {
- return blobToBinaryString(blob).then(function (binary) {
- return btoa(binary);
- });
-}
-
+// doesn't download the image more than once, because
+// browsers aren't dumb. uses the cache
function loadImage(src) {
return new Promise(function (resolve, reject) {
var img = new Image();
@@ -91,25 +50,6 @@ function loadImage(src) {
});
}
-function dataURLToBlob(dataURL) {
- return Promise.resolve().then(function () {
- var type = dataURL.match(/data:([^;]+)/)[1];
- var base64 = dataURL.replace(/^[^,]+,/, '');
-
- return createBlob([binaryStringToArrayBuffer(atob(base64))], {type: type});
- });
-}
-
-function createObjectURL(blob) {
- var compatURL = window.URL || window.webkitURL;
- return compatURL.createObjectURL(blob);
-}
-
-function revokeObjectURL(url) {
- var compatURL = window.URL || window.webkitURL;
- return compatURL.revokeObjectURL(url);
-}
-
function imgToCanvas(img) {
var canvas = document.createElement('canvas');
@@ -128,6 +68,141 @@ function imgToCanvas(img) {
return canvas;
}
+//
+// PUBLIC
+//
+
+/**
+ * Shim for
+ * [new Blob()]{@link https://developer.mozilla.org/en-US/docs/Web/API/Blob.Blob}
+ * to support
+ * [older browsers that use the deprecated BlobBuilder API]{@link http://caniuse.com/blob}.
+ *
+ * @param {Array} parts - content of the Blob
+ * @param {Object} options - usually just {type: myContentType}
+ * @returns {Blob}
+ */
+function createBlob(parts, options) {
+ options = options || {};
+ if (typeof options === 'string') {
+ options = {type: options}; // do you a solid here
+ }
+ return new Blob(parts, options);
+}
+
+/**
+ * Shim for
+ * [URL.createObjectURL()]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL}
+ * to support browsers that only have the prefixed
+ * webkitURL (e.g. Android <4.4).
+ * @param {Blob} blob
+ * @returns {string} url
+ */
+function createObjectURL(blob) {
+ return (window.URL || window.webkitURL).createObjectURL(blob);
+}
+
+/**
+ * Shim for
+ * [URL.revokeObjectURL()]{@link https://developer.mozilla.org/en-US/docs/Web/API/URL.revokeObjectURL}
+ * to support browsers that only have the prefixed
+ * webkitURL (e.g. Android <4.4).
+ * @param {string} url
+ */
+function revokeObjectURL(url) {
+ return (window.URL || window.webkitURL).revokeObjectURL(url);
+}
+
+/**
+ * Convert a Blob to a binary string. Returns a Promise.
+ *
+ * @param {Blob} blob
+ * @returns {Promise} Promise that resolves with the binary string
+ */
+function blobToBinaryString(blob) {
+ return new Promise(function (resolve, reject) {
+ var reader = new FileReader();
+ var hasBinaryString = typeof reader.readAsBinaryString === 'function';
+ reader.onloadend = function (e) {
+ var result = e.target.result || '';
+ if (hasBinaryString) {
+ return resolve(result);
+ }
+ resolve(arrayBufferToBinaryString(result));
+ };
+ reader.onerror = reject;
+ if (hasBinaryString) {
+ reader.readAsBinaryString(blob);
+ } else {
+ reader.readAsArrayBuffer(blob);
+ }
+ });
+}
+
+/**
+ * Convert a base64-encoded string to a Blob. Returns a Promise.
+ * @param {string} base64
+ * @param {string|undefined} type - the content type (optional)
+ * @returns {Promise} Promise that resolves with the Blob
+ */
+function base64StringToBlob(base64, type) {
+ return Promise.resolve().then(function () {
+ var parts = [binaryStringToArrayBuffer(atob(base64))];
+ return type ? createBlob(parts, {type: type}) : createBlob(parts);
+ });
+}
+
+/**
+ * Convert a binary string to a Blob. Returns a Promise.
+ * @param {string} binary
+ * @param {string|undefined} type - the content type (optional)
+ * @returns {Promise} Promise that resolves with the Blob
+ */
+function binaryStringToBlob(binary, type) {
+ return Promise.resolve().then(function () {
+ return base64StringToBlob(btoa(binary), type);
+ });
+}
+
+/**
+ * Convert a Blob to a binary string. Returns a Promise.
+ * @param {Blob} blob
+ * @returns {Promise} Promise that resolves with the binary string
+ */
+function blobToBase64String(blob) {
+ return blobToBinaryString(blob).then(function (binary) {
+ return btoa(binary);
+ });
+}
+
+/**
+ * Convert a data URL string
+ * (e.g. 'data:image/png;base64,iVBORw0KG...')
+ * to a Blob. Returns a Promise.
+ * @param {string} dataURL
+ * @returns {Promise} Promise that resolves with the Blob
+ */
+function dataURLToBlob(dataURL) {
+ return Promise.resolve().then(function () {
+ var type = dataURL.match(/data:([^;]+)/)[1];
+ var base64 = dataURL.replace(/^[^,]+,/, '');
+
+ var buff = binaryStringToArrayBuffer(atob(base64));
+ return createBlob([buff], {type: type});
+ });
+}
+
+/**
+ * Convert an image's src URL to a data URL by loading the image and painting
+ * it to a canvas. Returns a Promise.
+ *
+ * Note: this will coerce the image to the desired content type, and it
+ * will only paint the first frame of an animated GIF.
+ *
+ * @param {string} src
+ * @param {string|undefined} type - the content type (optional, defaults to 'image/png')
+ * @returns {Promise} Promise that resolves with the data URL string
+ */
function imgSrcToDataURL(src, type) {
type = type || 'image/png';
@@ -138,12 +213,14 @@ function imgSrcToDataURL(src, type) {
});
}
-function imgSrcToBlob(src, type) {
- type = type || 'image/png';
-
- return loadImage(src).then(function (img) {
- return imgToCanvas(img);
- }).then(function (canvas) {
+/**
+ * Convert a canvas to a Blob. Returns a Promise.
+ * @param {string} canvas
+ * @param {string|undefined} type - the content type (optional, defaults to 'image/png')
+ * @returns {Promise} Promise that resolves with the Blob
+ */
+function canvasToBlob(canvas, type) {
+ return Promise.resolve().then(function () {
if (typeof canvas.toBlob === 'function') {
return new Promise(function (resolve) {
canvas.toBlob(resolve, type);
@@ -153,12 +230,45 @@ function imgSrcToBlob(src, type) {
});
}
+/**
+ * Convert an image's src URL to a Blob by loading the image and painting
+ * it to a canvas. Returns a Promise.
+ *
+ * Note: this will coerce the image to the desired content type, and it
+ * will only paint the first frame of an animated GIF.
+ *
+ * @param {string} src
+ * @param {string|undefined} type - the content type (optional, defaults to 'image/png')
+ * @returns {Promise} Promise that resolves with the Blob
+ */
+function imgSrcToBlob(src, type) {
+ type = type || 'image/png';
+
+ return loadImage(src).then(function (img) {
+ return imgToCanvas(img);
+ }).then(function (canvas) {
+ return canvasToBlob(canvas, type);
+ });
+}
+
+/**
+ * Convert an ArrayBuffer to a Blob. Returns a Promise.
+ *
+ * @param {ArrayBuffer} buffer
+ * @param {string|undefined} type - the content type (optional)
+ * @returns {Promise} Promise that resolves with the Blob
+ */
function arrayBufferToBlob(buffer, type) {
return Promise.resolve().then(function () {
return createBlob([buffer], type);
});
}
+/**
+ * Convert a Blob to an ArrayBuffer. Returns a Promise.
+ * @param {Blob} blob
+ * @returns {Promise} Promise that resolves with the ArrayBuffer
+ */
function blobToArrayBuffer(blob) {
return blobToBinaryString(blob).then(function (binary) {
return binaryStringToArrayBuffer(binary);
@@ -171,6 +281,7 @@ module.exports = {
revokeObjectURL : revokeObjectURL,
imgSrcToBlob : imgSrcToBlob,
imgSrcToDataURL : imgSrcToDataURL,
+ canvasToBlob : canvasToBlob,
dataURLToBlob : dataURLToBlob,
blobToBase64String : blobToBase64String,
base64StringToBlob : base64StringToBlob,
diff --git a/package.json b/package.json
index 1932677..52554f7 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,7 @@
"url": "https://github.com/nolanlawson/blob-util/issues"
},
"scripts": {
- "test-node": "istanbul test ./node_modules/mocha/bin/_mocha test/test.js",
+ "test-node": "echo 'node tests disabled'",
"test-browser": "./bin/test-browser.js",
"jshint": "jshint -c .jshintrc *.js test/test.js",
"test": "npm run jshint && ./bin/run-test.sh",
@@ -28,7 +28,8 @@
"min": "uglifyjs dist/blob-util.js -mc > dist/blob-util.min.js",
"dev": "browserify -s blobUtil test/test.js > test/test-bundle.js && npm run dev-server",
"dev-server": "./bin/dev-server.js",
- "coverage": "npm test --coverage && istanbul check-coverage --lines 100 --function 100 --statements 100 --branches 100"
+ "coverage": "npm test --coverage && istanbul check-coverage --lines 100 --function 100 --statements 100 --branches 100",
+ "jsdoc": "jsdoc2md --heading-depth 3 ./index.js > api.md"
},
"dependencies": {
"blob": "0.0.4",
@@ -42,6 +43,8 @@
"chai-as-promised": "~4.1.0",
"http-server": "~0.5.5",
"istanbul": "^0.2.7",
+ "jsdoc": "^3.3.0-alpha10",
+ "jsdoc-to-markdown": "^0.5.9",
"jshint": "~2.3.0",
"mocha": "~1.18",
"phantomjs": "^1.9.7-5",