1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/708d0ccee63f/ changeset: 708d0ccee63f user: james_taylor date: 2013-02-02 23:56:55 summary: Support for using Sentry for error logging. If sentry_dsn is s= et in universe_wsgi.ini, then: - A middleware will be installed that send thrown exceptions to sentry - All logging messages of level WARN and above will be sent to sentry - All pages extending base/base_panels will have javascript errors captured and sent to sentry affected #: 11 files diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 eggs.ini --- a/eggs.ini +++ b/eggs.ini @@ -66,6 +66,7 @@ wchartype =3D 0.1 Whoosh =3D 0.3.18 ; fluent_logger =3D 0.3.3 +raven =3D 3.1.8 =20 ; extra version information [tags] diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -2,7 +2,7 @@ Universe configuration builder. """ =20 -import sys, os, tempfile +import sys, os, tempfile, re import logging, logging.config import ConfigParser from datetime import timedelta @@ -261,11 +261,24 @@ self.api_folders =3D string_as_bool( kwargs.get( 'api_folders', Fa= lse ) ) # This is for testing new library browsing capabilities. self.new_lib_browse =3D string_as_bool( kwargs.get( 'new_lib_brows= e', False ) ) + # Error logging with sentry + self.sentry_dsn =3D kwargs.get( 'sentry_dsn', None ) # Logging with fluentd self.fluent_log =3D string_as_bool( kwargs.get( 'fluent_log', Fals= e ) ) self.fluent_host =3D kwargs.get( 'fluent_host', 'localhost' ) self.fluent_port =3D int( kwargs.get( 'fluent_port', 24224 ) ) =20 + @property + def sentry_dsn_public( self ): + """ + Sentry URL with private key removed for use in client side scripts= ,=20 + sentry server will need to be configured to accept events + """ + if self.sentry_dsn: + return re.sub( r"^([^:/?#]+:)?//(\w+):(\w+)", r"\1//\2", self.= sentry_dsn ) + else: + return None + def __read_tool_job_config( self, global_conf_parser, section, key ): try: tool_runners_config =3D global_conf_parser.items( section ) @@ -387,8 +400,7 @@ =20 def configure_logging( config ): """ - Allow some basic logging configuration to be read from the cherrpy - config. + Allow some basic logging configuration to be read from ini file. """ # PasteScript will have already configured the logger if the appropria= te # sections were found in the config file, so we do nothing if the @@ -420,3 +432,10 @@ # Hook everything up handler.setFormatter( formatter ) root.addHandler( handler ) + # If sentry is configured, also log to it + if config.sentry_dsn: + pkg_resources.require( "raven" ) + from raven.handlers.logging import SentryHandler + sentry_handler =3D SentryHandler( config.sentry_dsn ) + sentry_handler.setLevel( logging.WARN ) + root.addHandler( sentry_handler ) diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 lib/galaxy/web/framework/base.py --- a/lib/galaxy/web/framework/base.py +++ b/lib/galaxy/web/framework/base.py @@ -9,7 +9,6 @@ import sys import tarfile import threading -import uuid =20 from Cookie import SimpleCookie =20 @@ -70,9 +69,6 @@ self.mapper.explicit =3D False self.api_mapper =3D routes.Mapper() self.transaction_factory =3D DefaultWebTransaction - # Each request will have a unique id. Since we are assuming - # a threaded model for the moment we can store that here - self.request_id =3D threading.local() # Set if trace logging is enabled self.trace_logger =3D None def add_ui_controller( self, controller_name, controller ): @@ -124,7 +120,7 @@ and calls it. """ # Immediately create request_id which we will use for logging - self.request_id =3D request_id =3D uuid.uuid1().hex + request_id =3D environ.get( 'request_id', 'unknown' ) if self.trace_logger: self.trace_logger.context_set( "request_id", request_id ) self.trace( message=3D"Starting request" ) @@ -136,6 +132,8 @@ self.trace_logger.context_remove( "request_id" ) =20 def handle_request( self, environ, start_response ): + # Grab the request_id (should have been set by middleware) + request_id =3D environ.get( 'request_id', 'unknown' ) # Map url using routes path_info =3D environ.get( 'PATH_INFO', '' ) map =3D self.mapper.match( path_info, environ ) @@ -157,6 +155,7 @@ rc.environ =3D environ # Setup the transaction trans =3D self.transaction_factory( environ ) + trans.request_id =3D request_id rc.redirect =3D trans.response.send_redirect # Get the controller class controller_name =3D map.pop( 'controller', None ) diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 lib/galaxy/webapps/galaxy/buildapp.py --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -240,6 +240,14 @@ from paste import recursive app =3D recursive.RecursiveMiddleware( app, conf ) log.debug( "Enabling 'recursive' middleware" ) + # If sentry logging is enabled, log here before propogating up to + # the error middleware + sentry_url =3D conf.get( 'sentry_url', None ) + if sentry_url: + pkg_resources.require( "raven") + from raven import Client + from raven.middleware import Sentry + app =3D Sentry( app, Client( sentry_url ) ) # Various debug middleware that can only be turned on if the debug # flag is set, either because they are insecure or greatly hurt # performance @@ -254,12 +262,6 @@ from paste.debug import profile app =3D profile.ProfileMiddleware( app, conf ) log.debug( "Enabling 'profile' middleware" ) - # Middleware that intercepts print statements and shows them on the - # returned page - if asbool( conf.get( 'use_printdebug', True ) ): - from paste.debug import prints - app =3D prints.PrintDebugMiddleware( app, conf ) - log.debug( "Enabling 'print debug' middleware" ) if debug and asbool( conf.get( 'use_interactive', False ) ): # Interactive exception debugging, scary dangerous if publicly # accessible, if not enabled we'll use the regular error printing @@ -288,6 +290,10 @@ from galaxy.web.framework.middleware.xforwardedhost import XForwardedH= ostMiddleware app =3D XForwardedHostMiddleware( app ) log.debug( "Enabling 'x-forwarded-host' middleware" ) + # Request ID middleware + from galaxy.web.framework.middleware.request_id import RequestIDMiddle= ware + app =3D RequestIDMiddleware( app ) + log.debug( "Enabling 'Request ID' middleware" ) return app =20 def wrap_in_static( app, global_conf, **local_conf ): diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 static/scripts/libs/raven.js --- /dev/null +++ b/static/scripts/libs/raven.js @@ -0,0 +1,438 @@ +(function(){ + +// Raven.js +// +// Originally based on the Arecibo JavaScript client. +// +// Requires: +// * TraceKit (included in the full and minified distribution files) + +'use strict'; + +// First, check for JSON support +// If there is no JSON, we no-op the core features of Raven +// since JSON is required to encode the payload +var _Raven =3D window.Raven, + hasJSON =3D !isUndefined(window.JSON), + globalServer, + globalUser, + globalKey, + globalProject, + globalOptions =3D { + logger: 'javascript', + ignoreErrors: [], + ignoreUrls: [] + }; + +var TK =3D TraceKit.noConflict(); + +// Disable Tracekit's remote fetching by default +TK.remoteFetching =3D false; + +/* + * The core Raven object + */ +var Raven =3D { + VERSION: '@VERSION', + + /* + * Raven.noConflict() + * + * Allow multiple versions of Raven to be installed. + */ + noConflict: function() { + window.Raven =3D _Raven; + return Raven; + }, + + /* + * Raven.config() + * + * Configure raven with a DSN and extra options + */ + config: function(dsn, options) { + var uri =3D parseUri(dsn), + lastSlash =3D uri.path.lastIndexOf('/'), + path =3D uri.path.substr(1, lastSlash); + + // merge in options + if (options) { + each(options, function(key, value){ + globalOptions[key] =3D value; + }); + } + + // "Script error." is hard coded into browsers for errors that it = can't read. + // this is the result of a script being pulled in from an external= domain and CORS. + globalOptions.ignoreErrors.push('Script error.'); + + globalKey =3D uri.user; + globalProject =3D ~~uri.path.substr(lastSlash + 1); + + // assemble the endpoint from the uri pieces + globalServer =3D uri.protocol + '://' + uri.host + + (uri.port ? ':' + uri.port : '') + + '/' + path + 'api/' + globalProject + '/store/'; + + if (globalOptions.fetchContext) { + TK.remoteFetching =3D true; + } + + // return for chaining + return Raven; + }, + + /* + * Raven.install() + * + * Installs a global window.onerror error handler + * to capture and report uncaught exceptions. + */ + install: function() { + if (!isSetup()) return; + + TK.report.subscribe(handleStackInfo); + + return Raven; + }, + + /* + * Raven.context() + * + * Wrap code within a context so Raven can capture errors + * reliably across domains that is executed immediately. + */ + context: function(options, func, args) { + if (isFunction(options)) { + args =3D func; + func =3D options; + options =3D undefined; + } + + Raven.wrap(options, func).apply(this, args); + }, + + /* Raven.wrap() + * + * Wrap code within a context and returns back a new function to be ex= ecuted + */ + wrap: function(options, func) { + // options is optional + if (isFunction(options)) { + func =3D options; + options =3D undefined; + } + + return function() { + try { + func.apply(this, arguments); + } catch(e) { + Raven.captureException(e, options); + } + }; + }, + + /* + * Raven.uninstall() + * + * Uninstalls the global error handler. + */ + uninstall: function() { + TK.report.unsubscribe(handleStackInfo); + + return Raven; + }, + + /* + * Raven.captureException() + * + * Manually capture an exception and send it over to Sentry + */ + captureException: function(ex, options) { + // TraceKit.report will re-raise any exception passed to it, + // which means you have to wrap it in try/catch. Instead, we + // can wrap it here and only re-raise if TraceKit.report + // raises an exception different from the one we asked to + // report on. + try { + TK.report(ex, options); + } catch(ex1) { + if(ex !=3D=3D ex1) { + throw ex1; + } + } + + return Raven; + }, + + /* + * Raven.captureMessage() + * + * Manually send a message to Sentry + */ + captureMessage: function(msg, options) { + // Fire away! + send( + arrayMerge({ + message: msg + }, options) + ); + + return Raven; + }, + + /* + * Raven.setUser() + * + * Set/clear a user to be sent along with the payload. + */ + setUser: function(user) { + globalUser =3D user; + + return Raven; + } +}; + +var uriKeys =3D 'source protocol authority userInfo user password host por= t relative path directory file query anchor'.split(' '), + uriPattern =3D /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:((= [^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.= [^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + +/**** Private functions ****/ +function parseUri(str) { + var m =3D uriPattern.exec(str), + uri =3D {}, + i =3D 14; + + while (i--) uri[uriKeys[i]] =3D m[i] || ''; + + return uri; +} + +function isUndefined(what) { + return typeof what =3D=3D=3D 'undefined'; +} + +function isFunction(what) { + return typeof what =3D=3D=3D 'function'; +} + +function each(obj, callback) { + var i, j; + + if (obj.length =3D=3D=3D undefined) { + for (i in obj) { + if (obj.hasOwnProperty(i)) { + callback.call(null, i, obj[i]); + } + } + } else { + for (i =3D 0, j =3D obj.length; i < j; i++) { + callback.call(null, i, obj[i]); + } + } +} + +var cachedAuth; + +function getAuthQueryString() { + if (cachedAuth) return cachedAuth; + + var qs =3D [ + 'sentry_version=3D2.0', + 'sentry_client=3Draven-js/' + Raven.VERSION + ]; + if (globalKey) { + qs.push('sentry_key=3D' + globalKey); + } + + cachedAuth =3D '?' + qs.join('&'); + return cachedAuth; +} + +function handleStackInfo(stackInfo, options) { + var frames =3D [], i =3D 0, j =3D stackInfo.stack && stackInfo.stack.l= ength || 0, frame; + + for (; i < j; i++) { + frame =3D normalizeFrame(stackInfo.stack[i]); + if (frame) { + frames.push(frame); + } + } + + processException( + stackInfo.name, + stackInfo.message, + stackInfo.url, + stackInfo.lineno, + frames, + options + ); +} + +function normalizeFrame(frame) { + if (!frame.url) return; + + // normalize the frames data + var normalized =3D { + filename: frame.url, + lineno: frame.line, + colno: frame.column, + 'function': frame.func || '?' + }, context =3D extractContextFromFrame(frame); + + if (context) { + var i =3D 3, keys =3D ['pre_context', 'context_line', 'post_contex= t']; + while (i--) normalized[keys[i]] =3D context[i]; + } + + normalized.in_app =3D !/(Raven|TraceKit)\./.test(normalized['function'= ]); + + return normalized; +} + +function extractContextFromFrame(frame) { + // immediately check if we should even attempt to parse a context + if (!frame.context || !globalOptions.fetchContext) return; + + var context =3D frame.context, + pivot =3D ~~(context.length / 2), + i =3D context.length, isMinified =3D false; + + while (i--) { + // We're making a guess to see if the source is minified or not. + // To do that, we make the assumption if *any* of the lines passed + // in are greater than 300 characters long, we bail. + // Sentry will see that there isn't a context + if (context[i].length > 300) { + isMinified =3D true; + break; + } + } + + if (isMinified) { + // The source is minified and we don't know which column. Fuck it. + if (isUndefined(frame.column)) return; + + // If the source is minified and has a frame column + // we take a chunk of the offending line to hopefully shed some li= ght + return [ + [], // no pre_context + context[pivot].substr(frame.column, 50), // grab 50 characters= , starting at the offending column + [] // no post_context + ]; + } + + return [ + context.slice(0, pivot), // pre_context + context[pivot], // context_line + context.slice(pivot + 1) // post_context + ]; +} + +function processException(type, message, fileurl, lineno, frames, options)= { + var stacktrace, label, i; + + // IE8 really doesn't have Array.prototype.indexOf + // Filter out a message that matches our ignore list + i =3D globalOptions.ignoreErrors.length; + while (i--) { + if (message =3D=3D=3D globalOptions.ignoreErrors[i]) { + return; + } + } + + if (frames && frames.length) { + stacktrace =3D {frames: frames}; + fileurl =3D fileurl || frames[0].filename; + } else if (fileurl) { + stacktrace =3D { + frames: [{ + filename: fileurl, + lineno: lineno + }] + }; + } + + i =3D globalOptions.ignoreUrls.length; + while (i--) { + if (globalOptions.ignoreUrls[i].test(fileurl)) { + return; + } + } + + label =3D lineno ? message + ' at ' + lineno : message; + + // Fire away! + send( + arrayMerge({ + 'sentry.interfaces.Exception': { + type: type, + value: message + }, + 'sentry.interfaces.Stacktrace': stacktrace, + culprit: fileurl, + message: label + }, options) + ); +} + +function arrayMerge(arr1, arr2) { + if (!arr2) { + return arr1; + } + each(arr2, function(key, value){ + arr1[key] =3D value; + }); + return arr1; +} + +function getHttpData() { + var http =3D { + url: window.location.href, + headers: { + 'User-Agent': navigator.userAgent + } + }; + + if (window.document.referrer) { + http.headers.Referer =3D window.document.referrer; + } + + return http; +} + +function send(data) { + if (!isSetup()) return; + + data =3D arrayMerge({ + project: globalProject, + logger: globalOptions.logger, + site: globalOptions.site, + platform: 'javascript', + 'sentry.interfaces.Http': getHttpData() + }, data ); + + if (globalUser) data['sentry.interfaces.User'] =3D globalUser; + + if (isFunction(globalOptions.dataCallback)) { + data =3D globalOptions.dataCallback(data); + } + + makeRequest(data); +} + +function makeRequest(data) { + new Image().src =3D globalServer + getAuthQueryString() + '&sentry_dat= a=3D' + encodeURIComponent(JSON.stringify(data)); +} + +function isSetup() { + if (!hasJSON) return false; // needs JSON support + if (!globalServer) { + console.error("Error: Raven has not been configured."); + return false; + } + return true; +} + +window.Raven =3D Raven; + +})(); diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 static/scripts/libs/tracekit.js --- /dev/null +++ b/static/scripts/libs/tracekit.js @@ -0,0 +1,1210 @@ +/* + TraceKit - Cross brower stack traces - github.com/occ/TraceKit + MIT license +*/ + +;(function(window, undefined) { + + +var TraceKit =3D {}; +var _oldTraceKit =3D window.TraceKit; + +/** + * TraceKit.noConflict: Export TraceKit out to another variable + * Example: var TK =3D TraceKit.noConflict() + */ +TraceKit.noConflict =3D function noConflict() { + window.TraceKit =3D _oldTraceKit; + return TraceKit; +}; + +/** + * TraceKit._has, a better form of hasOwnProperty + * Example: TraceKit._has(MainHostObject, property) =3D=3D=3D true/false + * + * @param {Object} host object to check property + * @param {string} key to check + */ +TraceKit._has =3D function _has(object, key) { + return Object.prototype.hasOwnProperty.call(object, key); +}; + +/** + * TraceKit.report: cross-browser processing of unhandled exceptions + * + * Syntax: + * 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 + * + * In theory, TraceKit should work on all of the following versions: + * - IE5.5+ (only 8.0 tested) + * - Firefox 0.9+ (only 3.5+ tested) + * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require + * Exceptions Have Stacktrace to be enabled in opera:config) + * - Safari 3+ (only 4+ tested) + * - Chrome 1+ (only 5+ tested) + * - Konqueror 3.5+ (untested) + * + * Requires TraceKit.computeStackTrace. + * + * Tries to catch all unhandled exceptions and report them to the + * subscribed handlers. Please note that TraceKit.report will rethrow the + * exception. This is REQUIRED in order to get a useful stack trace in IE. + * 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 + * TraceKit.computeStackTrace docs. + */ +TraceKit.report =3D (function reportModuleWrapper() { + var handlers =3D [], + lastException =3D null, + lastExceptionStack =3D null; + + /** + * Add a crash handler. + * @param {Function} handler + */ + function subscribe(handler) { + handlers.push(handler); + } + + /** + * Remove a crash handler. + * @param {Function} handler + */ + function unsubscribe(handler) { + for (var i =3D handlers.length - 1; i >=3D 0; --i) { + if (handlers[i] =3D=3D=3D handler) { + handlers.splice(i, 1); + } + } + } + + /** + * Dispatch stack information to all handlers. + * @param {Object.<string, *>} stack + */ + function notifyHandlers(stack, windowError) { + var exception =3D null; + if (windowError && !TraceKit.collectWindowErrors) { + return; + } + for (var i in handlers) { + if (TraceKit._has(handlers, i)) { + try { + handlers[i].apply(null, [stack].concat(Array.prototype= .slice.call(arguments, 2))); + } catch (inner) { + exception =3D inner; + } + } + } + + if (exception) { + throw exception; + } + } + + var _oldOnerrorHandler =3D window.onerror; + + /** + * Ensures all global unhandled exceptions are recorded. + * 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. + */ + window.onerror =3D function traceKitWindowOnError(message, url, lineNo= ) { + var stack =3D null; + + if (lastExceptionStack) { + TraceKit.computeStackTrace.augmentStackTraceWithInitialElement= (lastExceptionStack, url, lineNo, message); + stack =3D lastExceptionStack; + lastExceptionStack =3D null; + lastException =3D null; + } else { + var location =3D { + 'url': url, + 'line': lineNo + }; + location.func =3D TraceKit.computeStackTrace.guessFunctionName= (location.url, location.line); + location.context =3D TraceKit.computeStackTrace.gatherContext(= location.url, location.line); + stack =3D { + 'mode': 'onerror', + 'message': message, + 'url': document.location.href, + 'stack': [location], + 'useragent': navigator.userAgent + }; + } + + notifyHandlers(stack, 'from window.onerror'); + + if (_oldOnerrorHandler) { + return _oldOnerrorHandler.apply(this, arguments); + } + + return false; + }; + + /** + * Reports an unhandled Error to TraceKit. + * @param {Error} ex + */ + function report(ex) { + var args =3D Array.prototype.slice.call(arguments, 1); + if (lastExceptionStack) { + if (lastException =3D=3D=3D ex) { + return; // already caught by an inner catch block, ignore + } else { + var s =3D lastExceptionStack; + lastExceptionStack =3D null; + lastException =3D null; + notifyHandlers.apply(null, [s, null].concat(args)); + } + } + + var stack =3D TraceKit.computeStackTrace(ex); + lastExceptionStack =3D stack; + lastException =3D ex; + + // If the stack trace is incomplete, wait for 2 seconds for + // slow slow IE to see if onerror occurs or not before reporting + // this exception; otherwise, we will end up with an incomplete + // stack trace + window.setTimeout(function () { + if (lastException =3D=3D=3D ex) { + lastExceptionStack =3D null; + lastException =3D null; + notifyHandlers.apply(null, [stack, null].concat(args)); + } + }, (stack.incomplete ? 2000 : 0)); + + throw ex; // re-throw to propagate to the top level (and cause win= dow.onerror) + } + + report.subscribe =3D subscribe; + report.unsubscribe =3D unsubscribe; + return report; +}()); + +/** + * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript + * + * Syntax: + * s =3D TraceKit.computeStackTrace.ofCaller([depth]) + * s =3D TraceKit.computeStackTrace(exception) // consider using TraceKi= t.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 eleme= nt 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 + * number on top frame + * - Opera 10: full stack trace with line and column numbers + * - Opera 9-: full stack trace with line numbers + * - Chrome: full stack trace with line and column numbers + * - Safari: line and column number for the topmost stacktrace element + * only + * - IE: no line numbers whatsoever + * + * Tries to guess names of anonymous functions by looking for assignments + * in the source code. In IE and Safari, we have to guess source file names + * by searching for function bodies inside all page scripts. This will not + * work for scripts that are loaded cross-domain. + * Here be dragons: some function names may be guessed incorrectly, and + * duplicate functions may be mismatched. + * + * TraceKit.computeStackTrace should only be used for tracing purposes. + * Logging of unhandled exceptions should be done with TraceKit.report, + * which builds on top of TraceKit.computeStackTrace and provides better + * IE support by utilizing the window.onerror event to retrieve information + * about the top of the stack. + * + * Note: In IE and Safari, no stack trace is recorded on the Error object, + * so computeStackTrace instead walks its *own* chain of callers. + * This means that: + * * in Safari, some methods may be missing from the stack trace; + * * in IE, the topmost function in the stack trace will always be the + * caller of computeStackTrace. + * + * This is okay for tracing (because you are likely to be calling + * computeStackTrace from the function you want to be the topmost element + * of the stack trace anyway), but not okay for logging unhandled + * exceptions (because your catch block will likely be far away from the + * inner function that actually caused the exception). + * + * Tracing example: + * function trace(message) { + * var stackInfo =3D TraceKit.computeStackTrace.ofCaller(); + * var data =3D message + "\n"; + * for(var i in stackInfo.stack) { + * var item =3D stackInfo.stack[i]; + * data +=3D (item.func || '[anonymous]') + "() in " + item.ur= l + ":" + (item.line || '0') + "\n"; + * } + * if (window.console) + * console.info(data); + * else + * alert(data); + * } + */ +TraceKit.computeStackTrace =3D (function computeStackTraceWrapper() { + var debug =3D false, + sourceCache =3D {}; + + /** + * Attempts to retrieve source code via XMLHttpRequest, which is used + * to look up anonymous function names. + * @param {string} url URL of source code. + * @return {string} Source contents. + */ + function loadSource(url) { + if (!TraceKit.remoteFetching) { //Only attempt request if remoteFe= tching is on. + return ''; + } + try { + var XMLHttpRequestWrapper; + + if (typeof (XMLHttpRequest) =3D=3D=3D 'undefined') { // IE 5.x= -6.x: + XMLHttpRequestWrapper =3D function IEXMLHttpRequestSub() { + try { + return new ActiveXObject('Msxml2.XMLHTTP.6.0'); + } catch (e) {} + try { + return new ActiveXObject('Msxml2.XMLHTTP.3.0'); + } catch (e) {} + try { + return new ActiveXObject('Msxml2.XMLHTTP'); + } catch (e) {} + try { + return new ActiveXObject('Microsoft.XMLHTTP'); + } catch (e) {} + throw new Error('No XHR.'); + }; + } else { + XMLHttpRequestWrapper =3D XMLHttpRequest; + } + =20 + var request =3D new XMLHttpRequestWrapper(); + request.open('GET', url, false); + request.send(''); + return request.responseText; + } catch (e) { + return ''; + } + } + + /** + * Retrieves source code from the source code cache. + * @param {string} url URL of source code. + * @return {Array.<string>} Source contents. + */ + function getSource(url) { + if (!TraceKit._has(sourceCache, url)) { + // URL needs to be able to fetched within the acceptable domai= n. Otherwise, + // cross-domain errors will be triggered. + var source; + if (url.indexOf(document.domain) !=3D=3D -1) { + source =3D loadSource(url); + } else { + source =3D []; + } + sourceCache[url] =3D source.length ? source.split('\n') : []; + } + + return sourceCache[url]; + } + + /** + * Tries to use an externally loaded copy of source code to determine + * the name of a function by looking at the name of the variable it was + * assigned to, if any. + * @param {string} url URL of source code. + * @param {(string|number)} lineNo Line number in source code. + * @return {string} The function name, if discoverable. + */ + function guessFunctionName(url, lineNo) { + var reFunctionArgNames =3D /function ([^(]*)\(([^)]*)\)/, + reGuessFunction =3D /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=3D]\s*(fu= nction|eval|new Function)/, + line =3D '', + maxLines =3D 10, + source =3D getSource(url), + m; + + if (!source.length) { + return '?'; + } + + // Walk backwards from the first line in the function until we fin= d the line which + // matches the pattern above, which is the function definition + for (var i =3D 0; i < maxLines; ++i) { + line =3D source[lineNo - i] + line; + + if (line !=3D=3D undefined) { + if ((m =3D reGuessFunction.exec(line))) { + return m[1]; + } else if ((m =3D reFunctionArgNames.exec(line))) { + return m[1]; + } + } + } + + return '?'; + } + + /** + * Retrieves the surrounding lines from where an exception occurred. + * @param {string} url URL of source code. + * @param {(string|number)} line Line number in source code to centre + * around for context. + * @return {?Array.<string>} Lines of source code. + */ + function gatherContext(url, line) { + var source =3D getSource(url); + + if (!source.length) { + return null; + } + + var context =3D [], + // linesBefore & linesAfter are inclusive with the offending l= ine. + // if linesOfContext is even, there will be one extra line + // *before* the offending line. + linesBefore =3D Math.floor(TraceKit.linesOfContext / 2), + // Add one extra line if linesOfContext is odd + linesAfter =3D linesBefore + (TraceKit.linesOfContext % 2), + start =3D Math.max(0, line - linesBefore - 1), + end =3D Math.min(source.length, line + linesAfter - 1); + + line -=3D 1; // convert to 0-based index + + for (var i =3D start; i < end; ++i) { + if (typeof (source[i]) !=3D=3D 'undefined') { + context.push(source[i]); + } + } + + return context.length > 0 ? context : null; + } + + /** + * Escapes special characters, except for whitespace, in a string to be + * used inside a regular expression as a string literal. + * @param {string} text The string. + * @return {string} The escaped string literal. + */ + function escapeRegExp(text) { + return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&'); + } + + /** + * Escapes special characters in a string to be used inside a regular + * expression as a string literal. Also ensures that HTML entities will + * be matched the same as their literal friends. + * @param {string} body The string. + * @return {string} The escaped string. + */ + function escapeCodeAsRegExpForMatchingInsideHTML(body) { + return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', = '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').repl= ace(/\s+/g, '\\s+'); + } + + /** + * Determines where a code fragment occurs in the source code. + * @param {RegExp} re The function definition. + * @param {Array.<string>} urls A list of URLs to search. + * @return {?Object.<string, (string|number)>} An object containing + * the url, line, and column number of the defined function. + */ + function findSourceInUrls(re, urls) { + var source, m; + for (var i =3D 0, j =3D urls.length; i < j; ++i) { + // console.log('searching', urls[i]); + if ((source =3D getSource(urls[i])).length) { + source =3D source.join('\n'); + if ((m =3D re.exec(source))) { + // console.log('Found function in ' + urls[i]); + + return { + 'url': urls[i], + 'line': source.substring(0, m.index).split('\n').l= ength, + 'column': m.index - source.lastIndexOf('\n', m.ind= ex) - 1 + }; + } + } + } + + // console.log('no match'); + + return null; + } + + /** + * Determines at which column a code fragment occurs on a line of the + * source code. + * @param {string} fragment The code fragment. + * @param {string} url The URL to search. + * @param {(string|number)} line The line number to examine. + * @return {?number} The column number. + */ + function findSourceInLine(fragment, url, line) { + var source =3D getSource(url), + re =3D new RegExp('\\b' + escapeRegExp(fragment) + '\\b'), + m; + + line -=3D 1; + + if (source && source.length > line && (m =3D re.exec(source[line])= )) { + return m.index; + } + + return null; + } + + /** + * Determines where a function was defined within the source code. + * @param {(Function|string)} func A function reference or serialized + * function definition. + * @return {?Object.<string, (string|number)>} An object containing + * the url, line, and column number of the defined function. + */ + function findSourceByFunctionBody(func) { + var urls =3D [window.location.href], + scripts =3D document.getElementsByTagName('script'), + body, + code =3D '' + func, + codeRE =3D /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*= (\S[\s\S]*\S)\s*\}\s*$/, + eventRE =3D /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]= *\S)\s*\}\s*$/, + re, + parts, + result; + + for (var i =3D 0; i < scripts.length; ++i) { + var script =3D scripts[i]; + if (script.src) { + urls.push(script.src); + } + } + + if (!(parts =3D codeRE.exec(code))) { + re =3D new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+')); + } + + // not sure if this is really necessary, but I don=E2=80=99t have = a test + // corpus large enough to confirm that and it was in the original. + else { + var name =3D parts[1] ? '\\s+' + parts[1] : '', + args =3D parts[2].split(',').join('\\s*,\\s*'); + + body =3D escapeRegExp(parts[3]).replace(/;$/, ';?'); // semico= lon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+'); + re =3D new RegExp('function' + name + '\\s*\\(\\s*' + args + '= \\s*\\)\\s*{\\s*' + body + '\\s*}'); + } + + // look for a normal function definition + if ((result =3D findSourceInUrls(re, urls))) { + return result; + } + + // look for an old-school event handler function + if ((parts =3D eventRE.exec(code))) { + var event =3D parts[1]; + body =3D escapeCodeAsRegExpForMatchingInsideHTML(parts[2]); + + // look for a function defined in HTML as an onXXX handler + re =3D new RegExp('on' + event + '=3D[\\\'"]\\s*' + body + '\\= s*[\\\'"]', 'i'); + + if ((result =3D findSourceInUrls(re, urls[0]))) { + return result; + } + + // look for ??? + re =3D new RegExp(body); + + if ((result =3D findSourceInUrls(re, urls))) { + return result; + } + } + + return null; + } + + // Contents of Exception in various browsers. + // + // SAFARI: + // ex.message =3D Can't find variable: qq + // ex.line =3D 59 + // ex.sourceId =3D 580238192 + // ex.sourceURL =3D http://... + // ex.expressionBeginOffset =3D 96 + // ex.expressionCaretOffset =3D 98 + // ex.expressionEndOffset =3D 98 + // ex.name =3D ReferenceError + // + // FIREFOX: + // ex.message =3D qq is not defined + // ex.fileName =3D http://... + // ex.lineNumber =3D 59 + // ex.stack =3D ...stack trace... (see the example below) + // ex.name =3D ReferenceError + // + // CHROME: + // ex.message =3D qq is not defined + // ex.name =3D ReferenceError + // ex.type =3D not_defined + // ex.arguments =3D ['aa'] + // ex.stack =3D ...stack trace... + // + // INTERNET EXPLORER: + // ex.message =3D ... + // ex.name =3D ReferenceError + // + // OPERA: + // ex.message =3D ...message... (see the example below) + // ex.name =3D ReferenceError + // ex.opera#sourceloc =3D 11 (pretty much useless, duplicates the inf= o in ex.message) + // ex.stacktrace =3D n/a; see 'opera:config#UserPrefs|Exceptions Have = Stacktrace' + + /** + * Computes stack trace information from the stack property. + * Chrome and Gecko use this property. + * @param {Error} ex + * @return {?Object.<string, *>} Stack trace information. + */ + function computeStackTraceFromStackProp(ex) { + if (!ex.stack) { + return null; + } + + var chrome =3D /^\s*at (?:((?:\[object object\])?\S+) )?\(?((?:fil= e|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + gecko =3D /^\s*(\S*)(?:\((.*?)\))?@((?:file|http|https).*?):(\= d+)(?::(\d+))?\s*$/i, + lines =3D ex.stack.split('\n'), + stack =3D [], + parts, + element, + reference =3D /^(.*) is undefined$/.exec(ex.message); + + for (var i =3D 0, j =3D lines.length; i < j; ++i) { + if ((parts =3D gecko.exec(lines[i]))) { + element =3D { + 'url': parts[3], + 'func': parts[1] || '?', + 'args': parts[2] ? parts[2].split(',') : '', + 'line': +parts[4], + 'column': parts[5] ? +parts[5] : null + }; + } else if ((parts =3D chrome.exec(lines[i]))) { + element =3D { + 'url': parts[2], + 'func': parts[1] || '?', + 'line': +parts[3], + 'column': parts[4] ? +parts[4] : null + }; + } else { + continue; + } + + if (!element.func && element.line) { + element.func =3D guessFunctionName(element.url, element.li= ne); + } + + if (element.line) { + element.context =3D gatherContext(element.url, element.lin= e); + } + + stack.push(element); + } + + if (stack[0] && stack[0].line && !stack[0].column && reference) { + stack[0].column =3D findSourceInLine(reference[1], stack[0].ur= l, stack[0].line); + } + + if (!stack.length) { + return null; + } + + return { + 'mode': 'stack', + 'name': ex.name, + 'message': ex.message, + 'url': document.location.href, + 'stack': stack, + 'useragent': navigator.userAgent + }; + } + + /** + * Computes stack trace information from the stacktrace property. + * Opera 10 uses this property. + * @param {Error} ex + * @return {?Object.<string, *>} Stack trace information. + */ + 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 =3D ex.stacktrace; + + var testRE =3D / line (\d+), column (\d+) in (?:<anonymous functio= n: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i, + lines =3D stacktrace.split('\n'), + stack =3D [], + parts; + + for (var i =3D 0, j =3D lines.length; i < j; i +=3D 2) { + if ((parts =3D testRE.exec(lines[i]))) { + var element =3D { + 'line': +parts[1], + 'column': +parts[2], + 'func': parts[3] || parts[4], + 'args': parts[5] ? parts[5].split(',') : [], + 'url': parts[6] + }; + + if (!element.func && element.line) { + element.func =3D guessFunctionName(element.url, elemen= t.line); + } + if (element.line) { + try { + element.context =3D gatherContext(element.url, ele= ment.line); + } catch (exc) {} + } + + if (!element.context) { + element.context =3D [lines[i + 1]]; + } + + stack.push(element); + } + } + + if (!stack.length) { + return null; + } + + return { + 'mode': 'stacktrace', + 'name': ex.name, + 'message': ex.message, + 'url': document.location.href, + 'stack': stack, + 'useragent': navigator.userAgent + }; + } + + /** + * NOT TESTED. + * Computes stack trace information from an error message that includes + * the stack trace. + * 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.<string, *>} Stack information. + */ + function computeStackTraceFromOperaMultiLineMessage(ex) { + // Opera includes a stack trace into the exception message. An exa= mple is: + // + // Statement on line 3: Undefined variable: undefinedFunc + // Backtrace: + // Line 3 of linked script file://localhost/Users/andreyvit/Proj= ects/TraceKit/javascript-client/sample.js: In function zzz + // undefinedFunc(a); + // Line 7 of inline#1 script in file://localhost/Users/andreyvit= /Projects/TraceKit/javascript-client/sample.html: In function yyy + // zzz(x, y, z); + // Line 3 of inline#1 script in file://localhost/Users/andreyvit= /Projects/TraceKit/javascript-client/sample.html: In function xxx + // yyy(a, a, a); + // Line 1 of function script + // try { xxx('hi'); return false; } catch(ex) { TraceKit.repor= t(ex); } + // ... + + var lines =3D ex.message.split('\n'); + if (lines.length < 4) { + return null; + } + + var lineRE1 =3D /^\s*Line (\d+) of linked script ((?:file|http|htt= ps)\S+)(?:: in function (\S+))?\s*$/i, + lineRE2 =3D /^\s*Line (\d+) of inline#(\d+) script in ((?:file= |http|https)\S+)(?:: in function (\S+))?\s*$/i, + lineRE3 =3D /^\s*Line (\d+) of function script\s*$/i, + stack =3D [], + scripts =3D document.getElementsByTagName('script'), + inlineScriptBlocks =3D [], + parts, + i, + len, + source; + + for (i in scripts) { + if (TraceKit._has(scripts, i) && !scripts[i].src) { + inlineScriptBlocks.push(scripts[i]); + } + } + + for (i =3D 2, len =3D lines.length; i < len; i +=3D 2) { + var item =3D null; + if ((parts =3D lineRE1.exec(lines[i]))) { + item =3D { + 'url': parts[2], + 'func': parts[3], + 'line': +parts[1] + }; + } else if ((parts =3D lineRE2.exec(lines[i]))) { + item =3D { + 'url': parts[3], + 'func': parts[4] + }; + var relativeLine =3D (+parts[1]); // relative to the start= of the <SCRIPT> block + var script =3D inlineScriptBlocks[parts[2] - 1]; + if (script) { + source =3D getSource(item.url); + if (source) { + source =3D source.join('\n'); + var pos =3D source.indexOf(script.innerText); + if (pos >=3D 0) { + item.line =3D relativeLine + source.substring(= 0, pos).split('\n').length; + } + } + } + } else if ((parts =3D lineRE3.exec(lines[i]))) { + var url =3D window.location.href.replace(/#.*$/, ''), + line =3D parts[1]; + var re =3D new RegExp(escapeCodeAsRegExpForMatchingInsideH= TML(lines[i + 1])); + source =3D findSourceInUrls(re, [url]); + item =3D { + 'url': url, + 'line': source ? source.line : line, + 'func': '' + }; + } + + if (item) { + if (!item.func) { + item.func =3D guessFunctionName(item.url, item.line); + } + var context =3D gatherContext(item.url, item.line); + var midline =3D (context ? context[Math.floor(context.leng= th / 2)] : null); + if (context && midline.replace(/^\s*/, '') =3D=3D=3D lines= [i + 1].replace(/^\s*/, '')) { + item.context =3D context; + } else { + // if (context) alert("Context mismatch. Correct midli= ne:\n" + lines[i+1] + "\n\nMidline:\n" + midline + "\n\nContext:\n" + conte= xt.join("\n") + "\n\nURL:\n" + item.url); + item.context =3D [lines[i + 1]]; + } + stack.push(item); + } + } + if (!stack.length) { + return null; // could not parse multiline exception message as= Opera stack trace + } + + return { + 'mode': 'multiline', + 'name': ex.name, + 'message': lines[0], + 'url': document.location.href, + 'stack': stack, + 'useragent': navigator.userAgent + }; + } + + /** + * Adds information about the first frame to incomplete stack traces. + * Safari and IE require this to get complete data on the first frame. + * @param {Object.<string, *>} stackInfo Stack trace information from + * one of the compute* methods. + * @param {string} url The URL of the script that caused an error. + * @param {(number|string)} lineNo The line number of the script that + * caused an error. + * @param {string=3D} message The error generated by the browser, which + * hopefully contains the name of the object that caused the error. + * @return {boolean} Whether or not the stack information was + * augmented. + */ + function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, m= essage) { + var initial =3D { + 'url': url, + 'line': lineNo + }; + + if (initial.url && initial.line) { + stackInfo.incomplete =3D false; + + if (!initial.func) { + initial.func =3D guessFunctionName(initial.url, initial.li= ne); + } + + if (!initial.context) { + initial.context =3D gatherContext(initial.url, initial.lin= e); + } + + var reference =3D / '([^']+)' /.exec(message); + if (reference) { + initial.column =3D findSourceInLine(reference[1], initial.= url, initial.line); + } + + if (stackInfo.stack.length > 0) { + if (stackInfo.stack[0].url =3D=3D=3D initial.url) { + if (stackInfo.stack[0].line =3D=3D=3D initial.line) { + return false; // already in stack trace + } else if (!stackInfo.stack[0].line && stackInfo.stack= [0].func =3D=3D=3D initial.func) { + stackInfo.stack[0].line =3D initial.line; + stackInfo.stack[0].context =3D initial.context; + return false; + } + } + } + + stackInfo.stack.unshift(initial); + stackInfo.partial =3D true; + return true; + } else { + stackInfo.incomplete =3D true; + } + + return false; + } + + /** + * Computes stack trace information by walking the arguments.caller + * chain at the time the exception occurred. This will cause earlier + * frames to be missed but is the only way to get any stack trace in + * Safari and IE. The top frame is restored by + * {@link augmentStackTraceWithInitialElement}. + * @param {Error} ex + * @return {?Object.<string, *>} Stack trace information. + */ + function computeStackTraceByWalkingCallerChain(ex, depth) { + var functionName =3D /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-= 9\xA0-\uFFFF]*)?\s*\(/i, + stack =3D [], + funcs =3D {}, + recursion =3D false, + parts, + item, + source; + + for (var curr =3D computeStackTraceByWalkingCallerChain.caller; cu= rr && !recursion; curr =3D curr.caller) { + if (curr =3D=3D=3D computeStackTrace || curr =3D=3D=3D TraceKi= t.report) { + // console.log('skipping internal function'); + continue; + } + + item =3D { + 'url': null, + 'func': '?', + 'line': null, + 'column': null + }; + + if (curr.name) { + item.func =3D curr.name; + } else if ((parts =3D functionName.exec(curr.toString()))) { + item.func =3D parts[1]; + } + + if ((source =3D findSourceByFunctionBody(curr))) { + item.url =3D source.url; + item.line =3D source.line; + + if (item.func =3D=3D=3D '?') { + item.func =3D guessFunctionName(item.url, item.line); + } + + var reference =3D / '([^']+)' /.exec(ex.message || ex.desc= ription); + if (reference) { + item.column =3D findSourceInLine(reference[1], source.= url, source.line); + } + } + + if (funcs['' + curr]) { + recursion =3D true; + }else{ + funcs['' + curr] =3D true; + } + + stack.push(item); + } + + if (depth) { + // console.log('depth is ' + depth); + // console.log('stack is ' + stack.length); + stack.splice(0, depth); + } + + var result =3D { + 'mode': 'callers', + 'name': ex.name, + 'message': ex.message, + 'url': document.location.href, + 'stack': stack, + 'useragent': navigator.userAgent + }; + augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fil= eName, ex.line || ex.lineNumber, ex.message || ex.description); + return result; + } + + /** + * Computes a stack trace for an exception. + * @param {Error} ex + * @param {(string|number)=3D} depth + */ + function computeStackTrace(ex, depth) { + var stack =3D null; + depth =3D (depth =3D=3D null ? 0 : +depth); + + try { + // This must be tried first because Opera 10 *destroys* + // its stacktrace property if you try to access the stack + // property first!! + stack =3D computeStackTraceFromStacktraceProp(ex); + if (stack) { + return stack; + } + } catch (e) { + if (debug) { + throw e; + } + } + + try { + stack =3D computeStackTraceFromStackProp(ex); + if (stack) { + return stack; + } + } catch (e) { + if (debug) { + throw e; + } + } + + try { + stack =3D computeStackTraceFromOperaMultiLineMessage(ex); + if (stack) { + return stack; + } + } catch (e) { + if (debug) { + throw e; + } + } + + try { + stack =3D computeStackTraceByWalkingCallerChain(ex, depth + 1); + if (stack) { + return stack; + } + } catch (e) { + if (debug) { + throw e; + } + } + + return { + 'mode': 'failed' + }; + } + + /** + * Logs a stacktrace starting from the previous call and working down. + * @param {(number|string)=3D} depth How many frames deep to trace. + * @return {Object.<string, *>} Stack trace information. + */ + function computeStackTraceOfCaller(depth) { + depth =3D (depth =3D=3D null ? 0 : +depth) + 1; // "+ 1" because "= ofCaller" should drop one frame + try { + throw new Error(); + } catch (ex) { + return computeStackTrace(ex, depth + 1); + } + + return null; + } + + computeStackTrace.augmentStackTraceWithInitialElement =3D augmentStack= TraceWithInitialElement; + computeStackTrace.guessFunctionName =3D guessFunctionName; + computeStackTrace.gatherContext =3D gatherContext; + computeStackTrace.ofCaller =3D computeStackTraceOfCaller; + + return computeStackTrace; +}()); + +/** + * Extends support for global error handling for asynchronous browser + * functions. Adopted from Closure Library's errorhandler.js + */ +(function extendToAsynchronousCallbacks(w) { + var _helper =3D function _helper(fnName) { + var originalFn =3D w[fnName]; + w[fnName] =3D function traceKitAsyncExtension() { + // Make a copy of the arguments + var args =3D Array.prototype.slice.call(arguments, 0); + var originalCallback =3D args[0]; + if (typeof (originalCallback) =3D=3D=3D 'function') { + args[0] =3D function traceKitArgsZero() { + try { + originalCallback.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } + // IE < 9 doesn't support .call/.apply on setInterval/setTimeo= ut, but it + // also only supports 2 argument and doesn't care what "this" = is, so we + // can just call the original function directly. + if (originalFn.apply) { + return originalFn.apply(this, args); + } else { + return originalFn(args[0], args[1]); + } + }; + }; + + _helper('setTimeout'); + _helper('setInterval'); +}(window)); + +/** + * Extended support for backtraces and global error handling for most + * asynchronous jQuery functions. + */ +(function traceKitAsyncForjQuery($) { + + // quit if jQuery isn't on the page + if (!$) { + return; + } + + var _oldEventAdd =3D $.event.add; + $.event.add =3D function traceKitEventAdd(elem, types, handler, data, = selector) { + var _handler; + + if (handler.handler) { + _handler =3D handler.handler; + handler.handler =3D function traceKitHandler() { + try { + return _handler.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } else { + _handler =3D handler; + handler =3D function apply_handler() { + try { + return _handler.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } + + // If the handler we are attaching doesn=E2=80=99t have the same g= uid as + // the original, it will never be removed when someone tries to + // unbind the original function later. Technically as a result of + // this our guids are no longer globally unique, but whatever, that + // never hurt anybody RIGHT?! + if (_handler.guid) { + handler.guid =3D _handler.guid; + } else { + handler.guid =3D _handler.guid =3D $.guid++; + } + + return _oldEventAdd.call(this, elem, types, handler, data, selecto= r); + }; + + var _oldReady =3D $.fn.ready; + $.fn.ready =3D function traceKitjQueryReadyWrapper(fn) { + var _fn =3D function () { + try { + return fn.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + + return _oldReady.call(this, _fn); + }; + + var _oldAjax =3D $.ajax; + $.ajax =3D function traceKitAjaxWrapper(s) { + if ($.isFunction(s.complete)) { + var _oldComplete =3D s.complete; + s.complete =3D function traceKitjQueryComplete() { + try { + return _oldComplete.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } + + if ($.isFunction(s.error)) { + var _oldError =3D s.error; + s.error =3D function traceKitjQueryError() { + try { + return _oldError.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } + + if ($.isFunction(s.success)) { + var _oldSuccess =3D s.success; + s.success =3D function traceKitjQuerySuccess() { + try { + return _oldSuccess.apply(this, arguments); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + } + + try { + return _oldAjax.call(this, s); + } catch (e) { + TraceKit.report(e); + throw e; + } + }; + +}(window.jQuery)); + +//Default options: +if (!TraceKit.remoteFetching) { + TraceKit.remoteFetching =3D true; +} +if (!TraceKit.collectWindowErrors) { + TraceKit.collectWindowErrors =3D true; +} +if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) { + // 5 lines before, the offending line, 5 lines after + TraceKit.linesOfContext =3D 11; +} + + + +// Export to global object +window.TraceKit =3D TraceKit; + +}(window)); diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 static/scripts/packed/libs/raven.js --- /dev/null +++ b/static/scripts/packed/libs/raven.js @@ -0,0 +1,1 @@ +(function(){var x=3Dwindow.Raven,u=3D!h(window.JSON),e,o,p,q,f=3D{logger:"= javascript",ignoreErrors:[],ignoreUrls:[]};var z=3DTraceKit.noConflict();z.= remoteFetching=3Dfalse;var n=3D{VERSION:"@VERSION",noConflict:function(){wi= ndow.Raven=3Dx;return n},config:function(C,B){var D=3Dv(C),A=3DD.path.lastI= ndexOf("/"),E=3DD.path.substr(1,A);if(B){j(B,function(F,G){f[F]=3DG})}f.ign= oreErrors.push("Script error.");p=3DD.user;q=3D~~D.path.substr(A+1);e=3DD.p= rotocol+"://"+D.host+(D.port?":"+D.port:"")+"/"+E+"api/"+q+"/store/";if(f.f= etchContext){z.remoteFetching=3Dtrue}return n},install:function(){if(!s()){= return}z.report.subscribe(m);return n},context:function(B,C,A){if(c(B)){A= =3DC;C=3DB;B=3Dundefined}n.wrap(B,C).apply(this,A)},wrap:function(A,B){if(c= (A)){B=3DA;A=3Dundefined}return function(){try{B.apply(this,arguments)}catc= h(C){n.captureException(C,A)}}},uninstall:function(){z.report.unsubscribe(m= );return n},captureException:function(B,A){try{z.report(B,A)}catch(C){if(B!= =3D=3DC){throw C}}return n},captureMessage:function(B,A){l(r({message:B},A)= );return n},setUser:function(A){o=3DA;return n}};var b=3D"source protocol a= uthority userInfo user password host port relative path directory file quer= y anchor".split(" "),a=3D/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?(= (?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\= /]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;funct= ion v(D){var A=3Da.exec(D),C=3D{},B=3D14;while(B--){C[b[B]]=3DA[B]||""}retu= rn C}function h(A){return typeof A=3D=3D=3D"undefined"}function c(A){return= typeof A=3D=3D=3D"function"}function j(C,D){var B,A;if(C.length=3D=3D=3Dun= defined){for(B in C){if(C.hasOwnProperty(B)){D.call(null,B,C[B])}}}else{for= (B=3D0,A=3DC.length;B<A;B++){D.call(null,B,C[B])}}}var g;function d(){if(g)= {return g}var A=3D["sentry_version=3D2.0","sentry_client=3Draven-js/"+n.VER= SION];if(p){A.push("sentry_key=3D"+p)}g=3D"?"+A.join("&");return g}function= m(D,B){var F=3D[],C=3D0,A=3DD.stack&&D.stack.length||0,E;for(;C<A;C++){E= =3Dk(D.stack[C]);if(E){F.push(E)}}i(D.name,D.message,D.url,D.lineno,F,B)}fu= nction k(E){if(!E.url){return}var D=3D{filename:E.url,lineno:E.line,colno:E= .column,"function":E.func||"?"},B=3Dw(E);if(B){var A=3D3,C=3D["pre_context"= ,"context_line","post_context"];while(A--){D[C[A]]=3DB[A]}}D.in_app=3D!/(Ra= ven|TraceKit)\./.test(D["function"]);return D}function w(E){if(!E.context||= !f.fetchContext){return}var C=3DE.context,A=3D~~(C.length/2),B=3DC.length,D= =3Dfalse;while(B--){if(C[B].length>300){D=3Dtrue;break}}if(D){if(h(E.column= )){return}return[[],C[A].substr(E.column,50),[]]}return[C.slice(0,A),C[A],C= .slice(A+1)]}function i(D,H,F,A,C,I){var G,E,B;B=3Df.ignoreErrors.length;wh= ile(B--){if(H=3D=3D=3Df.ignoreErrors[B]){return}}if(C&&C.length){G=3D{frame= s:C};F=3DF||C[0].filename}else{if(F){G=3D{frames:[{filename:F,lineno:A}]}}}= B=3Df.ignoreUrls.length;while(B--){if(f.ignoreUrls[B].test(F)){return}}E=3D= A?H+" at "+A:H;l(r({"sentry.interfaces.Exception":{type:D,value:H},"sentry.= interfaces.Stacktrace":G,culprit:F,message:E},I))}function r(B,A){if(!A){re= turn B}j(A,function(C,D){B[C]=3DD});return B}function y(){var A=3D{url:wind= ow.location.href,headers:{"User-Agent":navigator.userAgent}};if(window.docu= ment.referrer){A.headers.Referer=3Dwindow.document.referrer}return A}functi= on l(A){if(!s()){return}A=3Dr({project:q,logger:f.logger,site:f.site,platfo= rm:"javascript","sentry.interfaces.Http":y()},A);if(o){A["sentry.interfaces= .User"]=3Do}if(c(f.dataCallback)){A=3Df.dataCallback(A)}t(A)}function t(A){= new Image().src=3De+d()+"&sentry_data=3D"+encodeURIComponent(JSON.stringify= (A))}function s(){if(!u){return false}if(!e){console.error("Error: Raven ha= s not been configured.");return false}return true}window.Raven=3Dn})(); \ No newline at end of file diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 static/scripts/packed/libs/tracekit.js --- /dev/null +++ b/static/scripts/packed/libs/tracekit.js @@ -0,0 +1,1 @@ +(function(h,d){var e=3D{};var j=3Dh.TraceKit;e.noConflict=3Dfunction g(){h= .TraceKit=3Dj;return e};e._has=3Dfunction a(k,l){return Object.prototype.ha= sOwnProperty.call(k,l)};e.report=3D(function i(){var k=3D[],o=3Dnull,q=3Dnu= ll;function l(t){k.push(t)}function r(u){for(var t=3Dk.length-1;t>=3D0;--t)= {if(k[t]=3D=3D=3Du){k.splice(t,1)}}}function p(t,v){var x=3Dnull;if(v&&!e.c= ollectWindowErrors){return}for(var w in k){if(e._has(k,w)){try{k[w].apply(n= ull,[t].concat(Array.prototype.slice.call(arguments,2)))}catch(u){x=3Du}}}i= f(x){throw x}}var s=3Dh.onerror;h.onerror=3Dfunction n(w,v,x){var t=3Dnull;= if(q){e.computeStackTrace.augmentStackTraceWithInitialElement(q,v,x,w);t=3D= q;q=3Dnull;o=3Dnull}else{var u=3D{url:v,line:x};u.func=3De.computeStackTrac= e.guessFunctionName(u.url,u.line);u.context=3De.computeStackTrace.gatherCon= text(u.url,u.line);t=3D{mode:"onerror",message:w,url:document.location.href= ,stack:[u],useragent:navigator.userAgent}}p(t,"from window.onerror");if(s){= return s.apply(this,arguments)}return false};function m(v){var u=3DArray.pr= ototype.slice.call(arguments,1);if(q){if(o=3D=3D=3Dv){return}else{var w=3Dq= ;q=3Dnull;o=3Dnull;p.apply(null,[w,null].concat(u))}}var t=3De.computeStack= Trace(v);q=3Dt;o=3Dv;h.setTimeout(function(){if(o=3D=3D=3Dv){q=3Dnull;o=3Dn= ull;p.apply(null,[t,null].concat(u))}},(t.incomplete?2000:0));throw v}m.sub= scribe=3Dl;m.unsubscribe=3Dr;return m}());e.computeStackTrace=3D(function b= (){var u=3Dfalse,q=3D{};function l(D){if(!e.remoteFetching){return""}try{va= r C;if(typeof(XMLHttpRequest)=3D=3D=3D"undefined"){C=3Dfunction E(){try{ret= urn new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(H){}try{return new Active= XObject("Msxml2.XMLHTTP.3.0")}catch(H){}try{return new ActiveXObject("Msxml= 2.XMLHTTP")}catch(H){}try{return new ActiveXObject("Microsoft.XMLHTTP")}cat= ch(H){}throw new Error("No XHR.")}}else{C=3DXMLHttpRequest}var F=3Dnew C();= F.open("GET",D,false);F.send("");return F.responseText}catch(G){return""}}f= unction t(C){if(!e._has(q,C)){var D;if(C.indexOf(document.domain)!=3D=3D-1)= {D=3Dl(C)}else{D=3D[]}q[C]=3DD.length?D.split("\n"):[]}return q[C]}function= z(D,F){var J=3D/function ([^(]*)\(([^)]*)\)/,G=3D/['"]?([0-9A-Za-z$_]+)['"= ]?\s*[:=3D]\s*(function|eval|new Function)/,K=3D"",I=3D10,C=3Dt(D),E;if(!C.= length){return"?"}for(var H=3D0;H<I;++H){K=3DC[F-H]+K;if(K!=3D=3Dd){if((E= =3DG.exec(K))){return E[1]}else{if((E=3DJ.exec(K))){return E[1]}}}}return"?= "}function B(E,K){var D=3Dt(E);if(!D.length){return null}var G=3D[],J=3DMat= h.floor(e.linesOfContext/2),C=3DJ+(e.linesOfContext%2),F=3DMath.max(0,K-J-1= ),H=3DMath.min(D.length,K+C-1);K-=3D1;for(var I=3DF;I<H;++I){if(typeof(D[I]= )!=3D=3D"undefined"){G.push(D[I])}}return G.length>0?G:null}function m(C){r= eturn C.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g,"\\$&")}function x(C){return m(= C).replace("<","(?:<|<)").replace(">","(?:>|>)").replace("&","(?:&|&a= mp;)").replace('"','(?:"|")').replace(/\s+/g,"\\s+")}function o(F,H){v= ar G,C;for(var E=3D0,D=3DH.length;E<D;++E){if((G=3Dt(H[E])).length){G=3DG.j= oin("\n");if((C=3DF.exec(G))){return{url:H[E],line:G.substring(0,C.index).s= plit("\n").length,column:C.index-G.lastIndexOf("\n",C.index)-1}}}}return nu= ll}function A(F,E,D){var H=3Dt(E),G=3Dnew RegExp("\\b"+m(F)+"\\b"),C;D-=3D1= ;if(H&&H.length>D&&(C=3DG.exec(H[D]))){return C.index}return null}function = v(H){var N=3D[h.location.href],I=3Ddocument.getElementsByTagName("script"),= L,F=3D""+H,E=3D/^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]= *\S)\s*\}\s*$/,G=3D/^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s= *\}\s*$/,P,J,Q;for(var K=3D0;K<I.length;++K){var O=3DI[K];if(O.src){N.push(= O.src)}}if(!(J=3DE.exec(F))){P=3Dnew RegExp(m(F).replace(/\s+/g,"\\s+"))}el= se{var D=3DJ[1]?"\\s+"+J[1]:"",M=3DJ[2].split(",").join("\\s*,\\s*");L=3Dm(= J[3]).replace(/;$/,";?");P=3Dnew RegExp("function"+D+"\\s*\\(\\s*"+M+"\\s*\= \)\\s*{\\s*"+L+"\\s*}")}if((Q=3Do(P,N))){return Q}if((J=3DG.exec(F))){var C= =3DJ[1];L=3Dx(J[2]);P=3Dnew RegExp("on"+C+"=3D[\\'\"]\\s*"+L+"\\s*[\\'\"]",= "i");if((Q=3Do(P,N[0]))){return Q}P=3Dnew RegExp(L);if((Q=3Do(P,N))){return= Q}}return null}function w(J){if(!J.stack){return null}var I=3D/^\s*at (?:(= (?:\[object object\])?\S+) )?\(?((?:file|http|https):.*?):(\d+)(?::(\d+))?\= )?\s*$/i,C=3D/^\s*(\S*)(?:\((.*?)\))?@((?:file|http|https).*?):(\d+)(?::(\d= +))?\s*$/i,L=3DJ.stack.split("\n"),K=3D[],F,H,D=3D/^(.*) is undefined$/.exe= c(J.message);for(var G=3D0,E=3DL.length;G<E;++G){if((F=3DC.exec(L[G]))){H= =3D{url:F[3],func:F[1]||"?",args:F[2]?F[2].split(","):"",line:+F[4],column:= F[5]?+F[5]:null}}else{if((F=3DI.exec(L[G]))){H=3D{url:F[2],func:F[1]||"?",l= ine:+F[3],column:F[4]?+F[4]:null}}else{continue}}if(!H.func&&H.line){H.func= =3Dz(H.url,H.line)}if(H.line){H.context=3DB(H.url,H.line)}K.push(H)}if(K[0]= &&K[0].line&&!K[0].column&&D){K[0].column=3DA(D[1],K[0].url,K[0].line)}if(!= K.length){return null}return{mode:"stack",name:J.name,message:J.message,url= :document.location.href,stack:K,useragent:navigator.userAgent}}function s(H= ){var J=3DH.stacktrace;var G=3D/ line (\d+), column (\d+) in (?:<anonymous = function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,L=3DJ.split("\n"),I=3D[= ],C;for(var F=3D0,D=3DL.length;F<D;F+=3D2){if((C=3DG.exec(L[F]))){var E=3D{= line:+C[1],column:+C[2],func:C[3]||C[4],args:C[5]?C[5].split(","):[],url:C[= 6]};if(!E.func&&E.line){E.func=3Dz(E.url,E.line)}if(E.line){try{E.context= =3DB(E.url,E.line)}catch(K){}}if(!E.context){E.context=3D[L[F+1]]}I.push(E)= }}if(!I.length){return null}return{mode:"stacktrace",name:H.name,message:H.= message,url:document.location.href,stack:I,useragent:navigator.userAgent}}f= unction p(U){var E=3DU.message.split("\n");if(E.length<4){return null}var G= =3D/^\s*Line (\d+) of linked script ((?:file|http|https)\S+)(?:: in functio= n (\S+))?\s*$/i,F=3D/^\s*Line (\d+) of inline#(\d+) script in ((?:file|http= |https)\S+)(?:: in function (\S+))?\s*$/i,C=3D/^\s*Line (\d+) of function s= cript\s*$/i,L=3D[],I=3Ddocument.getElementsByTagName("script"),T=3D[],P,R,S= ,Q;for(R in I){if(e._has(I,R)&&!I[R].src){T.push(I[R])}}for(R=3D2,S=3DE.len= gth;R<S;R+=3D2){var V=3Dnull;if((P=3DG.exec(E[R]))){V=3D{url:P[2],func:P[3]= ,line:+P[1]}}else{if((P=3DF.exec(E[R]))){V=3D{url:P[3],func:P[4]};var D=3D(= +P[1]);var W=3DT[P[2]-1];if(W){Q=3Dt(V.url);if(Q){Q=3DQ.join("\n");var K=3D= Q.indexOf(W.innerText);if(K>=3D0){V.line=3DD+Q.substring(0,K).split("\n").l= ength}}}}else{if((P=3DC.exec(E[R]))){var J=3Dh.location.href.replace(/#.*$/= ,""),M=3DP[1];var O=3Dnew RegExp(x(E[R+1]));Q=3Do(O,[J]);V=3D{url:J,line:Q?= Q.line:M,func:""}}}}if(V){if(!V.func){V.func=3Dz(V.url,V.line)}var H=3DB(V.= url,V.line);var N=3D(H?H[Math.floor(H.length/2)]:null);if(H&&N.replace(/^\s= */,"")=3D=3D=3DE[R+1].replace(/^\s*/,"")){V.context=3DH}else{V.context=3D[E= [R+1]]}L.push(V)}}if(!L.length){return null}return{mode:"multiline",name:U.= name,message:E[0],url:document.location.href,stack:L,useragent:navigator.us= erAgent}}function y(G,E,H,F){var D=3D{url:E,line:H};if(D.url&&D.line){G.inc= omplete=3Dfalse;if(!D.func){D.func=3Dz(D.url,D.line)}if(!D.context){D.conte= xt=3DB(D.url,D.line)}var C=3D/ '([^']+)' /.exec(F);if(C){D.column=3DA(C[1],= D.url,D.line)}if(G.stack.length>0){if(G.stack[0].url=3D=3D=3DD.url){if(G.st= ack[0].line=3D=3D=3DD.line){return false}else{if(!G.stack[0].line&&G.stack[= 0].func=3D=3D=3DD.func){G.stack[0].line=3DD.line;G.stack[0].context=3DD.con= text;return false}}}}G.stack.unshift(D);G.partial=3Dtrue;return true}else{G= .incomplete=3Dtrue}return false}function n(J,H){var I=3D/function\s+([_$a-z= A-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i,K=3D[],D=3D{},F=3Dfalse,G= ,L,C;for(var N=3Dn.caller;N&&!F;N=3DN.caller){if(N=3D=3D=3Dr||N=3D=3D=3De.r= eport){continue}L=3D{url:null,func:"?",line:null,column:null};if(N.name){L.= func=3DN.name}else{if((G=3DI.exec(N.toString()))){L.func=3DG[1]}}if((C=3Dv(= N))){L.url=3DC.url;L.line=3DC.line;if(L.func=3D=3D=3D"?"){L.func=3Dz(L.url,= L.line)}var E=3D/ '([^']+)' /.exec(J.message||J.description);if(E){L.column= =3DA(E[1],C.url,C.line)}}if(D[""+N]){F=3Dtrue}else{D[""+N]=3Dtrue}K.push(L)= }if(H){K.splice(0,H)}var M=3D{mode:"callers",name:J.name,message:J.message,= url:document.location.href,stack:K,useragent:navigator.userAgent};y(M,J.sou= rceURL||J.fileName,J.line||J.lineNumber,J.message||J.description);return M}= function r(D,F){var C=3Dnull;F=3D(F=3D=3Dnull?0:+F);try{C=3Ds(D);if(C){retu= rn C}}catch(E){if(u){throw E}}try{C=3Dw(D);if(C){return C}}catch(E){if(u){t= hrow E}}try{C=3Dp(D);if(C){return C}}catch(E){if(u){throw E}}try{C=3Dn(D,F+= 1);if(C){return C}}catch(E){if(u){throw E}}return{mode:"failed"}}function k= (D){D=3D(D=3D=3Dnull?0:+D)+1;try{throw new Error()}catch(C){return r(C,D+1)= }return null}r.augmentStackTraceWithInitialElement=3Dy;r.guessFunctionName= =3Dz;r.gatherContext=3DB;r.ofCaller=3Dk;return r}());(function f(k){var l= =3Dfunction l(o){var n=3Dk[o];k[o]=3Dfunction m(){var p=3DArray.prototype.s= lice.call(arguments,0);var r=3Dp[0];if(typeof(r)=3D=3D=3D"function"){p[0]= =3Dfunction q(){try{r.apply(this,arguments)}catch(s){e.report(s);throw s}}}= if(n.apply){return n.apply(this,p)}else{return n(p[0],p[1])}}};l("setTimeou= t");l("setInterval")}(h));(function c(q){if(!q){return}var p=3Dq.event.add;= q.event.add=3Dfunction n(w,u,v,x,r){var s;if(v.handler){s=3Dv.handler;v.han= dler=3Dfunction t(){try{return s.apply(this,arguments)}catch(z){e.report(z)= ;throw z}}}else{s=3Dv;v=3Dfunction y(){try{return s.apply(this,arguments)}c= atch(z){e.report(z);throw z}}}if(s.guid){v.guid=3Ds.guid}else{v.guid=3Ds.gu= id=3Dq.guid++}return p.call(this,w,u,v,x,r)};var l=3Dq.fn.ready;q.fn.ready= =3Dfunction k(r){var s=3Dfunction(){try{return r.apply(this,arguments)}catc= h(t){e.report(t);throw t}};return l.call(this,s)};var o=3Dq.ajax;q.ajax=3Df= unction m(x){if(q.isFunction(x.complete)){var v=3Dx.complete;x.complete=3Df= unction u(){try{return v.apply(this,arguments)}catch(s){e.report(s);throw s= }}}if(q.isFunction(x.error)){var r=3Dx.error;x.error=3Dfunction z(){try{ret= urn r.apply(this,arguments)}catch(s){e.report(s);throw s}}}if(q.isFunction(= x.success)){var w=3Dx.success;x.success=3Dfunction t(){try{return w.apply(t= his,arguments)}catch(s){e.report(s);throw s}}}try{return o.call(this,x)}cat= ch(y){e.report(y);throw y}}}(h.jQuery));if(!e.remoteFetching){e.remoteFetch= ing=3Dtrue}if(!e.collectWindowErrors){e.collectWindowErrors=3Dtrue}if(!e.li= nesOfContext||e.linesOfContext<1){e.linesOfContext=3D11}h.TraceKit=3De}(win= dow)); \ No newline at end of file diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 templates/base.mako --- a/templates/base.mako +++ b/templates/base.mako @@ -24,6 +24,17 @@ ## Default javascripts <%def name=3D"javascripts()"> =20 + ## Send errors to Sntry server if configured + %if app.config.sentry_dsn: + ${h.js( "libs/tracekit", "libs/raven" )} + <script> + Raven.config('${app.config.sentry_dsn_public}').install(); + %if trans.user: + Raven.setUser( { email: "${trans.user.email}" } ); + %endif + </script> + %endif + ${h.js( "libs/jquery/jquery", "libs/jquery/select2", @@ -54,7 +65,7 @@ error : function(){}, assert : function(){} }; - =20 + // Set up needed paths. var galaxy_paths =3D new GalaxyPaths({ root_path: '${h.url_for( "/" )}', diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 templates/base/base_panels.mako --- a/templates/base/base_panels.mako +++ b/templates/base/base_panels.mako @@ -45,9 +45,18 @@ =20 ## Default javascripts <%def name=3D"javascripts()"> - <!--[if lt IE 7]> - ${h.js( 'libs/IE/IE7', 'libs/IE/ie7-recalc' )} - <![endif]--> + + ## Send errors to Sntry server if configured + %if app.config.sentry_dsn: + ${h.js( "libs/tracekit", "libs/raven" )} + <script> + Raven.config('${app.config.sentry_dsn_public}').install(); + %if trans.user: + Raven.setUser( { email: "${trans.user.email}" } ); + %endif + </script> + %endif + ${h.js( 'libs/jquery/jquery', 'libs/json2', diff -r 7950ce95b2769f2fb63bfc8a549ee9bc9fdcbed2 -r 708d0ccee63fc75eb88e5dc= 3a9e0d6d3e5ff72e7 templates/root/tool_menu.mako --- a/templates/root/tool_menu.mako +++ b/templates/root/tool_menu.mako @@ -74,7 +74,7 @@ }); */ =20 - $( '.tooltip' ).tooltip(); + $( '.tooltip' ).tooltip().foasdasdasdoo(); =20 // TODO: is this necessary? $( "a[minsizehint]" ).click( function() { Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.