diff --git a/client/galaxy/scripts/libs/bootstrap-tour.js b/client/galaxy/scripts/libs/bootstrap-tour.js index 1874061e8cd..009525a9d09 100644 --- a/client/galaxy/scripts/libs/bootstrap-tour.js +++ b/client/galaxy/scripts/libs/bootstrap-tour.js @@ -1,15 +1,15 @@ /* ======================================================================== - * bootstrap-tour - v0.10.1 + * bootstrap-tour - v0.10.2 * http://bootstraptour.com * ======================================================================== - * Copyright 2012-2013 Ulrich Sossou + * Copyright 2012-2015 Ulrich Sossou * * ======================================================================== - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the MIT License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://opensource.org/licenses/MIT * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -39,6 +39,7 @@ storage: storage, debug: false, backdrop: false, + backdropContainer: 'body', backdropPadding: 0, redirect: true, orphan: false, @@ -58,10 +59,12 @@ onNext: function(tour) {}, onPrev: function(tour) {}, onPause: function(tour, duration) {}, - onResume: function(tour, duration) {} + onResume: function(tour, duration) {}, + onRedirectError: function(tour) {} }, options); this._force = false; this._inited = false; + this._current = null; this.backdrop = { overlay: null, $element: null, @@ -91,6 +94,7 @@ return $.extend({ id: "step-" + i, path: '', + host: '', placement: 'right', title: '', content: '

', @@ -100,8 +104,10 @@ container: this._options.container, autoscroll: this._options.autoscroll, backdrop: this._options.backdrop, + backdropContainer: this._options.backdropContainer, backdropPadding: this._options.backdropPadding, redirect: this._options.redirect, + reflexElement: this._options.steps[i].element, orphan: this._options.orphan, duration: this._options.duration, delay: this._options.delay, @@ -113,7 +119,8 @@ onNext: this._options.onNext, onPrev: this._options.onPrev, onPause: this._options.onPause, - onResume: this._options.onResume + onResume: this._options.onResume, + onRedirectError: this._options.onRedirectError }, this._options.steps[i]); } }; @@ -199,6 +206,7 @@ Tour.prototype.restart = function() { this._removeState('current_step'); this._removeState('end'); + this._removeState('redirect_to'); return this.start(); }; @@ -257,8 +265,9 @@ $element = $('body'); } $element.popover('destroy').removeClass("tour-" + _this._options.name + "-element tour-" + _this._options.name + "-" + i + "-element"); + $element.removeData('bs.popover'); if (step.reflex) { - $element.removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name); + $(step.reflexElement).removeClass('tour-step-element-reflex').off("" + (_this._reflexEvent(step.reflex)) + ".tour-" + _this._options.name); } if (step.backdrop) { _this._hideBackdrop(); @@ -286,7 +295,7 @@ promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0); showStepHelper = (function(_this) { return function(e) { - var current_path, path, showPopoverAndOverlay; + var path, showPopoverAndOverlay; _this.setCurrentStep(i); path = (function() { switch ({}.toString.call(step.path)) { @@ -298,13 +307,14 @@ return step.path; } }).call(_this); - current_path = [document.location.pathname, document.location.hash].join(''); - if (_this._isRedirect(path, current_path)) { - _this._redirect(step, path); - return; + if (_this._isRedirect(step.host, path, document.location)) { + _this._redirect(step, i, path); + if (!_this._isJustPathHashDifferent(step.host, path, document.location)) { + return; + } } if (_this._isOrphan(step)) { - if (!step.orphan) { + if (step.orphan === false) { _this._debug("Skip the orphan step " + (_this._current + 1) + ".\nOrphan option is false and the element does not exist or is hidden."); if (skipToPrevious) { _this._showPrevStep(); @@ -316,10 +326,10 @@ _this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true."); } if (step.backdrop) { - _this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0); + _this._showBackdrop(step); } showPopoverAndOverlay = function() { - if (_this.getCurrentStep() !== i) { + if (_this.getCurrentStep() !== i || _this.ended()) { return; } if ((step.element != null) && step.backdrop) { @@ -369,6 +379,10 @@ return this; }; + Tour.prototype.redraw = function() { + return this._showOverlayElement(this.getStep(this.getCurrentStep()).element, true); + }; + Tour.prototype._setState = function(key, value) { var e, keyName; if (this._options.storage) { @@ -450,16 +464,54 @@ } }; - Tour.prototype._isRedirect = function(path, currentPath) { - return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && path.replace(/\?.*$/, '').replace(/\/?$/, '') !== currentPath.replace(/\/?$/, ''))); + Tour.prototype._isRedirect = function(host, path, location) { + var currentPath; + if (host !== '') { + if (this._isHostDifferent(host, location.href)) { + return true; + } + } + currentPath = [location.pathname, location.search, location.hash].join(''); + return (path != null) && path !== '' && (({}.toString.call(path) === '[object RegExp]' && !path.test(currentPath)) || ({}.toString.call(path) === '[object String]' && this._isPathDifferent(path, currentPath))); }; - Tour.prototype._redirect = function(step, path) { + Tour.prototype._isHostDifferent = function(host, currentURL) { + return this._getProtocol(host) !== this._getProtocol(currentURL) || this._getHost(host) !== this._getHost(currentURL); + }; + + Tour.prototype._isPathDifferent = function(path, currentPath) { + return this._getPath(path) !== this._getPath(currentPath) || !this._equal(this._getQuery(path), this._getQuery(currentPath)) || !this._equal(this._getHash(path), this._getHash(currentPath)); + }; + + Tour.prototype._isJustPathHashDifferent = function(host, path, location) { + var currentPath; + if (host !== '') { + if (this._isHostDifferent(host, location.href)) { + return false; + } + } + currentPath = [location.pathname, location.search, location.hash].join(''); + if ({}.toString.call(path) === '[object String]') { + return this._getPath(path) === this._getPath(currentPath) && this._equal(this._getQuery(path), this._getQuery(currentPath)) && !this._equal(this._getHash(path), this._getHash(currentPath)); + } + return false; + }; + + Tour.prototype._redirect = function(step, i, path) { if ($.isFunction(step.redirect)) { return step.redirect.call(this, path); } else if (step.redirect === true) { - this._debug("Redirect to " + path); - return document.location.href = path; + this._debug("Redirect to " + step.host + path); + if (this._getState('redirect_to') === ("" + i)) { + this._debug("Error redirection loop to " + path); + this._removeState('redirect_to'); + if (step.onRedirectError != null) { + return step.onRedirectError(this); + } + } else { + this._setState('redirect_to', "" + i); + return document.location.href = "" + step.host + path; + } } }; @@ -472,7 +524,7 @@ }; Tour.prototype._showPopover = function(step, i) { - var $element, $tip, isOrphan, options; + var $element, $tip, isOrphan, options, shouldAddSmart; $(".tour-" + this._options.name).remove(); options = $.extend({}, this._options); isOrphan = this._isOrphan(step); @@ -487,9 +539,7 @@ $.extend(options, step.options); } if (step.reflex && !isOrphan) { - $element.addClass('tour-step-element-reflex'); - $element.off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name); - $element.on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) { + $(step.reflexElement).addClass('tour-step-element-reflex').off("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name).on("" + (this._reflexEvent(step.reflex)) + ".tour-" + this._options.name, (function(_this) { return function() { if (_this._isLast()) { return _this.next(); @@ -499,8 +549,9 @@ }; })(this)); } + shouldAddSmart = step.smartPlacement === true && step.placement.search(/auto/i) === -1; $element.popover({ - placement: step.placement, + placement: shouldAddSmart ? "auto " + step.placement : step.placement, trigger: 'manual', title: step.title, content: step.content, @@ -519,8 +570,12 @@ }; Tour.prototype._template = function(step, i) { - var $navigation, $next, $prev, $resume, $template; - $template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template); + var $navigation, $next, $prev, $resume, $template, template; + template = step.template; + if (this._isOrphan(step) && {}.toString.call(step.orphan) !== '[object Boolean]') { + template = step.orphan; + } + $template = $.isFunction(template) ? $(template(i, step)) : $(template); $navigation = $template.find('.popover-navigation'); $prev = $navigation.find('[data-role="prev"]'); $next = $navigation.find('[data-role="next"]'); @@ -529,11 +584,16 @@ $template.addClass('orphan'); } $template.addClass("tour-" + this._options.name + " tour-" + this._options.name + "-" + i); + if (step.reflex) { + $template.addClass("tour-" + this._options.name + "-reflex"); + } if (step.prev < 0) { $prev.addClass('disabled'); + $prev.prop('disabled', true); } if (step.next < 0) { $next.addClass('disabled'); + $next.prop('disabled', true); } if (!step.duration) { $resume.remove(); @@ -704,7 +764,7 @@ } }; - Tour.prototype._showBackdrop = function(element) { + Tour.prototype._showBackdrop = function(step) { if (this.backdrop.backgroundShown) { return; } @@ -712,7 +772,7 @@ "class": 'tour-backdrop' }); this.backdrop.backgroundShown = true; - return $('body').append(this.backdrop); + return $(step.backdropContainer).append(this.backdrop); }; Tour.prototype._hideBackdrop = function() { @@ -728,23 +788,25 @@ } }; - Tour.prototype._showOverlayElement = function(step) { + Tour.prototype._showOverlayElement = function(step, force) { var $element, elementData; $element = $(step.element); - if (!$element || $element.length === 0 || this.backdrop.overlayElementShown) { + if (!$element || $element.length === 0 || this.backdrop.overlayElementShown && !force) { return; } - this.backdrop.overlayElementShown = true; - this.backdrop.$element = $element.addClass('tour-step-backdrop'); - this.backdrop.$background = $('
', { - "class": 'tour-step-background' - }); + if (!this.backdrop.overlayElementShown) { + this.backdrop.$element = $element.addClass('tour-step-backdrop'); + this.backdrop.$background = $('
', { + "class": 'tour-step-background' + }); + this.backdrop.$background.appendTo(step.backdropContainer); + this.backdrop.overlayElementShown = true; + } elementData = { width: $element.innerWidth(), height: $element.innerHeight(), offset: $element.offset() }; - this.backdrop.$background.appendTo('body'); if (step.backdropPadding) { elementData = this._applyBackdropPadding(step.backdropPadding, elementData); } @@ -795,6 +857,69 @@ return this._duration = null; }; + Tour.prototype._getProtocol = function(url) { + url = url.split('://'); + if (url.length > 1) { + return url[0]; + } else { + return 'http'; + } + }; + + Tour.prototype._getHost = function(url) { + url = url.split('//'); + url = url.length > 1 ? url[1] : url[0]; + return url.split('/')[0]; + }; + + Tour.prototype._getPath = function(path) { + return path.replace(/\/?$/, '').split('?')[0].split('#')[0]; + }; + + Tour.prototype._getQuery = function(path) { + return this._getParams(path, '?'); + }; + + Tour.prototype._getHash = function(path) { + return this._getParams(path, '#'); + }; + + Tour.prototype._getParams = function(path, start) { + var param, params, paramsObject, _i, _len; + params = path.split(start); + if (params.length === 1) { + return {}; + } + params = params[1].split('&'); + paramsObject = {}; + for (_i = 0, _len = params.length; _i < _len; _i++) { + param = params[_i]; + param = param.split('='); + paramsObject[param[0]] = param[1] || ''; + } + return paramsObject; + }; + + Tour.prototype._equal = function(obj1, obj2) { + var k, v; + if ({}.toString.call(obj1) === '[object Object]' && {}.toString.call(obj2) === '[object Object]') { + for (k in obj1) { + v = obj1[k]; + if (obj2[k] !== v) { + return false; + } + } + for (k in obj2) { + v = obj2[k]; + if (obj1[k] !== v) { + return false; + } + } + return true; + } + return obj1 === obj2; + }; + return Tour; })(); diff --git a/client/galaxy/scripts/libs/d3.js b/client/galaxy/scripts/libs/d3.js index 3a0faeba112..aded45c440c 100644 --- a/client/galaxy/scripts/libs/d3.js +++ b/client/galaxy/scripts/libs/d3.js @@ -1,6 +1,6 @@ !function() { var d3 = { - version: "3.5.6" + version: "3.5.17" }; var d3_arraySlice = [].slice, d3_array = function(list) { return d3_arraySlice.call(list); @@ -220,20 +220,20 @@ while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; return pairs; }; - d3.zip = function() { - if (!(n = arguments.length)) return []; - for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { - for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { - zip[j] = arguments[j][i]; + d3.transpose = function(matrix) { + if (!(n = matrix.length)) return []; + for (var i = -1, m = d3.min(matrix, d3_transposeLength), transpose = new Array(m); ++i < m; ) { + for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n; ) { + row[j] = matrix[j][i]; } } - return zips; + return transpose; }; - function d3_zipLength(d) { + function d3_transposeLength(d) { return d.length; } - d3.transpose = function(matrix) { - return d3.zip.apply(d3, matrix); + d3.zip = function() { + return d3.transpose(arguments); }; d3.keys = function(map) { var keys = []; @@ -620,9 +620,10 @@ return d3_selectAll(selector, this); }; } + var d3_nsXhtml = "http://www.w3.org/1999/xhtml"; var d3_nsPrefix = { svg: "http://www.w3.org/2000/svg", - xhtml: "http://www.w3.org/1999/xhtml", + xhtml: d3_nsXhtml, xlink: "http://www.w3.org/1999/xlink", xml: "http://www.w3.org/XML/1998/namespace", xmlns: "http://www.w3.org/2000/xmlns/" @@ -631,10 +632,7 @@ prefix: d3_nsPrefix, qualify: function(name) { var i = name.indexOf(":"), prefix = name; - if (i >= 0) { - prefix = name.slice(0, i); - name = name.slice(i + 1); - } + if (i >= 0 && (prefix = name.slice(0, i)) !== "xmlns") name = name.slice(i + 1); return d3_nsPrefix.hasOwnProperty(prefix) ? { space: d3_nsPrefix[prefix], local: name @@ -808,7 +806,7 @@ function d3_selection_creator(name) { function create() { var document = this.ownerDocument, namespace = this.namespaceURI; - return namespace ? document.createElementNS(namespace, name) : document.createElement(name); + return namespace === d3_nsXhtml && document.documentElement.namespaceURI === d3_nsXhtml ? document.createElement(name) : document.createElementNS(namespace, name); } function createNS() { return this.ownerDocument.createElementNS(name.space, name.local); @@ -845,12 +843,14 @@ if (key) { var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; for (i = -1; ++i < n; ) { - if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { - exitNodes[i] = node; - } else { - nodeByKeyValue.set(keyValue, node); + if (node = group[i]) { + if (nodeByKeyValue.has(keyValue = key.call(node, node.__data__, i))) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues[i] = keyValue; } - keyValues[i] = keyValue; } for (i = -1; ++i < m; ) { if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { @@ -862,7 +862,7 @@ nodeByKeyValue.set(keyValue, true); } for (i = -1; ++i < n; ) { - if (nodeByKeyValue.get(keyValues[i]) !== true) { + if (i in keyValues && nodeByKeyValue.get(keyValues[i]) !== true) { exitNodes[i] = group[i]; } } @@ -1054,7 +1054,7 @@ group = d3_array(d3_selectAll(nodes, d3_document)); group.parentNode = d3_document.documentElement; } else { - group = nodes; + group = d3_array(nodes); group.parentNode = null; } return d3_selection([ group ]); @@ -1205,7 +1205,7 @@ } function dragstart(id, position, subject, move, end) { return function() { - var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); + var that = this, target = d3.event.target.correspondingElement || d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); if (origin) { dragOffset = origin.apply(that, arguments); dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; @@ -1233,7 +1233,7 @@ function ended() { if (!position(parent, dragId)) return; dragSubject.on(move + dragName, null).on(end + dragName, null); - dragRestore(dragged && d3.event.target === target); + dragRestore(dragged); dispatch({ type: "dragend" }); @@ -1285,18 +1285,22 @@ } var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; d3.interpolateZoom = function(p0, p1) { - var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; - var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; - function interpolate(t) { - var s = t * S; - if (dr) { - var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); + var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2], dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, i, S; + if (d2 < ε2) { + S = Math.log(w1 / w0) / ρ; + i = function(t) { + return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * t * S) ]; + }; + } else { + var d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1); + S = (r1 - r0) / ρ; + i = function(t) { + var s = t * S, coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; - } - return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; + }; } - interpolate.duration = S * 1e3; - return interpolate; + i.duration = S * 1e3; + return i; }; d3.behavior.zoom = function() { var view = { @@ -1366,8 +1370,9 @@ view = { x: view.x, y: view.y, - k: +_ + k: null }; + scaleTo(+_); rescale(); return zoom; }; @@ -1466,7 +1471,7 @@ }), center0 = null; } function mousedowned() { - var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); + var that = this, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); d3_selection_interrupt.call(that); zoomstarted(dispatch); function moved() { @@ -1476,7 +1481,7 @@ } function ended() { subject.on(mousemove, null).on(mouseup, null); - dragRestore(dragged && d3.event.target === target); + dragRestore(dragged); zoomended(dispatch); } } @@ -1698,9 +1703,8 @@ return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); } function d3_rgb_parse(format, rgb, hsl) { - format = format.toLowerCase(); var r = 0, g = 0, b = 0, m1, m2, color; - m1 = /([a-z]+)\((.*)\)/.exec(format); + m1 = /([a-z]+)\((.*)\)/.exec(format = format.toLowerCase()); if (m1) { m2 = m1[2].split(","); switch (m1[1]) { @@ -2115,17 +2119,19 @@ }; d3.csv = d3.dsv(",", "text/csv"); d3.tsv = d3.dsv(" ", "text/tab-separated-values"); - var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { + var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { setTimeout(callback, 17); }; - d3.timer = function(callback, delay, then) { + d3.timer = function() { + d3_timer.apply(this, arguments); + }; + function d3_timer(callback, delay, then) { var n = arguments.length; if (n < 2) delay = 0; if (n < 3) then = Date.now(); var time = then + delay, timer = { c: callback, t: time, - f: false, n: null }; if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; @@ -2135,7 +2141,8 @@ d3_timer_interval = 1; d3_timer_frame(d3_timer_step); } - }; + return timer; + } function d3_timer_step() { var now = d3_timer_mark(), delay = d3_timer_sweep() - now; if (delay > 24) { @@ -2154,22 +2161,21 @@ d3_timer_sweep(); }; function d3_timer_mark() { - var now = Date.now(); - d3_timer_active = d3_timer_queueHead; - while (d3_timer_active) { - if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); - d3_timer_active = d3_timer_active.n; + var now = Date.now(), timer = d3_timer_queueHead; + while (timer) { + if (now >= timer.t && timer.c(now - timer.t)) timer.c = null; + timer = timer.n; } return now; } function d3_timer_sweep() { var t0, t1 = d3_timer_queueHead, time = Infinity; while (t1) { - if (t1.f) { - t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; - } else { + if (t1.c) { if (t1.t < time) time = t1.t; t1 = (t0 = t1).n; + } else { + t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; } } d3_timer_queueTail = t0; @@ -2184,7 +2190,7 @@ var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); d3.formatPrefix = function(value, precision) { var i = 0; - if (value) { + if (value = +value) { if (value < 0) value *= -1; if (precision) value = d3.round(value, d3_format_precision(value, precision)); i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); @@ -2534,7 +2540,8 @@ if (i != string.length) return null; if ("p" in d) d.H = d.H % 12 + d.p * 12; var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); - if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { + if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("W" in d || "U" in d) { + if (!("w" in d)) d.w = "W" in d ? 1 : 0; date.setFullYear(d.y, 0, 1); date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); } else date.setFullYear(d.y, d.m, d.d); @@ -3518,7 +3525,7 @@ λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; } } - return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; + return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < -ε) ^ winding & 1; } function d3_geo_clipCircle(radius) { var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); @@ -5986,54 +5993,68 @@ f: 0 }; d3.interpolateTransform = d3_interpolateTransform; - function d3_interpolateTransform(a, b) { - var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; - if (ta[0] != tb[0] || ta[1] != tb[1]) { - s.push("translate(", null, ",", null, ")"); + function d3_interpolateTransformPop(s) { + return s.length ? s.pop() + "," : ""; + } + function d3_interpolateTranslate(ta, tb, s, q) { + if (ta[0] !== tb[0] || ta[1] !== tb[1]) { + var i = s.push("translate(", null, ",", null, ")"); q.push({ - i: 1, + i: i - 4, x: d3_interpolateNumber(ta[0], tb[0]) }, { - i: 3, + i: i - 2, x: d3_interpolateNumber(ta[1], tb[1]) }); } else if (tb[0] || tb[1]) { s.push("translate(" + tb + ")"); - } else { - s.push(""); } - if (ra != rb) { + } + function d3_interpolateRotate(ra, rb, s, q) { + if (ra !== rb) { if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; q.push({ - i: s.push(s.pop() + "rotate(", null, ")") - 2, + i: s.push(d3_interpolateTransformPop(s) + "rotate(", null, ")") - 2, x: d3_interpolateNumber(ra, rb) }); } else if (rb) { - s.push(s.pop() + "rotate(" + rb + ")"); + s.push(d3_interpolateTransformPop(s) + "rotate(" + rb + ")"); } - if (wa != wb) { + } + function d3_interpolateSkew(wa, wb, s, q) { + if (wa !== wb) { q.push({ - i: s.push(s.pop() + "skewX(", null, ")") - 2, + i: s.push(d3_interpolateTransformPop(s) + "skewX(", null, ")") - 2, x: d3_interpolateNumber(wa, wb) }); } else if (wb) { - s.push(s.pop() + "skewX(" + wb + ")"); + s.push(d3_interpolateTransformPop(s) + "skewX(" + wb + ")"); } - if (ka[0] != kb[0] || ka[1] != kb[1]) { - n = s.push(s.pop() + "scale(", null, ",", null, ")"); + } + function d3_interpolateScale(ka, kb, s, q) { + if (ka[0] !== kb[0] || ka[1] !== kb[1]) { + var i = s.push(d3_interpolateTransformPop(s) + "scale(", null, ",", null, ")"); q.push({ - i: n - 4, + i: i - 4, x: d3_interpolateNumber(ka[0], kb[0]) }, { - i: n - 2, + i: i - 2, x: d3_interpolateNumber(ka[1], kb[1]) }); - } else if (kb[0] != 1 || kb[1] != 1) { - s.push(s.pop() + "scale(" + kb + ")"); + } else if (kb[0] !== 1 || kb[1] !== 1) { + s.push(d3_interpolateTransformPop(s) + "scale(" + kb + ")"); } - n = q.length; + } + function d3_interpolateTransform(a, b) { + var s = [], q = []; + a = d3.transform(a), b = d3.transform(b); + d3_interpolateTranslate(a.translate, b.translate, s, q); + d3_interpolateRotate(a.rotate, b.rotate, s, q); + d3_interpolateSkew(a.skew, b.skew, s, q); + d3_interpolateScale(a.scale, b.scale, s, q); + a = b = null; return function(t) { - var i = -1, o; + var i = -1, n = q.length, o; while (++i < n) s[(o = q[i]).i] = o.x(t); return s.join(""); }; @@ -6137,7 +6158,7 @@ index: di, startAngle: x0, endAngle: x, - value: (x - x0) / k + value: groupSums[di] }; x += padding; } @@ -6205,7 +6226,7 @@ return chord; }; d3.layout.force = function() { - var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; + var force = {}, event = d3.dispatch("start", "tick", "end"), timer, size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; function repulse(node) { return function(quad, x1, _, x2) { if (quad.point !== node) { @@ -6229,6 +6250,7 @@ } force.tick = function() { if ((alpha *= .99) < .005) { + timer = null; event.end({ type: "end", alpha: alpha = 0 @@ -6246,7 +6268,7 @@ l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; x *= l; y *= l; - t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.x -= x * (k = s.weight + t.weight ? s.weight / (s.weight + t.weight) : .5); t.y -= y * k; s.x += x * (k = 1 - k); s.y += y * k; @@ -6342,13 +6364,21 @@ if (!arguments.length) return alpha; x = +x; if (alpha) { - if (x > 0) alpha = x; else alpha = 0; + if (x > 0) { + alpha = x; + } else { + timer.c = null, timer.t = NaN, timer = null; + event.end({ + type: "end", + alpha: alpha = 0 + }); + } } else if (x > 0) { event.start({ type: "start", alpha: alpha = x }); - d3.timer(force.tick); + timer = d3_timer(force.tick); } return force; }; @@ -6602,7 +6632,7 @@ function pie(data) { var n = data.length, values = data.map(function(d, i) { return +value.call(pie, d, i); - }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), k = (da - n * pa) / d3.sum(values), index = d3.range(n), arcs = [], v; + }), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), sum = d3.sum(values), k = sum ? (da - n * pa) / sum : 0, index = d3.range(n), arcs = [], v; if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { return values[j] - values[i]; } : function(i, j) { @@ -7315,10 +7345,8 @@ } function treemap(d) { var nodes = stickies || hierarchy(d), root = nodes[0]; - root.x = 0; - root.y = 0; - root.dx = size[0]; - root.dy = size[1]; + root.x = root.y = 0; + if (root.value) root.dx = size[0], root.dy = size[1]; else root.dx = root.dy = 0; if (stickies) hierarchy.revalue(root); scale([ root ], root.dx * root.dy / root.value); (stickies ? stickify : squarify)(root); @@ -7538,7 +7566,9 @@ return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); } function d3_scale_linearNice(domain, m) { - return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); + return domain; } function d3_scale_linearTickRange(domain, m) { if (m == null) m = 10; @@ -7640,10 +7670,11 @@ scale.tickFormat = function(n, format) { if (!arguments.length) return d3_scale_logFormat; if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); - var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, - Math.floor), e; + var k = Math.max(1, base * n / scale.ticks().length); return function(d) { - return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + var i = d / pow(Math.round(log(d))); + if (i * base < base - .5) i *= base; + return i <= k ? format(d) : ""; }; }; scale.copy = function() { @@ -7982,11 +8013,16 @@ } else { x2 = y2 = 0; } - if ((rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { + if (da > ε && (rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { cr = r0 < r1 ^ cw ? 0 : 1; - var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + var rc1 = rc, rc0 = rc; + if (da < π) { + var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); + rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); + rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); + } if (x1 != null) { - var rc1 = Math.min(rc, (r1 - lc) / (kc + 1)), t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); + var t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); if (rc === rc1) { path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); } else { @@ -7996,7 +8032,7 @@ path.push("M", x0, ",", y0); } if (x3 != null) { - var rc0 = Math.min(rc, (r0 - lc) / (kc - 1)), t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); + var t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); if (rc === rc0) { path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); } else { @@ -8078,7 +8114,7 @@ return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; } function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { - var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(r * r * d2 - D * D), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; + var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; } @@ -8150,10 +8186,10 @@ value.closed = /-closed$/.test(key); }); function d3_svg_lineLinear(points) { - return points.join("L"); + return points.length > 1 ? points.join("L") : points + "Z"; } function d3_svg_lineLinearClosed(points) { - return d3_svg_lineLinear(points) + "Z"; + return points.join("L") + "Z"; } function d3_svg_lineStep(points) { var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; @@ -8175,7 +8211,7 @@ return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); } function d3_svg_lineCardinalClosed(points, tension) { - return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + return points.length < 3 ? d3_svg_lineLinearClosed(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); } function d3_svg_lineCardinal(points, tension) { @@ -8611,9 +8647,11 @@ var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); function d3_selection_interruptNS(ns) { return function() { - var lock, active; - if ((lock = this[ns]) && (active = lock[lock.active])) { - if (--lock.count) delete lock[lock.active]; else delete this[ns]; + var lock, activeId, active; + if ((lock = this[ns]) && (active = lock[activeId = lock.active])) { + active.timer.c = null; + active.timer.t = NaN; + if (--lock.count) delete lock[activeId]; else delete this[ns]; lock.active += .5; active.event && active.event.interrupt.call(this, this.__data__, active.index); } @@ -8868,12 +8906,68 @@ var lock = node[ns] || (node[ns] = { active: 0, count: 0 - }), transition = lock[id]; + }), transition = lock[id], time, timer, duration, ease, tweens; + function schedule(elapsed) { + var delay = transition.delay; + timer.t = delay + time; + if (delay <= elapsed) return start(elapsed - delay); + timer.c = start; + } + function start(elapsed) { + var activeId = lock.active, active = lock[activeId]; + if (active) { + active.timer.c = null; + active.timer.t = NaN; + --lock.count; + delete lock[activeId]; + active.event && active.event.interrupt.call(node, node.__data__, active.index); + } + for (var cancelId in lock) { + if (+cancelId < id) { + var cancel = lock[cancelId]; + cancel.timer.c = null; + cancel.timer.t = NaN; + --lock.count; + delete lock[cancelId]; + } + } + timer.c = tick; + d3_timer(function() { + if (timer.c && tick(elapsed || 1)) { + timer.c = null; + timer.t = NaN; + } + return 1; + }, 0, time); + lock.active = id; + transition.event && transition.event.start.call(node, node.__data__, i); + tweens = []; + transition.tween.forEach(function(key, value) { + if (value = value.call(node, node.__data__, i)) { + tweens.push(value); + } + }); + ease = transition.ease; + duration = transition.duration; + } + function tick(elapsed) { + var t = elapsed / duration, e = ease(t), n = tweens.length; + while (n > 0) { + tweens[--n].call(node, e); + } + if (t >= 1) { + transition.event && transition.event.end.call(node, node.__data__, i); + if (--lock.count) delete lock[id]; else delete node[ns]; + return 1; + } + } if (!transition) { - var time = inherit.time; + time = inherit.time; + timer = d3_timer(schedule, 0, time); transition = lock[id] = { tween: new d3_Map(), time: time, + timer: timer, delay: inherit.delay, duration: inherit.duration, ease: inherit.ease, @@ -8881,49 +8975,6 @@ }; inherit = null; ++lock.count; - d3.timer(function(elapsed) { - var delay = transition.delay, duration, ease, timer = d3_timer_active, tweened = []; - timer.t = delay + time; - if (delay <= elapsed) return start(elapsed - delay); - timer.c = start; - function start(elapsed) { - if (lock.active > id) return stop(); - var active = lock[lock.active]; - if (active) { - --lock.count; - delete lock[lock.active]; - active.event && active.event.interrupt.call(node, node.__data__, active.index); - } - lock.active = id; - transition.event && transition.event.start.call(node, node.__data__, i); - transition.tween.forEach(function(key, value) { - if (value = value.call(node, node.__data__, i)) { - tweened.push(value); - } - }); - ease = transition.ease; - duration = transition.duration; - d3.timer(function() { - timer.c = tick(elapsed || 1) ? d3_true : tick; - return 1; - }, 0, time); - } - function tick(elapsed) { - if (lock.active !== id) return 1; - var t = elapsed / duration, e = ease(t), n = tweened.length; - while (n > 0) { - tweened[--n].call(node, e); - } - if (t >= 1) { - transition.event && transition.event.end.call(node, node.__data__, i); - return stop(); - } - } - function stop() { - if (--lock.count) delete lock[id]; else delete node[ns]; - return 1; - } - }, 0, time); } } d3.svg.axis = function() { @@ -8977,7 +9028,7 @@ }; axis.ticks = function() { if (!arguments.length) return tickArguments_; - tickArguments_ = arguments; + tickArguments_ = d3_array(arguments); return axis; }; axis.tickValues = function(x) { @@ -9499,6 +9550,5 @@ d3.xml = d3_xhrType(function(request) { return request.responseXML; }); - if (typeof define === "function" && define.amd) define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; - this.d3 = d3; + if (typeof define === "function" && define.amd) this.d3 = d3, define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; else this.d3 = d3; }(); \ No newline at end of file diff --git a/client/galaxy/scripts/libs/raven.js b/client/galaxy/scripts/libs/raven.js index 195d002ab65..6d0f2356afb 100644 --- a/client/galaxy/scripts/libs/raven.js +++ b/client/galaxy/scripts/libs/raven.js @@ -1,4 +1,4 @@ -/*! Raven.js 1.1.19 (b51bc89) | github.com/getsentry/raven-js */ +/*! Raven.js 1.1.22 (6278810) | github.com/getsentry/raven-js */ /* * Includes TraceKit @@ -21,7 +21,8 @@ var TraceKit = { remoteFetching: false, collectWindowErrors: true, // 3 lines before, the offending line, 3 lines after - linesOfContext: 7 + linesOfContext: 7, + debug: false }; // global reference to slice @@ -319,8 +320,7 @@ TraceKit.report = (function reportModuleWrapper() { * */ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { - var debug = false, - sourceCache = {}; + var sourceCache = {}; /** * Attempts to retrieve source code via XMLHttpRequest, which is used @@ -362,7 +362,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { // URL needs to be able to fetched within the acceptable domain. Otherwise, // cross-domain errors will be triggered. var source = ''; - if (url.indexOf(document.domain) !== -1) { + var domain = ''; + try { domain = document.domain; } catch (e) {} + if (url.indexOf(domain) !== -1) { source = loadSource(url); } sourceCache[url] = source ? source.split('\n') : []; @@ -627,12 +629,11 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @return {?Object.} Stack trace information. */ function computeStackTraceFromStackProp(ex) { - if (!ex.stack) { - return null; - } + if (isUndefined(ex.stack) || !ex.stack) return; - var chrome = /^\s*at (.*?) ?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + var chrome = /^\s*at (.*?) ?\(?((?:(?:file|https?|chrome-extension):.*?)|):(\d+)(?::(\d+))?\)?\s*$/i, gecko = /^\s*(.*?)(?:\((.*?)\))?@((?:file|https?|chrome).*?):(\d+)(?::(\d+))?\s*$/i, + winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i, lines = ex.stack.split('\n'), stack = [], parts, @@ -655,6 +656,13 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'line': +parts[3], 'column': parts[4] ? +parts[4] : null }; + } else if ((parts = winjs.exec(lines[i]))) { + element = { + 'url': parts[2], + 'func': parts[1] || UNKNOWN_FUNCTION, + 'line': +parts[3], + 'column': parts[4] ? +parts[4] : null + }; } else { continue; } @@ -702,6 +710,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { // else to it because Opera is not very good at providing it // reliably in other circumstances. var stacktrace = ex.stacktrace; + if (isUndefined(ex.stacktrace) || !ex.stacktrace) return; var testRE = / line (\d+), column (\d+) in (?:]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i, lines = stacktrace.split('\n'), @@ -951,6 +960,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { item.func = parts[1]; } + if (typeof item.func === 'undefined') { + try { + item.func = parts.input.substring(0, parts.input.indexOf('{')); + } catch (e) { } + } + if ((source = findSourceByFunctionBody(curr))) { item.url = source.url; item.line = source.line; @@ -1008,7 +1023,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return stack; } } catch (e) { - if (debug) { + if (TraceKit.debug) { throw e; } } @@ -1019,7 +1034,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return stack; } } catch (e) { - if (debug) { + if (TraceKit.debug) { throw e; } } @@ -1030,7 +1045,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return stack; } } catch (e) { - if (debug) { + if (TraceKit.debug) { throw e; } } @@ -1041,12 +1056,16 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return stack; } } catch (e) { - if (debug) { + if (TraceKit.debug) { throw e; } } - return {}; + return { + 'name': ex.name, + 'message': ex.message, + 'url': document.location.href, + }; } computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; @@ -1076,24 +1095,30 @@ var _Raven = window.Raven, ignoreUrls: [], whitelistUrls: [], includePaths: [], + crossOrigin: 'anonymous', collectWindowErrors: true, tags: {}, maxMessageLength: 100, extra: {} }, - authQueryString, isRavenInstalled = false, - objectPrototype = Object.prototype, + // capture references to window.console *and* all its methods first + // before the console plugin has a chance to monkey patch + originalConsole = window.console || {}, + originalConsoleMethods = {}, startTime = now(); +for (var method in originalConsole) { + originalConsoleMethods[method] = originalConsole[method]; +} /* * The core Raven singleton * * @this {Raven} */ var Raven = { - VERSION: '1.1.19', + VERSION: '1.1.22', debug: true, @@ -1166,8 +1191,6 @@ var Raven = { TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; - setAuthQueryString(); - // return for chaining return Raven; }, @@ -1302,7 +1325,8 @@ var Raven = { // raises an exception different from the one we asked to // report on. try { - TraceKit.report(ex, options); + var stack = TraceKit.computeStackTrace(ex); + handleStackInfo(stack, options); } catch(ex1) { if(ex !== ex1) { throw ex1; @@ -1473,7 +1497,7 @@ function triggerEvent(eventType, options) { } var dsnKeys = 'source protocol user pass host port path'.split(' '), - dsnPattern = /^(?:(\w+):)?\/\/(\w+)(:\w+)?@([\w\.-]+)(?::(\d+))?(\/.*)/; + dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; function RavenConfigError(message) { this.name = 'RavenConfigError'; @@ -1559,15 +1583,6 @@ function each(obj, callback) { } } - -function setAuthQueryString() { - authQueryString = - '?sentry_version=4' + - '&sentry_client=raven-js/' + Raven.VERSION + - '&sentry_key=' + globalKey; -} - - function handleStackInfo(stackInfo, options) { var frames = []; @@ -1614,7 +1629,7 @@ function normalizeFrame(frame) { normalized.in_app = !( // determine if an exception came from outside of our app // first we check the global includePaths list. - !globalOptions.includePaths.test(normalized.filename) || + (!!globalOptions.includePaths.test && !globalOptions.includePaths.test(normalized.filename)) || // Now we check for fun, if the function name is Raven or TraceKit /(Raven|TraceKit)\./.test(normalized['function']) || // finally, we do a last ditch effort and check for raven.min.js @@ -1664,20 +1679,15 @@ function extractContextFromFrame(frame) { } function processException(type, message, fileurl, lineno, frames, options) { - var stacktrace, label, i; + var stacktrace, i, fullMessage; + + if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return; - // In some instances message is not actually a string, no idea why, - // so we want to always coerce it to one. message += ''; + message = truncate(message, globalOptions.maxMessageLength); - // Sometimes an exception is getting logged in Sentry as - // - // This can only mean that the message was falsey since this value - // is hardcoded into Sentry itself. - // At this point, if the message is falsey, we bail since it's useless - if (type === 'Error' && !message) return; - - if (globalOptions.ignoreErrors.test(message)) return; + fullMessage = type + ': ' + message; + fullMessage = truncate(fullMessage, globalOptions.maxMessageLength); if (frames && frames.length) { fileurl = frames[0].filename || fileurl; @@ -1695,13 +1705,8 @@ function processException(type, message, fileurl, lineno, frames, options) { }; } - // Truncate the message to a max of characters - message = truncate(message, globalOptions.maxMessageLength); - - if (globalOptions.ignoreUrls && globalOptions.ignoreUrls.test(fileurl)) return; - if (globalOptions.whitelistUrls && !globalOptions.whitelistUrls.test(fileurl)) return; - - label = lineno ? message + ' at ' + lineno : message; + if (!!globalOptions.ignoreUrls.test && globalOptions.ignoreUrls.test(fileurl)) return; + if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return; // Fire away! send( @@ -1714,7 +1719,7 @@ function processException(type, message, fileurl, lineno, frames, options) { // sentry.interfaces.Stacktrace stacktrace: stacktrace, culprit: fileurl, - message: label + message: fullMessage }, options) ); } @@ -1738,13 +1743,18 @@ function now() { } function getHttpData() { + if (!document.location || !document.location.href) { + return; + } + var http = { - url: document.location.href, headers: { 'User-Agent': navigator.userAgent } }; + http.url = document.location.href; + if (document.referrer) { http.headers.Referer = document.referrer; } @@ -1753,15 +1763,17 @@ function getHttpData() { } function send(data) { - if (!isSetup()) return; - - data = objectMerge({ + var baseData = { project: globalProject, logger: globalOptions.logger, - platform: 'javascript', - // sentry.interfaces.Http - request: getHttpData() - }, data); + platform: 'javascript' + }; + var http = getHttpData(); + if (http) { + baseData.request = http; + } + + data = objectMerge(baseData, data); // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge data.tags = objectMerge(objectMerge({}, globalOptions.tags), data.tags); @@ -1780,7 +1792,7 @@ function send(data) { data.user = globalUser; } - // Include the release iff it's defined in globalOptions + // Include the release if it's defined in globalOptions if (globalOptions.release) data.release = globalOptions.release; if (isFunction(globalOptions.dataCallback)) { @@ -1802,27 +1814,46 @@ function send(data) { // Set lastEventId after we know the error should actually be sent lastEventId = data.event_id || (data.event_id = uuid4()); - makeRequest(data); + logDebug('debug', 'Raven about to send:', data); + + if (!isSetup()) return; + + (globalOptions.transport || makeRequest)({ + url: globalServer, + auth: { + sentry_version: '4', + sentry_client: 'raven-js/' + Raven.VERSION, + sentry_key: globalKey + }, + data: data, + options: globalOptions, + onSuccess: function success() { + triggerEvent('success', { + data: data, + src: globalServer + }); + }, + onError: function failure() { + triggerEvent('failure', { + data: data, + src: globalServer + }); + } + }); } +function makeRequest(opts) { + // Tack on sentry_data to auth options, which get urlencoded + opts.auth.sentry_data = JSON.stringify(opts.data); -function makeRequest(data) { var img = newImage(), - src = globalServer + authQueryString + '&sentry_data=' + encodeURIComponent(JSON.stringify(data)); + src = opts.url + '?' + urlencode(opts.auth); - img.crossOrigin = 'anonymous'; - img.onload = function success() { - triggerEvent('success', { - data: data, - src: src - }); - }; - img.onerror = img.onabort = function failure() { - triggerEvent('failure', { - data: data, - src: src - }); - }; + if (opts.options.crossOrigin || opts.options.crossOrigin === '') { + img.crossOrigin = opts.options.crossOrigin; + } + img.onload = opts.onSuccess; + img.onerror = img.onabort = opts.onError; img.src = src; } @@ -1833,10 +1864,14 @@ function newImage() { return document.createElement('img'); } +var ravenNotConfiguredError; + function isSetup() { if (!hasJSON) return false; // needs JSON support if (!globalServer) { - logDebug('error', 'Error: Raven has not been configured.'); + if (!ravenNotConfiguredError) + logDebug('error', 'Error: Raven has not been configured.'); + ravenNotConfiguredError = true; return false; } return true; @@ -1864,18 +1899,44 @@ function joinRegExp(patterns) { return new RegExp(sources.join('|'), 'i'); } -// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 function uuid4() { - return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random()*16|0, - v = c == 'x' ? r : (r&0x3|0x8); - return v.toString(16); - }); + var crypto = window.crypto || window.msCrypto; + + if (!isUndefined(crypto) && crypto.getRandomValues) { + // Use window.crypto API if available + 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); + }); + } } -function logDebug(level, message) { - if (window.console && console[level] && Raven.debug) { - console[level](message); +function logDebug(level) { + if (originalConsoleMethods[level] && Raven.debug) { + // _slice is coming from vendor/TraceKit/tracekit.js + // so it's accessible globally + originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1)); } } @@ -1886,6 +1947,15 @@ function afterLoad() { Raven.config(RavenConfig.dsn, RavenConfig.config).install(); } } + +function urlencode(o) { + var pairs = []; + each(o, function(key, value) { + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + return pairs.join('&'); +} + afterLoad(); // Expose Raven to the world diff --git a/client/galaxy/scripts/libs/require.js b/client/galaxy/scripts/libs/require.js index 5237640495b..e33c7dded70 100644 --- a/client/galaxy/scripts/libs/require.js +++ b/client/galaxy/scripts/libs/require.js @@ -1,5 +1,5 @@ /** vim: et:ts=4:sw=4:sts=4 - * @license RequireJS 2.1.20 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved. + * @license RequireJS 2.1.22 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved. * Available via the MIT or new BSD license. * see: http://github.com/jrburke/requirejs for details */ @@ -12,7 +12,7 @@ var requirejs, require, define; (function (global) { var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, - version = '2.1.20', + version = '2.1.22', commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/, @@ -861,21 +861,10 @@ var requirejs, require, define; if (this.depCount < 1 && !this.defined) { if (isFunction(factory)) { - //If there is an error listener, favor passing - //to that instead of throwing an error. However, - //only do it for define()'d modules. require - //errbacks should not be called for failures in - //their callbacks (#699). However if a global - //onError is set, use that. - if ((this.events.error && this.map.isDefine) || - req.onError !== defaultOnError) { - try { - exports = context.execCb(id, factory, depExports, exports); - } catch (e) { - err = e; - } - } else { + try { exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; } // Favor return value over exports. If node/cjs in play, @@ -892,12 +881,30 @@ var requirejs, require, define; } if (err) { - err.requireMap = this.map; - err.requireModules = this.map.isDefine ? [this.map.id] : null; - err.requireType = this.map.isDefine ? 'define' : 'require'; - return onError((this.error = err)); + // If there is an error listener, favor passing + // to that instead of throwing an error. However, + // only do it for define()'d modules. require + // errbacks should not be called for failures in + // their callbacks (#699). However if a global + // onError is set, use that. + if ((this.events.error && this.map.isDefine) || + req.onError !== defaultOnError) { + err.requireMap = this.map; + err.requireModules = this.map.isDefine ? [this.map.id] : null; + err.requireType = this.map.isDefine ? 'define' : 'require'; + return onError((this.error = err)); + } else if (typeof console !== 'undefined' && + console.error) { + // Log the error for debugging. If promises could be + // used, this would be different, but making do. + console.error(err); + } else { + // Do not want to completely lose the error. While this + // will mess up processing and lead to similar results + // as bug 1440, it at least surfaces the error. + req.onError(err); + } } - } else { //Just a literal value exports = factory; @@ -909,7 +916,11 @@ var requirejs, require, define; defined[id] = exports; if (req.onResourceLoad) { - req.onResourceLoad(context, this.map, this.depMaps); + var resLoadMaps = []; + each(this.depMaps, function (depMap) { + resLoadMaps.push(depMap.normalizedMap || depMap); + }); + req.onResourceLoad(context, this.map, resLoadMaps); } } @@ -968,6 +979,7 @@ var requirejs, require, define; this.map.parentMap); on(normalizedMap, 'defined', bind(this, function (value) { + this.map.normalizedMap = normalizedMap; this.init([], function () { return value; }, null, { enabled: true, ignore: true @@ -1706,7 +1718,21 @@ var requirejs, require, define; onScriptError: function (evt) { var data = getScriptData(evt); if (!hasPathFallback(data.id)) { - return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id])); + var parents = []; + eachProp(registry, function(value, key) { + if (key.indexOf('_@r') !== 0) { + each(value.depMaps, function(depMap) { + if (depMap.id === data.id) { + parents.push(key); + } + return true; + }); + } + }); + return onError(makeError('scripterror', 'Script error for "' + data.id + + (parents.length ? + '", needed by: ' + parents.join(', ') : + '"'), evt, [data.id])); } } }; @@ -1933,9 +1959,9 @@ var requirejs, require, define; //In a web worker, use importScripts. This is not a very //efficient use of importScripts, importScripts will block until //its script is downloaded and evaluated. However, if web workers - //are in play, the expectation that a build has been done so that - //only one script needs to be loaded anyway. This may need to be - //reevaluated if other use cases become common. + //are in play, the expectation is that a build has been done so + //that only one script needs to be loaded anyway. This may need + //to be reevaluated if other use cases become common. importScripts(url); //Account for anonymous modules diff --git a/client/galaxy/scripts/libs/tracekit.js b/client/galaxy/scripts/libs/tracekit.js index 5e2dc8a3860..4aba91d2572 100644 --- a/client/galaxy/scripts/libs/tracekit.js +++ b/client/galaxy/scripts/libs/tracekit.js @@ -1,8 +1,8 @@ -/* - TraceKit - Cross brower stack traces - github.com/csnover/TraceKit - MIT license -*/ - +/** + * https://github.com/csnover/TraceKit + * @license MIT + * @namespace TraceKit + */ (function(window, undefined) { if (!window) { return; @@ -15,25 +15,34 @@ var _oldTraceKit = window.TraceKit; var _slice = [].slice; var UNKNOWN_FUNCTION = '?'; - /** - * _has, a better form of hasOwnProperty - * Example: _has(MainHostObject, property) === true/false + * A better form of hasOwnProperty
+ * Example: `_has(MainHostObject, property) === true/false` * * @param {Object} object to check property * @param {string} key to check + * @return {Boolean} true if the object has the key and it is not inherited */ function _has(object, key) { return Object.prototype.hasOwnProperty.call(object, key); } +/** + * Returns true if the parameter is undefined
+ * Example: `_isUndefined(val) === true/false` + * + * @param {*} what Value to check + * @return {Boolean} true if undefined and false otherwise + */ function _isUndefined(what) { return typeof what === 'undefined'; } /** - * TraceKit.noConflict: Export TraceKit out to another variable - * Example: var TK = TraceKit.noConflict() + * Export TraceKit out to another variable
+ * Example: `var TK = TraceKit.noConflict()` + * @return {Object} The TraceKit object + * @memberof TraceKit */ TraceKit.noConflict = function noConflict() { window.TraceKit = _oldTraceKit; @@ -41,11 +50,12 @@ TraceKit.noConflict = function noConflict() { }; /** - * TraceKit.wrap: Wrap any function in a TraceKit reporter - * Example: func = TraceKit.wrap(func); + * Wrap any function in a TraceKit reporter
+ * Example: `func = TraceKit.wrap(func);` * * @param {Function} func Function to be wrapped * @return {Function} The wrapped func + * @memberof TraceKit */ TraceKit.wrap = function traceKitWrapper(func) { function wrapped() { @@ -60,23 +70,25 @@ TraceKit.wrap = function traceKitWrapper(func) { }; /** - * TraceKit.report: cross-browser processing of unhandled exceptions + * Cross-browser processing of unhandled exceptions * * Syntax: + * ```js * TraceKit.report.subscribe(function(stackInfo) { ... }) * TraceKit.report.unsubscribe(function(stackInfo) { ... }) * TraceKit.report(exception) * try { ...code... } catch(ex) { TraceKit.report(ex); } + * ``` * * Supports: * - Firefox: full stack trace with line numbers, plus column number - * on top frame; column number is not guaranteed - * - Opera: full stack trace with line and column numbers - * - Chrome: full stack trace with line and column numbers - * - Safari: line and column number for the top frame only; some frames - * may be missing, and column number is not guaranteed - * - IE: line and column number for the top frame only; some frames - * may be missing, and column number is not guaranteed + * on top frame; column number is not guaranteed + * - Opera: full stack trace with line and column numbers + * - Chrome: full stack trace with line and column numbers + * - Safari: line and column number for the top frame only; some frames + * may be missing, and column number is not guaranteed + * - IE: line and column number for the top frame only; some frames + * may be missing, and column number is not guaranteed * * In theory, TraceKit should work on all of the following versions: * - IE5.5+ (only 8.0 tested) @@ -95,17 +107,22 @@ TraceKit.wrap = function traceKitWrapper(func) { * If the exception does not reach the top of the browser, you will only * get a stack trace from the point where TraceKit.report was called. * - * Handlers receive a stackInfo object as described in the + * Handlers receive a TraceKit.StackTrace object as described in the * TraceKit.computeStackTrace docs. + * + * @memberof TraceKit + * @namespace */ TraceKit.report = (function reportModuleWrapper() { var handlers = [], + lastArgs = null, lastException = null, lastExceptionStack = null; /** * Add a crash handler. * @param {Function} handler + * @memberof TraceKit.report */ function subscribe(handler) { installGlobalHandler(); @@ -115,6 +132,7 @@ TraceKit.report = (function reportModuleWrapper() { /** * Remove a crash handler. * @param {Function} handler + * @memberof TraceKit.report */ function unsubscribe(handler) { for (var i = handlers.length - 1; i >= 0; --i) { @@ -126,7 +144,10 @@ TraceKit.report = (function reportModuleWrapper() { /** * Dispatch stack information to all handlers. - * @param {Object.} stack + * @param {TraceKit.StackTrace} stack + * @param {boolean} isWindowError Is this a top-level window error? + * @memberof TraceKit.report + * @throws An exception if an error occurs while calling an handler. */ function notifyHandlers(stack, isWindowError) { var exception = null; @@ -155,44 +176,36 @@ TraceKit.report = (function reportModuleWrapper() { * Supported by Gecko and IE. * @param {string} message Error message. * @param {string} url URL of script that generated the exception. - * @param {(number|string)} lineNo The line number at which the error - * occurred. - * @param {?(number|string)} columnNo The column number at which the error - * occurred. - * @param {?Error} errorObj The actual Error object. + * @param {(number|string)} lineNo The line number at which the error occurred. + * @param {(number|string)=} columnNo The column number at which the error occurred. + * @param {Error=} errorObj The actual Error object. + * @memberof TraceKit.report */ function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) { var stack = null; - if (errorObj) { - stack = TraceKit.computeStackTrace(errorObj); - } - else - { - if (lastExceptionStack) { - TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message); - stack = lastExceptionStack; - lastExceptionStack = null; - lastException = null; - } else { - var location = { - 'url': url, - 'line': lineNo, - 'column': columnNo - }; - location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line); - location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); - stack = { - 'mode': 'onerror', - 'message': message, - 'url': document.location.href, - 'stack': [location], - 'useragent': navigator.userAgent - }; - } - } + if (lastExceptionStack) { + TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message); + processLastException(); + } else if (errorObj) { + stack = TraceKit.computeStackTrace(errorObj); + notifyHandlers(stack, true); + } else { + var location = { + 'url': url, + 'line': lineNo, + 'column': columnNo + }; + location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line); + location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); + stack = { + 'mode': 'onerror', + 'message': message, + 'stack': [location] + }; - notifyHandlers(stack, 'from window.onerror'); + notifyHandlers(stack, true); + } if (_oldOnerrorHandler) { return _oldOnerrorHandler.apply(this, arguments); @@ -201,8 +214,11 @@ TraceKit.report = (function reportModuleWrapper() { return false; } - function installGlobalHandler () - { + /** + * Install a global onerror handler + * @memberof TraceKit.report + */ + function installGlobalHandler () { if (_onErrorHandlerInstalled === true) { return; } @@ -211,26 +227,38 @@ TraceKit.report = (function reportModuleWrapper() { _onErrorHandlerInstalled = true; } + /** + * Process the most recent exception + * @memberof TraceKit.report + */ + function processLastException() { + var _lastExceptionStack = lastExceptionStack, + _lastArgs = lastArgs; + lastArgs = null; + lastExceptionStack = null; + lastException = null; + notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs)); + } + /** * Reports an unhandled Error to TraceKit. * @param {Error} ex + * @memberof TraceKit.report + * @throws An exception if an incomplete stack trace is detected (old IE browsers). */ function report(ex) { - var args = _slice.call(arguments, 1); if (lastExceptionStack) { if (lastException === ex) { return; // already caught by an inner catch block, ignore } else { - var s = lastExceptionStack; - lastExceptionStack = null; - lastException = null; - notifyHandlers.apply(null, [s, null].concat(args)); + processLastException(); } } var stack = TraceKit.computeStackTrace(ex); lastExceptionStack = stack; lastException = ex; + lastArgs = _slice.call(arguments, 1); // If the stack trace is incomplete, wait for 2 seconds for // slow slow IE to see if onerror occurs or not before reporting @@ -238,9 +266,7 @@ TraceKit.report = (function reportModuleWrapper() { // stack trace window.setTimeout(function () { if (lastException === ex) { - lastExceptionStack = null; - lastException = null; - notifyHandlers.apply(null, [stack, null].concat(args)); + processLastException(); } }, (stack.incomplete ? 2000 : 0)); @@ -252,22 +278,36 @@ TraceKit.report = (function reportModuleWrapper() { return report; }()); +/** + * An object representing a single stack frame. + * @typedef {Object} StackFrame + * @property {string} url The JavaScript or HTML file URL. + * @property {string} func The function name, or empty for anonymous functions (if guessing did not work). + * @property {string[]?} args The arguments passed to the function, if known. + * @property {number=} line The line number, if known. + * @property {number=} column The column number, if known. + * @property {string[]} context An array of source code lines; the middle element corresponds to the correct line#. + * @memberof TraceKit + */ + +/** + * An object representing a JavaScript stack trace. + * @typedef {Object} StackTrace + * @property {string} name The name of the thrown exception. + * @property {string} message The exception error message. + * @property {TraceKit.StackFrame[]} stack An array of stack frames. + * @property {string} mode 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace. + * @memberof TraceKit + */ + /** * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript * * Syntax: + * ```js * s = TraceKit.computeStackTrace.ofCaller([depth]) * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below) - * Returns: - * s.name - exception name - * s.message - exception message - * s.stack[i].url - JavaScript or HTML file URL - * s.stack[i].func - function name, or empty for anonymous functions (if guessing did not work) - * s.stack[i].args - arguments passed to the function, if known - * s.stack[i].line - line number, if known - * s.stack[i].column - column number, if known - * s.stack[i].context - an array of source code lines; the middle element corresponds to the correct line# - * s.mode - 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace + * ``` * * Supports: * - Firefox: full stack trace with line numbers and unreliable column @@ -306,6 +346,7 @@ TraceKit.report = (function reportModuleWrapper() { * inner function that actually caused the exception). * * Tracing example: + * ```js * function trace(message) { * var stackInfo = TraceKit.computeStackTrace.ofCaller(); * var data = message + "\n"; @@ -318,6 +359,9 @@ TraceKit.report = (function reportModuleWrapper() { * else * alert(data); * } + * ``` + * @memberof TraceKit + * @namespace */ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { var debug = false, @@ -328,6 +372,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * to look up anonymous function names. * @param {string} url URL of source code. * @return {string} Source contents. + * @memberof TraceKit.computeStackTrace */ function loadSource(url) { if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on. @@ -356,6 +401,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Retrieves source code from the source code cache. * @param {string} url URL of source code. * @return {Array.} Source contents. + * @memberof TraceKit.computeStackTrace */ function getSource(url) { if (typeof url !== 'string') { @@ -365,10 +411,19 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { if (!_has(sourceCache, url)) { // URL needs to be able to fetched within the acceptable domain. Otherwise, // cross-domain errors will be triggered. + /* + Regex matches: + 0 - Full Url + 1 - Protocol + 2 - Domain + 3 - Port (Useful for internal applications) + 4 - Path + */ var source = ''; - - url = url || ''; - if (url.indexOf && url.indexOf(document.domain) !== -1) { + var domain = ''; + try { domain = window.document.domain; } catch (e) { } + var match = /(.*)\:\/\/([^:\/]+)([:\d]*)\/{0,1}([\s\S]*)/.exec(url); + if (match && match[2] === domain) { source = loadSource(url); } sourceCache[url] = source ? source.split('\n') : []; @@ -384,6 +439,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @param {string} url URL of source code. * @param {(string|number)} lineNo Line number in source code. * @return {string} The function name, if discoverable. + * @memberof TraceKit.computeStackTrace */ function guessFunctionName(url, lineNo) { var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/, @@ -420,6 +476,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @param {(string|number)} line Line number in source code to centre * around for context. * @return {?Array.} Lines of source code. + * @memberof TraceKit.computeStackTrace */ function gatherContext(url, line) { var source = getSource(url); @@ -454,6 +511,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * used inside a regular expression as a string literal. * @param {string} text The string. * @return {string} The escaped string literal. + * @memberof TraceKit.computeStackTrace */ function escapeRegExp(text) { return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&'); @@ -465,6 +523,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * be matched the same as their literal friends. * @param {string} body The string. * @return {string} The escaped string. + * @memberof TraceKit.computeStackTrace */ function escapeCodeAsRegExpForMatchingInsideHTML(body) { return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').replace(/\s+/g, '\\s+'); @@ -476,6 +535,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @param {Array.} urls A list of URLs to search. * @return {?Object.} An object containing * the url, line, and column number of the defined function. + * @memberof TraceKit.computeStackTrace */ function findSourceInUrls(re, urls) { var source, m; @@ -507,6 +567,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * @param {string} url The URL to search. * @param {(string|number)} line The line number to examine. * @return {?number} The column number. + * @memberof TraceKit.computeStackTrace */ function findSourceInLine(fragment, url, line) { var source = getSource(url), @@ -528,10 +589,15 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * function definition. * @return {?Object.} An object containing * the url, line, and column number of the defined function. + * @memberof TraceKit.computeStackTrace */ function findSourceByFunctionBody(func) { + if (_isUndefined(window && window.document)) { + return; + } + var urls = [window.location.href], - scripts = document.getElementsByTagName('script'), + scripts = window.document.getElementsByTagName('script'), body, code = '' + func, codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, @@ -630,16 +696,17 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Computes stack trace information from the stack property. * Chrome and Gecko use this property. * @param {Error} ex - * @return {?Object.} Stack trace information. + * @return {?TraceKit.StackTrace} Stack trace information. + * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromStackProp(ex) { if (!ex.stack) { return null; } - var chrome = /^\s*at (.*?) ?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i, - gecko = /^\s*(.*?)(?:\((.*?)\))?@?((?:file|https?|chrome):.*?):(\d+)(?::(\d+))?\s*$/i, - winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, + gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i, + winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|https?|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, lines = ex.stack.split('\n'), stack = [], parts, @@ -647,28 +714,31 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { reference = /^(.*) is undefined$/.exec(ex.message); for (var i = 0, j = lines.length; i < j; ++i) { - if ((parts = gecko.exec(lines[i]))) { + if ((parts = chrome.exec(lines[i]))) { + var isNative = parts[2] && parts[2].indexOf('native') !== -1; element = { - 'url': parts[3], + 'url': !isNative ? parts[2] : null, 'func': parts[1] || UNKNOWN_FUNCTION, - 'args': parts[2] ? parts[2].split(',') : '', - 'line': +parts[4], - 'column': parts[5] ? +parts[5] : null + 'args': isNative ? [parts[2]] : [], + 'line': parts[3] ? +parts[3] : null, + 'column': parts[4] ? +parts[4] : null }; - } else if ((parts = chrome.exec(lines[i]))) { + } else if ( parts = winjs.exec(lines[i]) ) { element = { 'url': parts[2], 'func': parts[1] || UNKNOWN_FUNCTION, + 'args': [], 'line': +parts[3], 'column': parts[4] ? +parts[4] : null }; - } else if ((parts = winjs.exec(lines[i]))) { - element = { - 'url': parts[2], - 'func': parts[1] || UNKNOWN_FUNCTION, - 'line': +parts[3], - 'column': parts[4] ? +parts[4] : null - }; + } else if ((parts = gecko.exec(lines[i]))) { + element = { + 'url': parts[3], + 'func': parts[1] || UNKNOWN_FUNCTION, + 'args': parts[2] ? parts[2].split(',') : [], + 'line': parts[4] ? +parts[4] : null, + 'column': parts[5] ? +parts[5] : null + }; } else { continue; } @@ -701,39 +771,53 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'mode': 'stack', 'name': ex.name, 'message': ex.message, - 'url': document.location.href, - 'stack': stack, - 'useragent': navigator.userAgent + 'stack': stack }; } /** * Computes stack trace information from the stacktrace property. - * Opera 10 uses this property. + * Opera 10+ uses this property. * @param {Error} ex - * @return {?Object.} Stack trace information. + * @return {?TraceKit.StackTrace} Stack trace information. + * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromStacktraceProp(ex) { // Access and store the stacktrace property before doing ANYTHING // else to it because Opera is not very good at providing it // reliably in other circumstances. var stacktrace = ex.stacktrace; + if (!stacktrace) { + return; + } - var testRE = / line (\d+), column (\d+) in (?:]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i, + var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i, + opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i, lines = stacktrace.split('\n'), stack = [], parts; - for (var i = 0, j = lines.length; i < j; i += 2) { - if ((parts = testRE.exec(lines[i]))) { - var element = { + for (var line = 0; line < lines.length; line += 2) { + var element = null; + if ((parts = opera10Regex.exec(lines[line]))) { + element = { + 'url': parts[2], + 'line': +parts[1], + 'column': null, + 'func': parts[3], + 'args':[] + }; + } else if ((parts = opera11Regex.exec(lines[line]))) { + element = { + 'url': parts[6], 'line': +parts[1], 'column': +parts[2], 'func': parts[3] || parts[4], - 'args': parts[5] ? parts[5].split(',') : [], - 'url': parts[6] + 'args': parts[5] ? parts[5].split(',') : [] }; + } + if (element) { if (!element.func && element.line) { element.func = guessFunctionName(element.url, element.line); } @@ -744,7 +828,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { } if (!element.context) { - element.context = [lines[i + 1]]; + element.context = [lines[line + 1]]; } stack.push(element); @@ -759,9 +843,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'mode': 'stacktrace', 'name': ex.name, 'message': ex.message, - 'url': document.location.href, - 'stack': stack, - 'useragent': navigator.userAgent + 'stack': stack }; } @@ -772,9 +854,11 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { * Opera 9 and earlier use this method if the option to show stack * traces is turned on in opera:config. * @param {Error} ex - * @return {?Object.} Stack information. + * @return {?TraceKit.StackTrace} Stack information. + * @memberof TraceKit.computeStackTrace */ function computeStackTraceFromOperaMultiLineMessage(ex) { + // TODO: Clean this function up // Opera includes a stack trace into the exception message. An example is: // // Statement on line 3: Undefined variable: undefinedFunc @@ -794,40 +878,42 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return null; } - var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i, - lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?)\S+)(?:: in function (\S+))?\s*$/i, + var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, + lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, lineRE3 = /^\s*Line (\d+) of function script\s*$/i, stack = [], - scripts = document.getElementsByTagName('script'), + scripts = (window && window.document && window.document.getElementsByTagName('script')), inlineScriptBlocks = [], - parts, - i, - len, - source; + parts; - for (i in scripts) { - if (_has(scripts, i) && !scripts[i].src) { - inlineScriptBlocks.push(scripts[i]); + for (var s in scripts) { + if (_has(scripts, s) && !scripts[s].src) { + inlineScriptBlocks.push(scripts[s]); } } - for (i = 2, len = lines.length; i < len; i += 2) { + for (var line = 2; line < lines.length; line += 2) { var item = null; - if ((parts = lineRE1.exec(lines[i]))) { + if ((parts = lineRE1.exec(lines[line]))) { item = { 'url': parts[2], 'func': parts[3], - 'line': +parts[1] + 'args': [], + 'line': +parts[1], + 'column': null }; - } else if ((parts = lineRE2.exec(lines[i]))) { + } else if ((parts = lineRE2.exec(lines[line]))) { item = { 'url': parts[3], - 'func': parts[4] + 'func': parts[4], + 'args': [], + 'line': +parts[1], + 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number. }; var relativeLine = (+parts[1]); // relative to the start of the