diff options
Diffstat (limited to 'result_collection_api/3rd_party/static/lib/shred/content.js')
-rw-r--r-- | result_collection_api/3rd_party/static/lib/shred/content.js | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/result_collection_api/3rd_party/static/lib/shred/content.js b/result_collection_api/3rd_party/static/lib/shred/content.js new file mode 100644 index 0000000..b8051fe --- /dev/null +++ b/result_collection_api/3rd_party/static/lib/shred/content.js @@ -0,0 +1,193 @@ + +// The purpose of the `Content` object is to abstract away the data conversions +// to and from raw content entities as strings. For example, you want to be able +// to pass in a Javascript object and have it be automatically converted into a +// JSON string if the `content-type` is set to a JSON-based media type. +// Conversely, you want to be able to transparently get back a Javascript object +// in the response if the `content-type` is a JSON-based media-type. + +// One limitation of the current implementation is that it [assumes the `charset` is UTF-8](https://github.com/spire-io/shred/issues/5). + +// The `Content` constructor takes an options object, which *must* have either a +// `body` or `data` property and *may* have a `type` property indicating the +// media type. If there is no `type` attribute, a default will be inferred. +var Content = function(options) { + this.body = options.body; + this.data = options.data; + this.type = options.type; +}; + +Content.prototype = { + // Treat `toString()` as asking for the `content.body`. That is, the raw content entity. + // + // toString: function() { return this.body; } + // + // Commented out, but I've forgotten why. :/ +}; + + +// `Content` objects have the following attributes: +Object.defineProperties(Content.prototype,{ + +// - **type**. Typically accessed as `content.type`, reflects the `content-type` +// header associated with the request or response. If not passed as an options +// to the constructor or set explicitly, it will infer the type the `data` +// attribute, if possible, and, failing that, will default to `text/plain`. + type: { + get: function() { + if (this._type) { + return this._type; + } else { + if (this._data) { + switch(typeof this._data) { + case "string": return "text/plain"; + case "object": return "application/json"; + } + } + } + return "text/plain"; + }, + set: function(value) { + this._type = value; + return this; + }, + enumerable: true + }, + +// - **data**. Typically accessed as `content.data`, reflects the content entity +// converted into Javascript data. This can be a string, if the `type` is, say, +// `text/plain`, but can also be a Javascript object. The conversion applied is +// based on the `processor` attribute. The `data` attribute can also be set +// directly, in which case the conversion will be done the other way, to infer +// the `body` attribute. + data: { + get: function() { + if (this._body) { + return this.processor.parser(this._body); + } else { + return this._data; + } + }, + set: function(data) { + if (this._body&&data) Errors.setDataWithBody(this); + this._data = data; + return this; + }, + enumerable: true + }, + +// - **body**. Typically accessed as `content.body`, reflects the content entity +// as a UTF-8 string. It is the mirror of the `data` attribute. If you set the +// `data` attribute, the `body` attribute will be inferred and vice-versa. If +// you attempt to set both, an exception is raised. + body: { + get: function() { + if (this._data) { + return this.processor.stringify(this._data); + } else { + return this._body.toString(); + } + }, + set: function(body) { + if (this._data&&body) Errors.setBodyWithData(this); + this._body = body; + return this; + }, + enumerable: true + }, + +// - **processor**. The functions that will be used to convert to/from `data` and +// `body` attributes. You can add processors. The two that are built-in are for +// `text/plain`, which is basically an identity transformation and +// `application/json` and other JSON-based media types (including custom media +// types with `+json`). You can add your own processors. See below. + processor: { + get: function() { + var processor = Content.processors[this.type]; + if (processor) { + return processor; + } else { + // Return the first processor that matches any part of the + // content type. ex: application/vnd.foobar.baz+json will match json. + var main = this.type.split(";")[0]; + var parts = main.split(/\+|\//); + for (var i=0, l=parts.length; i < l; i++) { + processor = Content.processors[parts[i]] + } + return processor || {parser:identity,stringify:toString}; + } + }, + enumerable: true + }, + +// - **length**. Typically accessed as `content.length`, returns the length in +// bytes of the raw content entity. + length: { + get: function() { + if (typeof Buffer !== 'undefined') { + return Buffer.byteLength(this.body); + } + return this.body.length; + } + } +}); + +Content.processors = {}; + +// The `registerProcessor` function allows you to add your own processors to +// convert content entities. Each processor consists of a Javascript object with +// two properties: +// - **parser**. The function used to parse a raw content entity and convert it +// into a Javascript data type. +// - **stringify**. The function used to convert a Javascript data type into a +// raw content entity. +Content.registerProcessor = function(types,processor) { + +// You can pass an array of types that will trigger this processor, or just one. +// We determine the array via duck-typing here. + if (types.forEach) { + types.forEach(function(type) { + Content.processors[type] = processor; + }); + } else { + // If you didn't pass an array, we just use what you pass in. + Content.processors[types] = processor; + } +}; + +// Register the identity processor, which is used for text-based media types. +var identity = function(x) { return x; } + , toString = function(x) { return x.toString(); } +Content.registerProcessor( + ["text/html","text/plain","text"], + { parser: identity, stringify: toString }); + +// Register the JSON processor, which is used for JSON-based media types. +Content.registerProcessor( + ["application/json; charset=utf-8","application/json","json"], + { + parser: function(string) { + return JSON.parse(string); + }, + stringify: function(data) { + return JSON.stringify(data); }}); + +var qs = require('querystring'); +// Register the post processor, which is used for JSON-based media types. +Content.registerProcessor( + ["application/x-www-form-urlencoded"], + { parser : qs.parse, stringify : qs.stringify }); + +// Error functions are defined separately here in an attempt to make the code +// easier to read. +var Errors = { + setDataWithBody: function(object) { + throw new Error("Attempt to set data attribute of a content object " + + "when the body attributes was already set."); + }, + setBodyWithData: function(object) { + throw new Error("Attempt to set body attribute of a content object " + + "when the data attributes was already set."); + } +} +module.exports = Content;
\ No newline at end of file |