Skip to content

ref: Move utils from raven.js to utils.js file #1084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
368 changes: 23 additions & 345 deletions src/raven.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
/*global XDomainRequest:false, __DEV__:false*/
/*global XDomainRequest:false */

var TraceKit = require('../vendor/TraceKit/tracekit');
var stringify = require('../vendor/json-stringify-safe/stringify');
var RavenConfigError = require('./configError');
var utils = require('./utils');

var isError = utils.isError,
isObject = utils.isObject;
var utils = require('./utils');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't want to change all instances to utils.something in the codebase, but rather just pull them out like this to make this PR "easier" to digest.

var isError = utils.isError;
var isObject = utils.isObject;
var isObject = utils.isObject;
var isError = utils.isError;
var isUndefined = utils.isUndefined;
var isFunction = utils.isFunction;
var isString = utils.isString;
var isEmptyObject = utils.isEmptyObject;
var each = utils.each;
var objectMerge = utils.objectMerge;
var truncate = utils.truncate;
var objectFrozen = utils.objectFrozen;
var hasKey = utils.hasKey;
var joinRegExp = utils.joinRegExp;
var urlencode = utils.urlencode;
var uuid4 = utils.uuid4;
var htmlTreeAsString = utils.htmlTreeAsString;
var isSameException = utils.isSameException;
var isSameStacktrace = utils.isSameStacktrace;
var parseUrl = utils.parseUrl;
var fill = utils.fill;

var wrapConsoleMethod = require('./console').wrapMethod;

Expand Down Expand Up @@ -1891,347 +1910,6 @@ Raven.prototype = {
}
};

/*------------------------------------------------
* utils
*
* conditionally exported for test via Raven.utils
=================================================
*/
var objectPrototype = Object.prototype;

function isUndefined(what) {
return what === void 0;
}

function isFunction(what) {
return typeof what === 'function';
}

function isString(what) {
return objectPrototype.toString.call(what) === '[object String]';
}

function isEmptyObject(what) {
for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars
return true;
}

function each(obj, callback) {
var i, j;

if (isUndefined(obj.length)) {
for (i in obj) {
if (hasKey(obj, i)) {
callback.call(null, i, obj[i]);
}
}
} else {
j = obj.length;
if (j) {
for (i = 0; i < j; i++) {
callback.call(null, i, obj[i]);
}
}
}
}

function objectMerge(obj1, obj2) {
if (!obj2) {
return obj1;
}
each(obj2, function(key, value) {
obj1[key] = value;
});
return obj1;
}

/**
* This function is only used for react-native.
* react-native freezes object that have already been sent over the
* js bridge. We need this function in order to check if the object is frozen.
* So it's ok that objectFrozen returns false if Object.isFrozen is not
* supported because it's not relevant for other "platforms". See related issue:
* https://github.com/getsentry/react-native-sentry/issues/57
*/
function objectFrozen(obj) {
if (!Object.isFrozen) {
return false;
}
return Object.isFrozen(obj);
}

function truncate(str, max) {
return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
}

/**
* hasKey, a better form of hasOwnProperty
* Example: hasKey(MainHostObject, property) === true/false
*
* @param {Object} host object to check property
* @param {string} key to check
*/
function hasKey(object, key) {
return objectPrototype.hasOwnProperty.call(object, key);
}

function joinRegExp(patterns) {
// Combine an array of regular expressions and strings into one large regexp
// Be mad.
var sources = [],
i = 0,
len = patterns.length,
pattern;

for (; i < len; i++) {
pattern = patterns[i];
if (isString(pattern)) {
// If it's a string, we need to escape it
// Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'));
} else if (pattern && pattern.source) {
// If it's a regexp already, we want to extract the source
sources.push(pattern.source);
}
// Intentionally skip other cases
}
return new RegExp(sources.join('|'), 'i');
}

function urlencode(o) {
var pairs = [];
each(o, function(key, value) {
pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
});
return pairs.join('&');
}

// borrowed from https://tools.ietf.org/html/rfc3986#appendix-B
// intentionally using regex and not <a/> href parsing trick because React Native and other
// environments where DOM might not be available
function parseUrl(url) {
var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/);
if (!match) return {};

