Source: br/src/br/Errors.js

"use strict";

/**
* @module br/Errors
*/

var br = require('br/Core');

/**
* @class
* @alias module:br/Errors
* 
* @classdesc
* Constructs a new <code>Error</code> of the provided type.
* 
* <code>br/Errors</code> extends the built in <code>Error</code> and allows the
* error type to be specified in the constructor. The <code>name</code>
* property is set to the specified type.
* 
* @param {String} type The error type to be thrown.
* @param {String} message A human-readable description of the error.
* @param {String} [fileName] (Optional) The name of the file containing the code that caused the error.
* @param {int} [lineNumber] (Optional) The line number of the code that caused the error.
*/
function CustomError(type, message, fileName, lineNumber) {
	this.name = type || "";
	this.message = message || "";
	this.fileName = fileName;
	this.lineNumber = lineNumber;

	// If the browser we're in provides an ability to get the stack, then get it here.
	var e = new Error();
	if (e.stack) {
		this.realStack = e.stack;
		this.stack = "Error: "+type+": "+message+"\n\tat "+getStack(e).join("\n\tat ");
	}
}

br.extend(CustomError, Error);

/**
* Returns the string representation of this error
*/
CustomError.prototype.toString = function toString() {
	return this.stack || this.message;
};

exports.CustomError = CustomError;

/**
 * This error type is thrown when a method has been invoked at an illegal or
 * inappropriate time.
 */
exports.ILLEGAL_STATE = "IllegalStateError";

/**
 * This error type is thrown from acceptance test fixtures and indicates a
 * problem with the test rather than the code under test. For example, if a
 * particular fixture can only be used in a 'given' clause but is invoked in a
 * 'then' clause, this error will be thrown. This will result in a test 'error'
 * rather than a test 'failure'.
 */
exports.INVALID_TEST = "InvalidTestError";
exports.ILLEGAL_TEST_CLAUSE = "IllegalTestClauseError";

/**
 * This error is thrown when an interface method is called that should have
 * been implemented in the interface implementor class.
 */
exports.UNIMPLEMENTED_INTERFACE = "UnimplementedInterfaceError";

/**
 * This error is thrown when an abstract method is called that should have
 * been implemented in the extending class.
 */
exports.UNIMPLEMENTED_ABSTRACT_METHOD = "UnimplementedAbstractMethodError";

/**
 * This error is thrown when an operation is being attempted on an a class instance
 * and it does not have the required implementation.
 */
exports.NOT_SUPPORTED = "NotSupportedError";

/**
 * This error type is thrown when a method is called with one or more invalid
 * parameters. This could either be because a required parameter is not provided
 * or a provided parameter is of the wrong type or is invalid for another reason
 * (eg a string representation of a date that doesn't parse to an actual date).
 */
exports.INVALID_PARAMETERS = "InvalidParametersError";

/**
 * This error type indicates that a request for data has failed.
 */
exports.REQUEST_FAILED = "RequestFailedError";

/**
 * This error type indicates that some required data was invalid.
 */
exports.INVALID_DATA = "InvalidDataError";

function getCustomErrorConstructor(type) {
	var customErrorConstructor = function(message, filename, lineNumber) {
		CustomError.call(this, type, message, filename, lineNumber);
	};
	br.extend(customErrorConstructor, CustomError);
	return customErrorConstructor;
}

for (var key in exports) {
	if (typeof exports[key] === 'string') {
		var className = exports[key];
		exports[className] = getCustomErrorConstructor(className);
	}
}

exports.EVAL = "EvalError";
exports.EvalError = EvalError;
exports.RANGE = "RangeError";
exports.RangeError = RangeError;
exports.REFERENCE = "ReferenceError";
exports.ReferenceError = ReferenceError;
exports.SYNTAX = "SyntaxError";
exports.SyntaxError = SyntaxError;
exports.TYPE = "TypeError";
exports.TypeError = TypeError;


// static private methods /////////////////////////////////////////////////////

/** @private */
function normaliseStack(stackString) {
	var stack;

	if(stackString) {
		stack = stackString.split("\n");
		for (var i = stack.length - 1; i >= 0; --i) {
			if (stack[i] === 'Error' || stack[i] === '') {
				stack.splice(i, 1);
			} else {
				var header = stack[i].match(/^\s*at\s+/);
				if (header !== null) {
					stack[i] = stack[i].substring(header[0].length);
				}
			}
		}
	}
	return stack;
}

/** @private */
var irrelevantStack = normaliseStack((new (getCustomErrorConstructor('irrelevant'))()).realStack);

/** @private */
function getStack(e) {
	var stack = normaliseStack(e.stack);
	if (irrelevantStack !== undefined) {
		var line = 0;
		while (stack[0] === irrelevantStack[line++]) {
			stack.shift();
		}
	}
	return stack;
}