From a21886df00513436e039fe5a55276449cbf1b2ef Mon Sep 17 00:00:00 2001 From: gits2501 Date: Sun, 1 Apr 2018 13:44:04 +0200 Subject: [PATCH] Added testing and coverage --- .eslintrc.yml | 254 +++++++++++++++++++++++++++++++++++++ .gitignore | 1 + .travis.yml | 10 ++ README.md | 41 ++++++ package.json | 15 ++- src/utils.js | 241 ++---------------------------------- test/customerror.js | 45 ++++++- utils.js | 296 -------------------------------------------- 8 files changed, 373 insertions(+), 530 deletions(-) create mode 100644 .eslintrc.yml create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 README.md delete mode 100644 utils.js diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..ef6836d --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,254 @@ +env: + browser: true + commonjs: true + es6: true + node: true +extends: 'eslint:recommended' +rules: + accessor-pairs: error + array-bracket-newline: error + array-bracket-spacing: error + array-callback-return: 'off' + array-element-newline: error + arrow-body-style: error + arrow-parens: error + arrow-spacing: error + block-scoped-var: error + block-spacing: + - error + - never + brace-style: + - error + - stroustrup + callback-return: error + camelcase: error + capitalized-comments: + - error + - never + class-methods-use-this: error + comma-dangle: error + comma-spacing: 'off' + comma-style: + - error + - last + complexity: error + computed-property-spacing: + - error + - never + consistent-return: error + consistent-this: error + curly: 'off' + default-case: error + dot-location: error + dot-notation: 'off' + eol-last: error + eqeqeq: error + for-direction: error + func-call-spacing: error + func-name-matching: error + func-names: + - error + - never + func-style: + - error + - declaration + function-paren-newline: error + generator-star-spacing: error + getter-return: error + global-require: error + guard-for-in: 'off' + handle-callback-err: error + id-blacklist: error + id-length: 'off' + id-match: error + implicit-arrow-linebreak: error + indent: 'off' + indent-legacy: 'off' + init-declarations: 'off' + jsx-quotes: error + key-spacing: 'off' + keyword-spacing: 'off' + line-comment-position: 'off' + linebreak-style: + - error + - unix + lines-around-comment: error + lines-around-directive: error + lines-between-class-members: error + max-depth: error + max-len: 'off' + max-lines: error + max-nested-callbacks: error + max-params: error + max-statements: 'off' + max-statements-per-line: error + multiline-comment-style: + - error + - separate-lines + multiline-ternary: error + new-cap: error + new-parens: error + newline-after-var: + - error + - never + newline-before-return: 'off' + newline-per-chained-call: error + no-alert: error + no-array-constructor: error + no-await-in-loop: error + no-bitwise: error + no-buffer-constructor: error + no-caller: error + no-catch-shadow: error + no-confusing-arrow: error + no-continue: error + no-div-regex: error + no-duplicate-imports: error + no-else-return: error + no-empty-function: error + no-eq-null: error + no-eval: error + no-extend-native: error + no-extra-bind: error + no-extra-label: error + no-extra-parens: error + no-floating-decimal: error + no-implicit-coercion: error + no-implicit-globals: error + no-implied-eval: error + no-inline-comments: 'off' + no-inner-declarations: + - error + - functions + no-invalid-this: error + no-iterator: error + no-label-var: error + no-labels: error + no-lone-blocks: error + no-lonely-if: error + no-loop-func: error + no-magic-numbers: 'off' + no-mixed-operators: error + no-mixed-requires: error + no-multi-assign: error + no-multi-spaces: + - error + - ignoreEOLComments: true + no-multi-str: error + no-multiple-empty-lines: error + no-native-reassign: error + no-negated-condition: error + no-negated-in-lhs: error + no-nested-ternary: error + no-new: error + no-new-func: error + no-new-object: error + no-new-require: error + no-new-wrappers: error + no-octal-escape: error + no-param-reassign: error + no-path-concat: error + no-plusplus: error + no-process-env: error + no-process-exit: error + no-proto: error + no-prototype-builtins: 'off' + no-restricted-globals: error + no-restricted-imports: error + no-restricted-modules: error + no-restricted-properties: error + no-restricted-syntax: error + no-return-assign: error + no-return-await: error + no-script-url: error + no-self-compare: error + no-sequences: error + no-shadow: error + no-shadow-restricted-names: error + no-spaced-func: error + no-sync: error + no-tabs: error + no-template-curly-in-string: error + no-ternary: error + no-throw-literal: error + no-trailing-spaces: 'off' + no-undef-init: error + no-undefined: error + no-underscore-dangle: error + no-unmodified-loop-condition: error + no-unneeded-ternary: error + no-unused-expressions: error + no-use-before-define: error + no-useless-call: error + no-useless-computed-key: error + no-useless-concat: error + no-useless-constructor: error + no-useless-rename: error + no-useless-return: error + no-var: 'off' + no-void: error + no-warning-comments: error + no-whitespace-before-property: error + no-with: error + nonblock-statement-body-position: error + object-curly-newline: error + object-curly-spacing: error + object-property-newline: error + object-shorthand: 'off' + one-var: 'off' + one-var-declaration-per-line: error + operator-assignment: error + operator-linebreak: error + padded-blocks: 'off' + padding-line-between-statements: error + prefer-arrow-callback: 'off' + prefer-const: error + prefer-destructuring: error + prefer-numeric-literals: error + prefer-promise-reject-errors: error + prefer-reflect: 'off' + prefer-rest-params: error + prefer-spread: error + prefer-template: 'off' + quote-props: 'off' + quotes: 'off' + radix: error + require-await: error + require-jsdoc: 'off' + rest-spread-spacing: error + semi: 'off' + semi-spacing: error + semi-style: + - error + - last + sort-imports: error + sort-keys: + - error + - desc + sort-vars: error + space-before-blocks: 'off' + space-before-function-paren: 'off' + space-in-parens: + - error + - never + space-infix-ops: error + space-unary-ops: error + spaced-comment: + - error + - always + strict: error + switch-colon-spacing: error + symbol-description: error + template-curly-spacing: error + template-tag-spacing: error + unicode-bom: + - error + - never + valid-jsdoc: error + vars-on-top: 'off' + wrap-iife: error + wrap-regex: error + yield-star-spacing: error + yoda: + - error + - never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..4d5ed67 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,10 @@ +language: node_js +node_js: +- 6.0.0 +- 8.6.0 +git: + depth: 3 +cache: + directories: + - node_modules + diff --git a/README.md b/README.md new file mode 100644 index 0000000..04f31ef --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +## twiz-client-utils [![Build Status](https://travis-ci.org/gits2501/twiz-client-utils.svg?branch=master)](https://travis-ci.org/gits2501/twiz-client-utils) +Utility functions and modulules that are used in [twiz-client](https://github.com/gits2501/twiz-client). But can be used elsewhere. + +#### Current functions: + name | info + -----|------ +*percentEncode* | percent encoding by rfc 3986 . +*formEncode* | encodes flat javascript objects by x-www-formencoded scheme + +#### Current modules: + +name | info +-----|----- +*CustomError* | provides ability of defining and throwing Error/s with custom `name` so users of your code can check `error.name` instead of usualy more verbose `error.message`. Works in Node and browsers. + + +#### installation: + + `npm install twiz-client-utils` + +#### usage: + + var CustomError = require('twiz-client-utils').CustomError + ```javascript + function Bush(branches){ + this.branches = branches; + + CustomError.call(this) // adds CustomError functionality + this.addCustomErrors({ // adds custom errors with addCustomErrors api + smallBush: 'must have more then 10 branches' + }) + + if(this.branches < 3) throw this.CustomError('smallBush') // throw error with CustomError + } + + var b; + try{ + b = new Bush(5); + catch(e){ + if(e.name === 'smallBush') ... + } diff --git a/package.json b/package.json index 532ad6f..f12be2d 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,24 @@ { "name": "twiz-client-utils", "version": "1.0.0", - "description": "utilities for twiz-client", + "description": "CustomError and percentEncode functions for twiz-client", "main": "src/utils.js", + "scripts": { - "test": "tap -Rspec --cov test/*.js" + + "lint": "eslint src/utils.js", + "tap": "tap -Rspec test/*.js", + "test": "nyc npm run tap && nyc report --reporter=text-lcov | coveralls", + "testLocal": "nyc npm run tap && nyc report --reporter=text-lcov" }, + "author": "github.com/gits2501", "license": "MIT", + "devDependencies": { - "mocha": "^5.0.5", + "coveralls": "^3.0.1", + "eslint": "^4.19.1", + "nyc": "^11.9.0", "tap": "^11.1.3" } } diff --git a/src/utils.js b/src/utils.js index 0b81fb0..abf3961 100644 --- a/src/utils.js +++ b/src/utils.js @@ -40,232 +40,8 @@ return pairs.join("&"); } - var request = ( function request (){ - var request = {}; - - request.messages = { - cbAlreadyCalled: "Callback function has already been called.", - cbWasNotCalled: "Calback function provided was not called.", - urlNotSet: "You must provide url for the request you make.", - callbackNotProvided: "Callback function was not provided.", - notJSON: 'Faileg to parse data as JSON', - encodingNotSupported: "Encoding you provided is not supported", - noContentType: "Failed to get content-type header from response" - }; - - request.initRequest = function(args){ // Propertie names, in args object, that this function supports are: - // url, queryParams, callback, httpMethod, body, beforeSend - this.request = this.createRequest(); // Creates XMLHttpRequest object - var temp; // Temporary place holder - - for(var prop in args){ // iterates trough every argument provided - if(!args.hasOwnProperty(prop)) continue; - temp = args[prop]; - switch(prop){ - case "url": - this.setUrl(temp); // sets the reqest url - break; - case "queryParams": - this.setQuery(temp); // Makes query string for url - break; - case "callback": - this.addListener(temp); // Adds listener for succesful data retrieval and invokes callback - break; - case "method": - this.method = temp.toUpperCase() || "GET" // request method - break; - case "body": console.log("has DATA"); - this.body = temp; // add body for request - break; - case "parse": - this.parse = temp; - break; - case "encoding": - this.encoding = temp; - break; - case "beforeSend": - this.beforeSend = temp // For instance, if we need to set additonal request specific headers - // this.beforeSend is invoked before sending the request, but afther open() - // is called. Here we have created new property. - break; - } - } - - if(!this.url) throw new Error(this.messages.urlNotSet); // Throw error if url was not provided in args - if(!this.method) this.method = "GET"; // Defaults to "GET" method if one was not provided in args object - if (!this.request.onreadystatechange) throw new Error(this.messages.callbackNotProvided); // cb missing - - // console.log("request Instance:", this, typeof this) - console.log(args); - this.sendRequest(); // Makes the actual http request - - }; - - request.createRequest = function(){ - try{ - return new XMLHttpRequest(); // standard - } - catch(e){ console.log(e); - try{ - return new ActiveXObject("Microsoft.XMLHTTP"); // IE specific ... - } - catch(e){ - return new ActiveXObject("Msxml12.XMLHTTP"); - } - } - } - request.setUrl = function(url){ - if(!this.url) this.url = url; - else this.url = url + this.url; // if setQueryParams() run before set url, we already have query string - // in "this.url". So "url" needs to go first. - }; - - request.setQuery = function(queryParams){ - this.queryString = formEncode(queryParams);// Function uses form-url-encoded scheme to return query string - if(this.url.indexOf("?") === -1) this.url+="?"; // if doesnt have query delimiter add it. - this.url+= this.queryString; // Adds query string to url - - }; - - request.addListener = function(callback) { - var alreadyCalled = false; - - this.request.onreadystatechange = function(){ - - if(this.request.readyState === 4){ - if(alreadyCalled){ - console.log('cbAlreadyCalled'); - return; - } - - alreadyCalled = true; - - var statusCode = this.request.status; - var contentType = this.request.getResponseHeader("Content-type");// Get the response's content type - - this.invokeCallback(statusCode, contentType, callback); - - } - }.bind(this); // Async functions lose -this- context because they start executing when functions that - // invoked them already finished their execution. Here we pass whatever "this" references - // in the moment addListener() is invoked. Meaning, "this" will repesent each - // instance of request, see return function below. - }; - - request.invokeCallback = function (statusCode, contentType, callback){ - var error; - var data; - var temp; - - if(!contentType) throw new Error(this.messages.noContentType); - var contentType = contentType.split(';')[0]; // get just type , in case there is charset specified - - console.log('content-type: ', contentType) - switch(contentType){ // get request data from apropriate property, parse it if indicated - case "application/json": - try{ console.log('content-type is application/json') - if(this.parse) temp = JSON.parse(this.request.responseText); // parse json data - else temp = this.request.responseText; - console.log('temp afther JSON parsing: ', temp) - } - catch(e){ - throw new Error(this.messages.notJSON + " \n"+ e); // if parsing failed note it - } - break; - case "application/xml": - temp = this.request.responseXML; // responceXML already parsed as a DOM object - break; - case "application/x-www-url-formencoded": - temp = {}; - this.request.responseText.trim().split("&").forEach(function(el, i){ // split on & - - var pairs = el.split('='); - var header = decodeURIComponent(pairs[0]); // decode header name - var value = decodeURIComponent(pairs[1]); // decode value - temp[header] = value; // adds to data header name and its value - }, temp) - break; - default: - temp = this.request.responseText;// text/html , text/css and others are treated as text - } - - if(statusCode !== 200){ // on error create error object - error = { 'status': statusCode, 'statusText': this.request.statusText, 'data': temp } - } - else data = temp; // no error, data is object we got from payload - - - callback(error, data) // invoke callback - - } - - request.setHeader = function(header, value){ // set the request header - this.request.setRequestHeader(header, value); - }; - - request.setBody = function(){ // sets Content-Type encoding and encode the body of a request - console.log("In setBody") - if(this.body){ // check for data payload - - if(!this.encoding){ // If there is no string that indicates encoding - this.setHeader("Content-Type", "text/plain"); // default to text, set the content type (was formEnc) - } - else { - switch(this.encoding.toLowerCase()){ // when there is encoding string - case "form": - this.body = formEncode(this.body) // encode the body - this.setHeader("Content-Type", "application/x-www-url-formencoded;charset=utf-8"); - break; - case "json": - this.body = JSON.stringify(this.body) - this.setHeader("Content-Type", "application/json;charset=utf-8"); - break; - case "text": - this.setHeader("Content-Type", 'text/plain;charset=utf-8'); - break; - default: - throw new Error(this.messages.encodingNotSupported); - } - } - } - else { - this.body.data = null; // set the body to null - } - - - }; - request.sendRequest = function(){ - - if(this.request.readyState == "0") this.request.open(this.method, this.url);// "0" means open() not called - if(this.beforeSend) this.beforeSend(this.request) // if user supplied beforeSend() func, call it. - console.log("This before setBody!", "req state:"+ this.request.readyState); - - if(this.body) this.setBody(); - else if(this.method === "POST") this.body = null; // set it to 'null' if there is no body and method - //is POST. This is just xmlhttp request spec. - - if(this.method === "GET") this.request.send(null); - if(this.method === "POST") this.request.send(this.body); - - }; - - return function(args){ // modul returns this function as API - - var r = Object.create(request); // behavior delegation link - - if(args){ - r.initRequest(args); // Initialise request and sends it, if args are provided - return; // if not , then return the object that indirectly, through closures - } // have access to prototype chain of request API. That is it has acess to - // an instance of request API (here it is "r"). - - return phantomHead = { initRequest: r.initRequest.bind(r) } // "borrow" method from instance, bind it to instance - } - - })(); - - function CustomError(){ + function CustomError(){ this.messages = {}; // error messages place holder @@ -287,10 +63,17 @@ } + + function throwAsyncError (error){ + if(Promise) return this.reject(error); // if we have promise use reject for async Errors + + throw error; // otherways just throw it + } module.exports = { - percentEncode: percentEncode, - formEncode: formEncode, - request: request, - CustomError: CustomError + + 'percentEncode': percentEncode, + 'formEncode': formEncode, + 'CustomError': CustomError, + 'throwAsyncError': throwAsyncError } diff --git a/test/customerror.js b/test/customerror.js index afffa6a..2f38a0f 100644 --- a/test/customerror.js +++ b/test/customerror.js @@ -1,5 +1,6 @@ -var test = require('tap').test; -var CustomError = require('../src/utils').CustomError; +var test = require('tap').test; +var CustomError = require('../src/utils').CustomError; +var throwAsyncError = require('../src/utils').throwAsyncError function Bar (){ @@ -35,4 +36,44 @@ test('CustomError throw',function(t){ t.end() }) +test('throw Async Error (Promise) ', function(t){ // simulate promise async aware error throwing, promise avalable + var baz = {} + baz.throwAsyncError = throwAsyncError; // get async error throwing function + + var p = new Promise(function(res, rej){ + baz.reject = rej; + }) + + test('throws async error', function(t){ + t.plan(1); + var error = 'error in promise' + baz.throwAsyncError(error); // throw error (calls reject(..) if Promise is globaly avalable) + + p.then(null, + function rejected(err){ + t.equals(err, error); + }) + }) + + t.end() +}) +test('throw Async Error (no Promise)', function(t){ //' when no promise is avalable just throw error + test('throws error', function(t){ + t.plan(1); + + var baz = {} + var error = 'sync error' + baz.throwAsyncError = throwAsyncError; // get async error throwing function + + Promise = '' // simulate no promise avalable + var savedPromise = Promise; + + t.throw(baz.throwAsyncError.bind(baz, error), error); + + Promise = savedPromise; // return promise functionality + + }) + + t.end() +}) diff --git a/utils.js b/utils.js deleted file mode 100644 index 5025b6b..0000000 --- a/utils.js +++ /dev/null @@ -1,296 +0,0 @@ -'use strict' - - function percentEncode(str){ // percent encode by RFC3986 - - return encodeURIComponent(str).replace(/[!'()*]/g, function(c){ // percent encodes unsafe chars, then - // it follows RFC3986 and percent encodes - // reserved characters in sqere brackets. - return '%' + c.charCodeAt(0).toString(16); // takes binary representation of every reserved char - // , coverts it to hex string char and appends to "%". - }); - - } - - function formEncode(dataObj, spaces){ - var pairs = []; - var value; - var key; - var type; - for(var name in dataObj){ - type = typeof dataObj[name]; - if(dataObj.hasOwnProperty(name) && type !== "function" && dataObj[name] !== "null"){ // only props - // in dataObj - key = percentEncode(name); // encode property name - - if(type === 'object'){ - value = formEncode(dataObj[name], spaces); // form encode object - value = percentEncode(value) // since return value is string, percent encode it - } - else value = percentEncode(dataObj[name]) // property is not object, percent encode it - - if(!spaces){ - key = key.replace(/%20/g, "+") - value = value.replace(/%20/g, "+"); // substitute space encoding for + - } - - pairs.push(key + "=" + value) - } - } - - return pairs.join("&"); - } - - var request = ( function request (){ - - var request = {}; - - request.messages = { - cbAlreadyCalled: "Callback function has already been called.", - cbWasNotCalled: "Calback function provided was not called.", - urlNotSet: "You must provide url for the request you make.", - callbackNotProvided: "Callback function was not provided.", - notJSON: 'Faileg to parse data as JSON', - encodingNotSupported: "Encoding you provided is not supported", - noContentType: "Failed to get content-type header from response" - }; - - request.initRequest = function(args){ // Propertie names, in args object, that this function supports are: - // url, queryParams, callback, httpMethod, body, beforeSend - this.request = this.createRequest(); // Creates XMLHttpRequest object - var temp; // Temporary place holder - - for(var prop in args){ // iterates trough every argument provided - if(!args.hasOwnProperty(prop)) continue; - temp = args[prop]; - switch(prop){ - case "url": - this.setUrl(temp); // sets the reqest url - break; - case "queryParams": - this.setQuery(temp); // Makes query string for url - break; - case "callback": - this.addListener(temp); // Adds listener for succesful data retrieval and invokes callback - break; - case "method": - this.method = temp.toUpperCase() || "GET" // request method - break; - case "body": console.log("has DATA"); - this.body = temp; // add body for request - break; - case "parse": - this.parse = temp; - break; - case "encoding": - this.encoding = temp; - break; - case "beforeSend": - this.beforeSend = temp // For instance, if we need to set additonal request specific headers - // this.beforeSend is invoked before sending the request, but afther open() - // is called. Here we have created new property. - break; - } - } - - if(!this.url) throw new Error(this.messages.urlNotSet); // Throw error if url was not provided in args - if(!this.method) this.method = "GET"; // Defaults to "GET" method if one was not provided in args object - if (!this.request.onreadystatechange) throw new Error(this.messages.callbackNotProvided); // cb missing - - // console.log("request Instance:", this, typeof this) - console.log(args); - this.sendRequest(); // Makes the actual http request - - }; - - request.createRequest = function(){ - try{ - return new XMLHttpRequest(); // standard - } - catch(e){ console.log(e); - try{ - return new ActiveXObject("Microsoft.XMLHTTP"); // IE specific ... - } - catch(e){ - return new ActiveXObject("Msxml12.XMLHTTP"); - } - } - } - request.setUrl = function(url){ - if(!this.url) this.url = url; - else this.url = url + this.url; // if setQueryParams() run before set url, we already have query string - // in "this.url". So "url" needs to go first. - }; - - request.setQuery = function(queryParams){ - this.queryString = formEncode(queryParams);// Function uses form-url-encoded scheme to return query string - if(this.url.indexOf("?") === -1) this.url+="?"; // if doesnt have query delimiter add it. - this.url+= this.queryString; // Adds query string to url - - }; - - request.addListener = function(callback) { - var alreadyCalled = false; - - this.request.onreadystatechange = function(){ - - if(this.request.readyState === 4){ - if(alreadyCalled){ - console.log('cbAlreadyCalled'); - return; - } - - alreadyCalled = true; - - var statusCode = this.request.status; - var contentType = this.request.getResponseHeader("Content-type");// Get the response's content type - - this.invokeCallback(statusCode, contentType, callback); - - } - }.bind(this); // Async functions lose -this- context because they start executing when functions that - // invoked them already finished their execution. Here we pass whatever "this" references - // in the moment addListener() is invoked. Meaning, "this" will repesent each - // instance of request, see return function below. - }; - - request.invokeCallback = function (statusCode, contentType, callback){ - var error; - var data; - var temp; - - if(!contentType) throw new Error(this.messages.noContentType); - var contentType = contentType.split(';')[0]; // get just type , in case there is charset specified - - console.log('content-type: ', contentType) - switch(contentType){ // get request data from apropriate property, parse it if indicated - case "application/json": - try{ console.log('content-type is application/json') - if(this.parse) temp = JSON.parse(this.request.responseText); // parse json data - else temp = this.request.responseText; - console.log('temp afther JSON parsing: ', temp) - } - catch(e){ - throw new Error(this.messages.notJSON + " \n"+ e); // if parsing failed note it - } - break; - case "application/xml": - temp = this.request.responseXML; // responceXML already parsed as a DOM object - break; - case "application/x-www-url-formencoded": - temp = {}; - this.request.responseText.trim().split("&").forEach(function(el, i){ // split on & - - var pairs = el.split('='); - var header = decodeURIComponent(pairs[0]); // decode header name - var value = decodeURIComponent(pairs[1]); // decode value - temp[header] = value; // adds to data header name and its value - }, temp) - break; - default: - temp = this.request.responseText;// text/html , text/css and others are treated as text - } - - if(statusCode !== 200){ // on error create error object - error = { 'status': statusCode, 'statusText': this.request.statusText, 'data': temp } - } - else data = temp; // no error, data is object we got from payload - - - callback(error, data) // invoke callback - - } - - request.setHeader = function(header, value){ // set the request header - this.request.setRequestHeader(header, value); - }; - - request.setBody = function(){ // sets Content-Type encoding and encode the body of a request - console.log("In setBody") - if(this.body){ // check for data payload - - if(!this.encoding){ // If there is no string that indicates encoding - this.setHeader("Content-Type", "text/plain"); // default to text, set the content type (was formEnc) - } - else { - switch(this.encoding.toLowerCase()){ // when there is encoding string - case "form": - this.body = formEncode(this.body) // encode the body - this.setHeader("Content-Type", "application/x-www-url-formencoded;charset=utf-8"); - break; - case "json": - this.body = JSON.stringify(this.body) - this.setHeader("Content-Type", "application/json;charset=utf-8"); - break; - case "text": - this.setHeader("Content-Type", 'text/plain;charset=utf-8'); - break; - default: - throw new Error(this.messages.encodingNotSupported); - } - } - } - else { - this.body.data = null; // set the body to null - } - - - }; - request.sendRequest = function(){ - - if(this.request.readyState == "0") this.request.open(this.method, this.url);// "0" means open() not called - if(this.beforeSend) this.beforeSend(this.request) // if user supplied beforeSend() func, call it. - console.log("This before setBody!", "req state:"+ this.request.readyState); - - if(this.body) this.setBody(); - else if(this.method === "POST") this.body = null; // set it to 'null' if there is no body and method - //is POST. This is just xmlhttp request spec. - - if(this.method === "GET") this.request.send(null); - if(this.method === "POST") this.request.send(this.body); - - }; - - return function(args){ // modul returns this function as API - - var r = Object.create(request); // behavior delegation link - - if(args){ - r.initRequest(args); // Initialise request and sends it, if args are provided - return; // if not , then return the object that indirectly, through closures - } // have access to prototype chain of request API. That is it has acess to - // an instance of request API (here it is "r"). - - return phantomHead = { initRequest: r.initRequest.bind(r) } // "borrow" method from instance, bind it to instance - } - - })(); - - function CustomError(){ - - this.messages = {}; // error messages place holder - - - this.addCustomErrors = function (errors){ // add custom error messages - - Object.getOwnPropertyNames(errors).map(function(name){ - - this.messages[name] = errors[name]; - },this) - } - - this.CustomError = function(name){// uses built-in Error func to make custom err info - - var err = Error(this.messages[name]); // take message text - err['name'] = name; // set error name - return err; - } - - - } - - module.exports = { - percentEncode: percentEncode, - formEncode: formEncode, - request: request, - CustomError: CustomError - }