// coerce to undefined values to empty string so we don't get 'undefined'
var query = match[6] || '';
var fragment = match[8] || '';
return {
protocol: match[2],
host: match[4],
path: match[5],
relative: match[5] + query + fragment // everything minus origin
};
}
function uuid4() {
var crypto = _window.crypto || _window.msCrypto;

if (!isUndefined(crypto) && crypto.getRandomValues) {
// Use window.crypto API if available
// eslint-disable-next-line no-undef
var arr = new Uint16Array(8);
crypto.getRandomValues(arr);

// set 4 in byte 7
arr[3] = (arr[3] & 0xfff) | 0x4000;
// set 2 most significant bits of byte 9 to '10'
arr[4] = (arr[4] & 0x3fff) | 0x8000;

var pad = function(num) {
var v = num.toString(16);
while (v.length < 4) {
v = '0' + v;
}
return v;
};

return (
pad(arr[0]) +
pad(arr[1]) +
pad(arr[2]) +
pad(arr[3]) +
pad(arr[4]) +
pad(arr[5]) +
pad(arr[6]) +
pad(arr[7])
);
} else {
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (Math.random() * 16) | 0,
v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
}

/**
* Given a child DOM element, returns a query-selector statement describing that
* and its ancestors
* e.g. [HTMLElement] => body > div > input#foo.btn[name=baz]
* @param elem
* @returns {string}
*/
function htmlTreeAsString(elem) {
/* eslint no-extra-parens:0*/
var MAX_TRAVERSE_HEIGHT = 5,
MAX_OUTPUT_LEN = 80,
out = [],
height = 0,
len = 0,
separator = ' > ',
sepLength = separator.length,
nextStr;

while (elem && height++ < MAX_TRAVERSE_HEIGHT) {
nextStr = htmlElementAsString(elem);
// bail out if
// - nextStr is the 'html' element
// - the length of the string that would be created exceeds MAX_OUTPUT_LEN
// (ignore this limit if we are on the first iteration)
if (
nextStr === 'html' ||
(height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN)
) {
break;
}

out.push(nextStr);

len += nextStr.length;
elem = elem.parentNode;
}

return out.reverse().join(separator);
}

/**
* Returns a simple, query-selector representation of a DOM element
* e.g. [HTMLElement] => input#foo.btn[name=baz]
* @param HTMLElement
* @returns {string}
*/
function htmlElementAsString(elem) {
var out = [],
className,
classes,
key,
attr,
i;

if (!elem || !elem.tagName) {
return '';
}

out.push(elem.tagName.toLowerCase());
if (elem.id) {
out.push('#' + elem.id);
}

className = elem.className;
if (className && isString(className)) {
classes = className.split(/\s+/);
for (i = 0; i < classes.length; i++) {
out.push('.' + classes[i]);
}
}
var attrWhitelist = ['type', 'name', 'title', 'alt'];
for (i = 0; i < attrWhitelist.length; i++) {
key = attrWhitelist[i];
attr = elem.getAttribute(key);
if (attr) {
out.push('[' + key + '="' + attr + '"]');
}
}
return out.join('');
}

/**
* Returns true if either a OR b is truthy, but not both
*/
function isOnlyOneTruthy(a, b) {
return !!(!!a ^ !!b);
}

/**
* Returns true if the two input exception interfaces have the same content
*/
function isSameException(ex1, ex2) {
if (isOnlyOneTruthy(ex1, ex2)) return false;

ex1 = ex1.values[0];
ex2 = ex2.values[0];

if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false;

return isSameStacktrace(ex1.stacktrace, ex2.stacktrace);
}

/**
* Returns true if the two input stack trace interfaces have the same content
*/
function isSameStacktrace(stack1, stack2) {
if (isOnlyOneTruthy(stack1, stack2)) return false;

var frames1 = stack1.frames;
var frames2 = stack2.frames;

// Exit early if frame count differs
if (frames1.length !== frames2.length) return false;

// Iterate through every frame; bail out if anything differs
var a, b;
for (var i = 0; i < frames1.length; i++) {
a = frames1[i];
b = frames2[i];
if (
a.filename !== b.filename ||
a.lineno !== b.lineno ||
a.colno !== b.colno ||
a['function'] !== b['function']
)
return false;
}
return true;
}

/**
* Polyfill a method
* @param obj object e.g. `document`
* @param name method name present on object e.g. `addEventListener`
* @param replacement replacement function
* @param track {optional} record instrumentation to an array
*/
function fill(obj, name, replacement, track) {
var orig = obj[name];
obj[name] = replacement(orig);
if (track) {
track.push([obj, name, orig]);
}
}

if (typeof __DEV__ !== 'undefined' && __DEV__) {
Raven.utils = {
isUndefined: isUndefined,
isFunction: isFunction,
isString: isString,
isObject: isObject,
isEmptyObject: isEmptyObject,
isError: isError,
each: each,
objectMerge: objectMerge,
truncate: truncate,
hasKey: hasKey,
joinRegExp: joinRegExp,
urlencode: urlencode,
uuid4: uuid4,
htmlTreeAsString: htmlTreeAsString,
htmlElementAsString: htmlElementAsString,
parseUrl: parseUrl,
fill: fill
};
}

// Deprecations
Raven.prototype.setUser = Raven.prototype.setUserContext;
Raven.prototype.setReleaseContext = Raven.prototype.setRelease;
Expand Down
Loading