Permalink
Browse files

Added `DataURL` object

  • Loading branch information...
1 parent a44b2b2 commit 013f965fa3fed58a719eede552cb366b5e827091 @ZER0 committed Mar 19, 2012
Showing with 221 additions and 3 deletions.
  1. +43 −0 packages/api-utils/docs/url.md
  2. +123 −1 packages/api-utils/lib/url.js
  3. +55 −2 packages/api-utils/tests/test-url.js
@@ -83,3 +83,46 @@ The `url` module provides functionality for the parsing and retrieving of URLs.
The converted URL as a string.
</api>
+<api name="DataURL">
+@class
+<api name="DataURL">
+@constructor
+ The DataURL constructor creates an object that represents a data: URL,
+ verifying that the provided string is a valid data: URL in the process.
+
+@param uri {string}
+ A string to be parsed as Data URL. If is not a valid URI, this constructor
+ will throw an exception.
+</api>
+
+<api name="mimeType">
+@property {string}
+ The MIME type of the data. By default is empty, that accordingly to RFC2397
+ is equivalent to `text/plain`
+</api>
+
+<api name="parameters">
+@property {object}
+ An hashmap that contains the parameters of the Data URL. By default is
+ empty, that accordingly to RFC2397 is equivalent to `{"charset" : "US-ASCII"}`
+</api>
+
+<api name="base64">
+@property {boolean}
+ Defines the encoding for the value in `data` property.
+</api>
+
+<api name="data">
+@property {string}
+ The string that contains the data in the Data URL.
+ If the `uri` given to the constructor contains `base64` parameter, this string is decoded.
+</api>
+
+<api name="toString">
+@method
+ Returns a string representation of the Data URL.
+ If `base64` is `true`, the `data` property is encoded using base-64 encoding.
+@returns {string}
+ The URL as a string.
+</api>
+</api>
@@ -4,7 +4,8 @@
"use strict";
-const {Cc,Ci,Cr} = require("chrome");
+const { Cc, Ci, Cr, atob, btoa } = require("chrome");
+const { Base, Class } = require('./base');
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
@@ -111,3 +112,124 @@ function URL(url, base) {
URL.prototype = Object.create(String.prototype);
exports.URL = URL;
+
+/**
+ * Parse and serialize a Data URL.
+ *
+ * See: http://tools.ietf.org/html/rfc2397
+ *
+ * Note: Could be extended in the future to decode / encode automatically binary
+ * data.
+ */
+const DataURL = Base.extend({
+
+ /**
+ * The MIME type of the data. By default is empty, that accordingly to RFC
+ * is equivalent to "text/plain"
+ */
+ mimeType: "",
+
+ /**
+ * An hashmap that contains the parameters of the Data URL. By default is
+ * empty, that accordingly to RFC is equivalent to {"charset" : "US-ASCII"}
+ */
+ parameters: null,
+
+ /**
+ * The string that represent the data in the Data URL
+ */
+ data: "",
+
+ /**
+ *
+ */
+ get base64 () {
+ return "base64" in this.parameters;
+ },
+
+ set base64 (value) {
+ if (value)
+ this.parameters["base64"] = "";
+ else
+ delete this.parameters["base64"];
+ },
+ /**
+ * Initialize the Data URL object. If a uri is give, it will be parsed.
+ *
+ * @param {String} [uri] The uri to parse
+ *
+ * @throws {URIError} if the Data URL is malformed
+ */
+ initialize: function(uri) {
+ this.merge({
+ parameters: {},
+ mimeType: this.mimeType,
+ data: this.data
+ })
+
+ if (!uri)
+ return;
+
+ uri = String(uri);
+
+ let matches = uri.match(/^data:([^,]*),([\s\S]*)/i);
+
+ if (!matches)
+ throw new URIError("Malformed Data URL: " + uri);
+
+ let mediaType = matches[1].trim();
+
+ this.data = decodeURIComponent(matches[2].trim());
+
+ if (!mediaType)
+ return;
+
+ let parametersList = mediaType.split(";");
+
+ this.mimeType = parametersList.shift().trim();
+
+ for (let parameter, i = 0; parameter = parametersList[i++];) {
+ let pairs = parameter.split("=");
+ let name = pairs[0].trim();
+ let value = pairs.length > 1 ? decodeURIComponent(pairs[1].trim()) : "";
+
+ this.parameters[name] = value;
+ }
+
+ if (this.base64)
+ this.data = atob(this.data);
+
+ },
+
+ /**
+ * Returns the object as a valid Data URL string
+ *
+ * @returns {String} The Data URL
+ */
+ toString : function() {
+ let parametersList = [];
+
+ for (let name in this.parameters) {
+ let encodedParameter = encodeURIComponent(name);
+ let value = this.parameters[name];
+
+ if (value)
+ encodedParameter += "=" + encodeURIComponent(value);
+
+ parametersList.push(encodedParameter);
+ }
+
+ if (parametersList.length > 0)
+ parametersList.unshift("");
+
+
+ let data = this.base64 ? btoa(this.data) : this.data;
+
+ return "data:" +
+ this.mimeType +
+ parametersList.join(";") + "," +
+ encodeURIComponent(data);
+ }
+});
+
+exports.DataURL = Class(DataURL);
@@ -151,11 +151,11 @@ exports.testURL = function(test) {
"h:foo",
"toString should roundtrip");
// test relative + base
- test.assertEqual(URL("mypath", "http://foo").toString(),
+ test.assertEqual(URL("mypath", "http://foo").toString(),
"http://foo/mypath",
"relative URL resolved to base");
// test relative + no base
- test.assertRaises(function() URL("path").toString(),
+ test.assertRaises(function() URL("path").toString(),
"malformed URI: path",
"no base for relative URI should throw");
@@ -202,3 +202,56 @@ exports.testStringInterface = function(test) {
test.assertEqual(a.trimRight(), EM.trimRight(), "trimRight on URL works.");
test.assertEqual(a.trimLeft(), EM.trimLeft(), "trimLeft on URL works.");
}
+
+exports.testDataURLwithouthURI = function (test) {
+ const { DataURL } = url;
+
+ let dataURL = new DataURL();
+
+ test.assertEqual(dataURL.base64, false, "base64 is false for empty uri")
+ test.assertEqual(dataURL.data, "", "data is an empty string for empty uri")
+ test.assertEqual(dataURL.mimeType, "", "mimeType is an empty string for empty uri")
+ test.assertEqual(Object.keys(dataURL.parameters).length, 0, "parameters is an empty object for empty uri");
+
+ test.assertEqual(dataURL.toString(), "data:,");
+}
+
+exports.testDataURLwithMalformedURI = function (test) {
+ const { DataURL } = url;
+
+ test.assertRaises(function() {
+ let dataURL = new DataURL("http://www.mozilla.com/");
+ },
+ "Malformed Data URL: http://www.mozilla.com/",
+ "DataURL raises an exception for malformed data uri"
+ );
+}
+
+exports.testDataURLparse = function (test) {
+ const { DataURL } = url;
+
+ let dataURL = new DataURL("data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E");
+
+ test.assertEqual(dataURL.base64, false, "base64 is false for non base64 data uri")
+ test.assertEqual(dataURL.data, "<h1>Hello!</h1>", "data is properly decoded")
+ test.assertEqual(dataURL.mimeType, "text/html", "mimeType is set properly")
+ test.assertEqual(Object.keys(dataURL.parameters).length, 1, "one parameters specified");
+ test.assertEqual(dataURL.parameters["charset"], "US-ASCII", "charset parsed");
+
+ test.assertEqual(dataURL.toString(), "data:text/html;charset=US-ASCII,%3Ch1%3EHello!%3C%2Fh1%3E");
+}
+
+exports.testDataURLparseBase64 = function (test) {
+ const { DataURL } = url;
+ const { base64Decode } = require("api-utils/utils/data");
+
+ let base64Data = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAQ0lEQVRYhe3OwQkAIBTD0Oyqg7idbqUr9B9EhBRyLY8F+0akEyBAgIBvAI1eCuaIEiBAgAABzwH50sNqAgQIEPAYcABJQw5EXdmcNgAAAABJRU5ErkJggg==";
+ let dataURL = new DataURL("data:image/png;base64," + base64Data);
+
+ test.assertEqual(dataURL.base64, true, "base64 is true for base64 encoded data uri")
+ test.assertEqual(dataURL.data, base64Decode(base64Data), "data is properly decoded")
+ test.assertEqual(dataURL.mimeType, "image/png", "mimeType is set properly")
+ test.assertEqual(Object.keys(dataURL.parameters).length, 1, "one parameters specified");
+
+ test.assertEqual(dataURL.toString(), "data:image/png;base64," + encodeURIComponent(base64Data));
+}

0 comments on commit 013f965

Please sign in to comment.