BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
53534682
Commit
53534682
authored
Apr 21, 2016
by
Jacob Schatz
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'revert-sentry-js' into 'master'
Revert "Merge branch 'sentry-js' into 'master'" This reverts commit
0f309794
, reversing changes made to
1e596fef
. See merge request !3851
parents
f4859b93
6211a8a8
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
0 additions
and
2483 deletions
+0
-2483
CHANGELOG
CHANGELOG
+0
-1
application.js.coffee
app/assets/javascripts/application.js.coffee
+0
-1
raven_config.js.coffee
app/assets/javascripts/raven_config.js.coffee
+0
-44
gon_helper.rb
lib/gitlab/gon_helper.rb
+0
-1
raven.js
vendor/assets/javascripts/raven.js
+0
-2436
No files found.
CHANGELOG
View file @
53534682
...
@@ -90,7 +90,6 @@ v 8.7.0 (unreleased)
...
@@ -90,7 +90,6 @@ v 8.7.0 (unreleased)
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- Fix repository cache invalidation issue when project is recreated with an empty repo (Stan Hu)
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- Fix: Allow empty recipients list for builds emails service when pushed is added (Frank Groeneveld)
- Improved markdown forms
- Improved markdown forms
- Show JavaScript errors in sentry
- Diff design updates (colors, button styles, etc)
- Diff design updates (colors, button styles, etc)
- Copying and pasting a diff no longer pastes the line numbers or +/-
- Copying and pasting a diff no longer pastes the line numbers or +/-
- Add null check to formData when updating profile content to fix Firefox bug
- Add null check to formData when updating profile content to fix Firefox bug
...
...
app/assets/javascripts/application.js.coffee
View file @
53534682
...
@@ -55,7 +55,6 @@
...
@@ -55,7 +55,6 @@
#= require_tree .
#= require_tree .
#= require fuzzaldrin-plus
#= require fuzzaldrin-plus
#= require cropper
#= require cropper
#= require raven
window
.
slugify
=
(
text
)
->
window
.
slugify
=
(
text
)
->
text
.
replace
(
/[^-a-zA-Z0-9]+/g
,
'_'
).
toLowerCase
()
text
.
replace
(
/[^-a-zA-Z0-9]+/g
,
'_'
).
toLowerCase
()
...
...
app/assets/javascripts/raven_config.js.coffee
deleted
100644 → 0
View file @
f4859b93
@
raven
=
init
:
->
if
gon
.
sentry_dsn
?
Raven
.
config
(
gon
.
sentry_dsn
,
{
includePaths
:
[
/gon.relative_url_root/
]
ignoreErrors
:
[
# Random plugins/extensions
'top.GLOBALS'
,
# See: http://blog.errorception.com/2012/03/tale-of-unfindable-js-error. html
'originalCreateNotification'
,
'canvas.contentDocument'
,
'MyApp_RemoveAllHighlights'
,
'http://tt.epicplay.com'
,
'Can
\'
t find variable: ZiteReader'
,
'jigsaw is not defined'
,
'ComboSearch is not defined'
,
'http://loading.retry.widdit.com/'
,
'atomicFindClose'
,
# ISP "optimizing" proxy - `Cache-Control: no-transform` seems to
# reduce this. (thanks @acdha)
# See http://stackoverflow.com/questions/4113268
'bmi_SafeAddOnload'
,
'EBCallBackMessageReceived'
,
# See http://toolbar.conduit.com/Developer/HtmlAndGadget/Methods/JSInjection.aspx
'conduitPage'
],
ignoreUrls
:
[
# Chrome extensions
/extensions\//i
,
/^chrome:\/\//i
,
# Other plugins
/127\.0\.0\.1:4001\/isrunning/i
,
# Cacaoweb
/webappstoolbarba\.texthelp\.com\//i
,
/metrics\.itunes\.apple\.com\.edgesuite\.net\//i
]
}).
install
()
if
gon
.
current_user_id
Raven
.
setUserContext
({
id
:
gon
.
current_user_id
})
$
->
raven
.
init
()
lib/gitlab/gon_helper.rb
View file @
53534682
...
@@ -7,7 +7,6 @@ module Gitlab
...
@@ -7,7 +7,6 @@ module Gitlab
gon
.
max_file_size
=
current_application_settings
.
max_attachment_size
gon
.
max_file_size
=
current_application_settings
.
max_attachment_size
gon
.
relative_url_root
=
Gitlab
.
config
.
gitlab
.
relative_url_root
gon
.
relative_url_root
=
Gitlab
.
config
.
gitlab
.
relative_url_root
gon
.
user_color_scheme
=
Gitlab
::
ColorSchemes
.
for_user
(
current_user
).
css_class
gon
.
user_color_scheme
=
Gitlab
::
ColorSchemes
.
for_user
(
current_user
).
css_class
gon
.
sentry_dsn
=
ApplicationSetting
.
current
.
sentry_dsn
if
Rails
.
env
.
production?
if
current_user
if
current_user
gon
.
current_user_id
=
current_user
.
id
gon
.
current_user_id
=
current_user
.
id
...
...
vendor/assets/javascripts/raven.js
deleted
100644 → 0
View file @
f4859b93
/*! Raven.js 2.3.0 (b09d766) | github.com/getsentry/raven-js */
/*
* Includes TraceKit
* https://github.com/getsentry/TraceKit
*
* Copyright 2016 Matt Robenolt and other contributors
* Released under the BSD license
* https://github.com/getsentry/raven-js/blob/master/LICENSE
*
*/
(
function
(
f
){
if
(
typeof
exports
===
"object"
&&
typeof
module
!==
"undefined"
){
module
.
exports
=
f
()}
else
if
(
typeof
define
===
"function"
&&
define
.
amd
){
define
([],
f
)}
else
{
var
g
;
if
(
typeof
window
!==
"undefined"
){
g
=
window
}
else
if
(
typeof
global
!==
"undefined"
){
g
=
global
}
else
if
(
typeof
self
!==
"undefined"
){
g
=
self
}
else
{
g
=
this
}
g
.
Raven
=
f
()}})(
function
(){
var
define
,
module
,
exports
;
return
(
function
e
(
t
,
n
,
r
){
function
s
(
o
,
u
){
if
(
!
n
[
o
]){
if
(
!
t
[
o
]){
var
a
=
typeof
require
==
"function"
&&
require
;
if
(
!
u
&&
a
)
return
a
(
o
,
!
0
);
if
(
i
)
return
i
(
o
,
!
0
);
var
f
=
new
Error
(
"Cannot find module '"
+
o
+
"'"
);
throw
f
.
code
=
"MODULE_NOT_FOUND"
,
f
}
var
l
=
n
[
o
]
=
{
exports
:{}};
t
[
o
][
0
].
call
(
l
.
exports
,
function
(
e
){
var
n
=
t
[
o
][
1
][
e
];
return
s
(
n
?
n
:
e
)},
l
,
l
.
exports
,
e
,
t
,
n
,
r
)}
return
n
[
o
].
exports
}
var
i
=
typeof
require
==
"function"
&&
require
;
for
(
var
o
=
0
;
o
<
r
.
length
;
o
++
)
s
(
r
[
o
]);
return
s
})({
1
:[
function
(
_dereq_
,
module
,
exports
){
'use strict'
;
function
RavenConfigError
(
message
)
{
this
.
name
=
'RavenConfigError'
;
this
.
message
=
message
;
}
RavenConfigError
.
prototype
=
new
Error
();
RavenConfigError
.
prototype
.
constructor
=
RavenConfigError
;
module
.
exports
=
RavenConfigError
;
},{}],
2
:[
function
(
_dereq_
,
module
,
exports
){
/*global XDomainRequest:false*/
'use strict'
;
var
TraceKit
=
_dereq_
(
5
);
var
RavenConfigError
=
_dereq_
(
1
);
var
utils
=
_dereq_
(
4
);
var
isFunction
=
utils
.
isFunction
;
var
isUndefined
=
utils
.
isUndefined
;
var
isError
=
utils
.
isError
;
var
isEmptyObject
=
utils
.
isEmptyObject
;
var
hasKey
=
utils
.
hasKey
;
var
joinRegExp
=
utils
.
joinRegExp
;
var
each
=
utils
.
each
;
var
objectMerge
=
utils
.
objectMerge
;
var
truncate
=
utils
.
truncate
;
var
urlencode
=
utils
.
urlencode
;
var
uuid4
=
utils
.
uuid4
;
var
dsnKeys
=
'source protocol user pass host port path'
.
split
(
' '
),
dsnPattern
=
/^
(?:(\w
+
)
:
)?\/\/(?:(\w
+
)(
:
\w
+
)?
@
)?([\w\.
-
]
+
)(?:
:
(\d
+
))?(\/
.*
)
/
;
function
now
()
{
return
+
new
Date
();
}
// 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
function
Raven
()
{
this
.
_hasJSON
=
!!
(
typeof
JSON
===
'object'
&&
JSON
.
stringify
);
// Raven can run in contexts where there's no document (react-native)
this
.
_hasDocument
=
typeof
document
!==
'undefined'
;
this
.
_lastCapturedException
=
null
;
this
.
_lastEventId
=
null
;
this
.
_globalServer
=
null
;
this
.
_globalKey
=
null
;
this
.
_globalProject
=
null
;
this
.
_globalContext
=
{};
this
.
_globalOptions
=
{
logger
:
'javascript'
,
ignoreErrors
:
[],
ignoreUrls
:
[],
whitelistUrls
:
[],
includePaths
:
[],
crossOrigin
:
'anonymous'
,
collectWindowErrors
:
true
,
maxMessageLength
:
0
,
stackTraceLimit
:
50
};
this
.
_ignoreOnError
=
0
;
this
.
_isRavenInstalled
=
false
;
this
.
_originalErrorStackTraceLimit
=
Error
.
stackTraceLimit
;
// capture references to window.console *and* all its methods first
// before the console plugin has a chance to monkey patch
this
.
_originalConsole
=
window
.
console
||
{};
this
.
_originalConsoleMethods
=
{};
this
.
_plugins
=
[];
this
.
_startTime
=
now
();
this
.
_wrappedBuiltIns
=
[];
for
(
var
method
in
this
.
_originalConsole
)
{
// eslint-disable-line guard-for-in
this
.
_originalConsoleMethods
[
method
]
=
this
.
_originalConsole
[
method
];
}
}
/*
* The core Raven singleton
*
* @this {Raven}
*/
Raven
.
prototype
=
{
// Hardcode version string so that raven source can be loaded directly via
// webpack (using a build step causes webpack #1617). Grunt verifies that
// this value matches package.json during build.
// See: https://github.com/getsentry/raven-js/issues/465
VERSION
:
'2.3.0'
,
debug
:
false
,
TraceKit
:
TraceKit
,
// alias to TraceKit
/*
* Configure Raven with a DSN and extra options
*
* @param {string} dsn The public Sentry DSN
* @param {object} options Optional set of of global options [optional]
* @return {Raven}
*/
config
:
function
(
dsn
,
options
)
{
var
self
=
this
;
if
(
this
.
_globalServer
)
{
this
.
_logDebug
(
'error'
,
'Error: Raven has already been configured'
);
return
this
;
}
if
(
!
dsn
)
return
this
;
// merge in options
if
(
options
)
{
each
(
options
,
function
(
key
,
value
){
// tags and extra are special and need to be put into context
if
(
key
===
'tags'
||
key
===
'extra'
)
{
self
.
_globalContext
[
key
]
=
value
;
}
else
{
self
.
_globalOptions
[
key
]
=
value
;
}
});
}
var
uri
=
this
.
_parseDSN
(
dsn
),
lastSlash
=
uri
.
path
.
lastIndexOf
(
'/'
),
path
=
uri
.
path
.
substr
(
1
,
lastSlash
);
this
.
_dsn
=
dsn
;
// "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.
this
.
_globalOptions
.
ignoreErrors
.
push
(
/^Script error
\.?
$/
);
this
.
_globalOptions
.
ignoreErrors
.
push
(
/^Javascript error: Script error
\.?
on line 0$/
);
// join regexp rules into one big rule
this
.
_globalOptions
.
ignoreErrors
=
joinRegExp
(
this
.
_globalOptions
.
ignoreErrors
);
this
.
_globalOptions
.
ignoreUrls
=
this
.
_globalOptions
.
ignoreUrls
.
length
?
joinRegExp
(
this
.
_globalOptions
.
ignoreUrls
)
:
false
;
this
.
_globalOptions
.
whitelistUrls
=
this
.
_globalOptions
.
whitelistUrls
.
length
?
joinRegExp
(
this
.
_globalOptions
.
whitelistUrls
)
:
false
;
this
.
_globalOptions
.
includePaths
=
joinRegExp
(
this
.
_globalOptions
.
includePaths
);
this
.
_globalKey
=
uri
.
user
;
this
.
_globalSecret
=
uri
.
pass
&&
uri
.
pass
.
substr
(
1
);
this
.
_globalProject
=
uri
.
path
.
substr
(
lastSlash
+
1
);
this
.
_globalServer
=
this
.
_getGlobalServer
(
uri
);
this
.
_globalEndpoint
=
this
.
_globalServer
+
'/'
+
path
+
'api/'
+
this
.
_globalProject
+
'/store/'
;
if
(
this
.
_globalOptions
.
fetchContext
)
{
TraceKit
.
remoteFetching
=
true
;
}
if
(
this
.
_globalOptions
.
linesOfContext
)
{
TraceKit
.
linesOfContext
=
this
.
_globalOptions
.
linesOfContext
;
}
TraceKit
.
collectWindowErrors
=
!!
this
.
_globalOptions
.
collectWindowErrors
;
// return for chaining
return
this
;
},
/*
* Installs a global window.onerror error handler
* to capture and report uncaught exceptions.
* At this point, install() is required to be called due
* to the way TraceKit is set up.
*
* @return {Raven}
*/
install
:
function
()
{
var
self
=
this
;
if
(
this
.
isSetup
()
&&
!
this
.
_isRavenInstalled
)
{
TraceKit
.
report
.
subscribe
(
function
()
{
self
.
_handleOnErrorStackInfo
.
apply
(
self
,
arguments
);
});
this
.
_wrapBuiltIns
();
// Install all of the plugins
this
.
_drainPlugins
();
this
.
_isRavenInstalled
=
true
;
}
Error
.
stackTraceLimit
=
this
.
_globalOptions
.
stackTraceLimit
;
return
this
;
},
/*
* Wrap code within a context so Raven can capture errors
* reliably across domains that is executed immediately.
*
* @param {object} options A specific set of options for this context [optional]
* @param {function} func The callback to be immediately executed within the context
* @param {array} args An array of arguments to be called with the callback [optional]
*/
context
:
function
(
options
,
func
,
args
)
{
if
(
isFunction
(
options
))
{
args
=
func
||
[];
func
=
options
;
options
=
undefined
;
}
return
this
.
wrap
(
options
,
func
).
apply
(
this
,
args
);
},
/*
* Wrap code within a context and returns back a new function to be executed
*
* @param {object} options A specific set of options for this context [optional]
* @param {function} func The function to be wrapped in a new context
* @return {function} The newly wrapped functions with a context
*/
wrap
:
function
(
options
,
func
)
{
var
self
=
this
;
// 1 argument has been passed, and it's not a function
// so just return it
if
(
isUndefined
(
func
)
&&
!
isFunction
(
options
))
{
return
options
;
}
// options is optional
if
(
isFunction
(
options
))
{
func
=
options
;
options
=
undefined
;
}
// At this point, we've passed along 2 arguments, and the second one
// is not a function either, so we'll just return the second argument.
if
(
!
isFunction
(
func
))
{
return
func
;
}
// We don't wanna wrap it twice!
try
{
if
(
func
.
__raven__
)
{
return
func
;
}
}
catch
(
e
)
{
// Just accessing the __raven__ prop in some Selenium environments
// can cause a "Permission denied" exception (see raven-js#495).
// Bail on wrapping and return the function as-is (defers to window.onerror).
return
func
;
}
// If this has already been wrapped in the past, return that
if
(
func
.
__raven_wrapper__
){
return
func
.
__raven_wrapper__
;
}
function
wrapped
()
{
var
args
=
[],
i
=
arguments
.
length
,
deep
=
!
options
||
options
&&
options
.
deep
!==
false
;
// Recursively wrap all of a function's arguments that are
// functions themselves.
while
(
i
--
)
args
[
i
]
=
deep
?
self
.
wrap
(
options
,
arguments
[
i
])
:
arguments
[
i
];
try
{
return
func
.
apply
(
this
,
args
);
}
catch
(
e
)
{
self
.
_ignoreNextOnError
();
self
.
captureException
(
e
,
options
);
throw
e
;
}
}
// copy over properties of the old function
for
(
var
property
in
func
)
{
if
(
hasKey
(
func
,
property
))
{
wrapped
[
property
]
=
func
[
property
];
}
}
func
.
__raven_wrapper__
=
wrapped
;
wrapped
.
prototype
=
func
.
prototype
;
// Signal that this function has been wrapped already
// for both debugging and to prevent it to being wrapped twice
wrapped
.
__raven__
=
true
;
wrapped
.
__inner__
=
func
;
return
wrapped
;
},
/*
* Uninstalls the global error handler.
*
* @return {Raven}
*/
uninstall
:
function
()
{
TraceKit
.
report
.
uninstall
();
this
.
_restoreBuiltIns
();
Error
.
stackTraceLimit
=
this
.
_originalErrorStackTraceLimit
;
this
.
_isRavenInstalled
=
false
;
return
this
;
},
/*
* Manually capture an exception and send it over to Sentry
*
* @param {error} ex An exception to be logged
* @param {object} options A specific set of options for this error [optional]
* @return {Raven}
*/
captureException
:
function
(
ex
,
options
)
{
// If not an Error is passed through, recall as a message instead
if
(
!
isError
(
ex
))
return
this
.
captureMessage
(
ex
,
options
);
// Store the raw exception object for potential debugging and introspection
this
.
_lastCapturedException
=
ex
;
// 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
{
var
stack
=
TraceKit
.
computeStackTrace
(
ex
);
this
.
_handleStackInfo
(
stack
,
options
);
}
catch
(
ex1
)
{
if
(
ex
!==
ex1
)
{
throw
ex1
;
}
}
return
this
;
},
/*
* Manually send a message to Sentry
*
* @param {string} msg A plain message to be captured in Sentry
* @param {object} options A specific set of options for this message [optional]
* @return {Raven}
*/
captureMessage
:
function
(
msg
,
options
)
{
// config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an
// early call; we'll error on the side of logging anything called before configuration since it's
// probably something you should see:
if
(
!!
this
.
_globalOptions
.
ignoreErrors
.
test
&&
this
.
_globalOptions
.
ignoreErrors
.
test
(
msg
))
{
return
;
}
// Fire away!
this
.
_send
(
objectMerge
({
message
:
msg
+
''
// Make sure it's actually a string
},
options
)
);
return
this
;
},
addPlugin
:
function
(
plugin
/*arg1, arg2, ... argN*/
)
{
var
pluginArgs
=
Array
.
prototype
.
slice
.
call
(
arguments
,
1
);
this
.
_plugins
.
push
([
plugin
,
pluginArgs
]);
if
(
this
.
_isRavenInstalled
)
{
this
.
_drainPlugins
();
}
return
this
;
},
/*
* Set/clear a user to be sent along with the payload.
*
* @param {object} user An object representing user data [optional]
* @return {Raven}
*/
setUserContext
:
function
(
user
)
{
// Intentionally do not merge here since that's an unexpected behavior.
this
.
_globalContext
.
user
=
user
;
return
this
;
},
/*
* Merge extra attributes to be sent along with the payload.
*
* @param {object} extra An object representing extra data [optional]
* @return {Raven}
*/
setExtraContext
:
function
(
extra
)
{
this
.
_mergeContext
(
'extra'
,
extra
);
return
this
;
},
/*
* Merge tags to be sent along with the payload.
*
* @param {object} tags An object representing tags [optional]
* @return {Raven}
*/
setTagsContext
:
function
(
tags
)
{
this
.
_mergeContext
(
'tags'
,
tags
);
return
this
;
},
/*
* Clear all of the context.
*
* @return {Raven}
*/
clearContext
:
function
()
{
this
.
_globalContext
=
{};
return
this
;
},
/*
* Get a copy of the current context. This cannot be mutated.
*
* @return {object} copy of context
*/
getContext
:
function
()
{
// lol javascript
return
JSON
.
parse
(
JSON
.
stringify
(
this
.
_globalContext
));
},
/*
* Set release version of application
*
* @param {string} release Typically something like a git SHA to identify version
* @return {Raven}
*/
setRelease
:
function
(
release
)
{
this
.
_globalOptions
.
release
=
release
;
return
this
;
},
/*
* Set the dataCallback option
*
* @param {function} callback The callback to run which allows the
* data blob to be mutated before sending
* @return {Raven}
*/
setDataCallback
:
function
(
callback
)
{
this
.
_globalOptions
.
dataCallback
=
callback
;
return
this
;
},
/*
* Set the shouldSendCallback option
*
* @param {function} callback The callback to run which allows
* introspecting the blob before sending
* @return {Raven}
*/
setShouldSendCallback
:
function
(
callback
)
{
this
.
_globalOptions
.
shouldSendCallback
=
callback
;
return
this
;
},
/**
* Override the default HTTP transport mechanism that transmits data
* to the Sentry server.
*
* @param {function} transport Function invoked instead of the default
* `makeRequest` handler.
*
* @return {Raven}
*/
setTransport
:
function
(
transport
)
{
this
.
_globalOptions
.
transport
=
transport
;
return
this
;
},
/*
* Get the latest raw exception that was captured by Raven.
*
* @return {error}
*/
lastException
:
function
()
{
return
this
.
_lastCapturedException
;
},
/*
* Get the last event id
*
* @return {string}
*/
lastEventId
:
function
()
{
return
this
.
_lastEventId
;
},
/*
* Determine if Raven is setup and ready to go.
*
* @return {boolean}
*/
isSetup
:
function
()
{
if
(
!
this
.
_hasJSON
)
return
false
;
// needs JSON support
if
(
!
this
.
_globalServer
)
{
if
(
!
this
.
ravenNotConfiguredError
)
{
this
.
ravenNotConfiguredError
=
true
;
this
.
_logDebug
(
'error'
,
'Error: Raven has not been configured.'
);
}
return
false
;
}
return
true
;
},
afterLoad
:
function
()
{
// TODO: remove window dependence?
// Attempt to initialize Raven on load
var
RavenConfig
=
window
.
RavenConfig
;
if
(
RavenConfig
)
{
this
.
config
(
RavenConfig
.
dsn
,
RavenConfig
.
config
).
install
();
}
},
showReportDialog
:
function
(
options
)
{
if
(
!
window
.
document
)
// doesn't work without a document (React native)
return
;
options
=
options
||
{};
var
lastEventId
=
options
.
eventId
||
this
.
lastEventId
();
if
(
!
lastEventId
)
{
throw
new
RavenConfigError
(
'Missing eventId'
);
}
var
dsn
=
options
.
dsn
||
this
.
_dsn
;
if
(
!
dsn
)
{
throw
new
RavenConfigError
(
'Missing DSN'
);
}
var
encode
=
encodeURIComponent
;
var
qs
=
''
;
qs
+=
'?eventId='
+
encode
(
lastEventId
);
qs
+=
'&dsn='
+
encode
(
dsn
);
var
user
=
options
.
user
||
this
.
_globalContext
.
user
;
if
(
user
)
{
if
(
user
.
name
)
qs
+=
'&name='
+
encode
(
user
.
name
);
if
(
user
.
email
)
qs
+=
'&email='
+
encode
(
user
.
email
);
}
var
globalServer
=
this
.
_getGlobalServer
(
this
.
_parseDSN
(
dsn
));
var
script
=
document
.
createElement
(
'script'
);
script
.
async
=
true
;
script
.
src
=
globalServer
+
'/api/embed/error-page/'
+
qs
;
(
document
.
head
||
document
.
body
).
appendChild
(
script
);
},
/**** Private functions ****/
_ignoreNextOnError
:
function
()
{
var
self
=
this
;
this
.
_ignoreOnError
+=
1
;
setTimeout
(
function
()
{
// onerror should trigger before setTimeout
self
.
_ignoreOnError
-=
1
;
});
},
_triggerEvent
:
function
(
eventType
,
options
)
{
// NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
var
evt
,
key
;
if
(
!
this
.
_hasDocument
)
return
;
options
=
options
||
{};
eventType
=
'raven'
+
eventType
.
substr
(
0
,
1
).
toUpperCase
()
+
eventType
.
substr
(
1
);
if
(
document
.
createEvent
)
{
evt
=
document
.
createEvent
(
'HTMLEvents'
);
evt
.
initEvent
(
eventType
,
true
,
true
);
}
else
{
evt
=
document
.
createEventObject
();
evt
.
eventType
=
eventType
;
}
for
(
key
in
options
)
if
(
hasKey
(
options
,
key
))
{
evt
[
key
]
=
options
[
key
];
}
if
(
document
.
createEvent
)
{
// IE9 if standards
document
.
dispatchEvent
(
evt
);
}
else
{
// IE8 regardless of Quirks or Standards
// IE9 if quirks
try
{
document
.
fireEvent
(
'on'
+
evt
.
eventType
.
toLowerCase
(),
evt
);
}
catch
(
e
)
{
// Do nothing
}
}
},
/**
* Install any queued plugins
*/
_wrapBuiltIns
:
function
()
{
var
self
=
this
;
function
fill
(
obj
,
name
,
replacement
,
noUndo
)
{
var
orig
=
obj
[
name
];
obj
[
name
]
=
replacement
(
orig
);
if
(
!
noUndo
)
{
self
.
_wrappedBuiltIns
.
push
([
obj
,
name
,
orig
]);
}
}
function
wrapTimeFn
(
orig
)
{
return
function
(
fn
,
t
)
{
// preserve arity
// Make a copy of the arguments
var
args
=
[].
slice
.
call
(
arguments
);
var
originalCallback
=
args
[
0
];
if
(
isFunction
(
originalCallback
))
{
args
[
0
]
=
self
.
wrap
(
originalCallback
);
}
// IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it
// also supports only two arguments and doesn't care what this is, so we
// can just call the original function directly.
if
(
orig
.
apply
)
{
return
orig
.
apply
(
this
,
args
);
}
else
{
return
orig
(
args
[
0
],
args
[
1
]);
}
};
}
fill
(
window
,
'setTimeout'
,
wrapTimeFn
);
fill
(
window
,
'setInterval'
,
wrapTimeFn
);
if
(
window
.
requestAnimationFrame
)
{
fill
(
window
,
'requestAnimationFrame'
,
function
(
orig
)
{
return
function
(
cb
)
{
return
orig
(
self
.
wrap
(
cb
));
};
});
}
// event targets borrowed from bugsnag-js:
// https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666
'EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload'
.
replace
(
/
\w
+/g
,
function
(
global
)
{
var
proto
=
window
[
global
]
&&
window
[
global
].
prototype
;
if
(
proto
&&
proto
.
hasOwnProperty
&&
proto
.
hasOwnProperty
(
'addEventListener'
))
{
fill
(
proto
,
'addEventListener'
,
function
(
orig
)
{
return
function
(
evt
,
fn
,
capture
,
secure
)
{
// preserve arity
try
{
if
(
fn
&&
fn
.
handleEvent
)
{
fn
.
handleEvent
=
self
.
wrap
(
fn
.
handleEvent
);
}
}
catch
(
err
)
{
// can sometimes get 'Permission denied to access property "handle Event'
}
return
orig
.
call
(
this
,
evt
,
self
.
wrap
(
fn
),
capture
,
secure
);
};
});
fill
(
proto
,
'removeEventListener'
,
function
(
orig
)
{
return
function
(
evt
,
fn
,
capture
,
secure
)
{
fn
=
fn
&&
(
fn
.
__raven_wrapper__
?
fn
.
__raven_wrapper__
:
fn
);
return
orig
.
call
(
this
,
evt
,
fn
,
capture
,
secure
);
};
});
}
});
if
(
'XMLHttpRequest'
in
window
)
{
fill
(
XMLHttpRequest
.
prototype
,
'send'
,
function
(
origSend
)
{
return
function
(
data
)
{
// preserve arity
var
xhr
=
this
;
'onreadystatechange onload onerror onprogress'
.
replace
(
/
\w
+/g
,
function
(
prop
)
{
if
(
prop
in
xhr
&&
Object
.
prototype
.
toString
.
call
(
xhr
[
prop
])
===
'[object Function]'
)
{
fill
(
xhr
,
prop
,
function
(
orig
)
{
return
self
.
wrap
(
orig
);
},
true
/* noUndo */
);
// don't track filled methods on XHR instances
}
});
return
origSend
.
apply
(
this
,
arguments
);
};
});
}
var
$
=
window
.
jQuery
||
window
.
$
;
if
(
$
&&
$
.
fn
&&
$
.
fn
.
ready
)
{
fill
(
$
.
fn
,
'ready'
,
function
(
orig
)
{
return
function
(
fn
)
{
return
orig
.
call
(
this
,
self
.
wrap
(
fn
));
};
});
}
},
_restoreBuiltIns
:
function
()
{
// restore any wrapped builtins
var
builtin
;
while
(
this
.
_wrappedBuiltIns
.
length
)
{
builtin
=
this
.
_wrappedBuiltIns
.
shift
();
var
obj
=
builtin
[
0
],
name
=
builtin
[
1
],
orig
=
builtin
[
2
];
obj
[
name
]
=
orig
;
}
},
_drainPlugins
:
function
()
{
var
self
=
this
;
// FIX ME TODO
each
(
this
.
_plugins
,
function
(
_
,
plugin
)
{
var
installer
=
plugin
[
0
];
var
args
=
plugin
[
1
];
installer
.
apply
(
self
,
[
self
].
concat
(
args
));
});
},
_parseDSN
:
function
(
str
)
{
var
m
=
dsnPattern
.
exec
(
str
),
dsn
=
{},
i
=
7
;
try
{
while
(
i
--
)
dsn
[
dsnKeys
[
i
]]
=
m
[
i
]
||
''
;
}
catch
(
e
)
{
throw
new
RavenConfigError
(
'Invalid DSN: '
+
str
);
}
if
(
dsn
.
pass
&&
!
this
.
_globalOptions
.
allowSecretKey
)
{
throw
new
RavenConfigError
(
'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key'
);
}
return
dsn
;
},
_getGlobalServer
:
function
(
uri
)
{
// assemble the endpoint from the uri pieces
var
globalServer
=
'//'
+
uri
.
host
+
(
uri
.
port
?
':'
+
uri
.
port
:
''
);
if
(
uri
.
protocol
)
{
globalServer
=
uri
.
protocol
+
':'
+
globalServer
;
}
return
globalServer
;
},
_handleOnErrorStackInfo
:
function
()
{
// if we are intentionally ignoring errors via onerror, bail out
if
(
!
this
.
_ignoreOnError
)
{
this
.
_handleStackInfo
.
apply
(
this
,
arguments
);
}
},
_handleStackInfo
:
function
(
stackInfo
,
options
)
{
var
self
=
this
;
var
frames
=
[];
if
(
stackInfo
.
stack
&&
stackInfo
.
stack
.
length
)
{
each
(
stackInfo
.
stack
,
function
(
i
,
stack
)
{
var
frame
=
self
.
_normalizeFrame
(
stack
);
if
(
frame
)
{
frames
.
push
(
frame
);
}
});
}
this
.
_triggerEvent
(
'handle'
,
{
stackInfo
:
stackInfo
,
options
:
options
});
this
.
_processException
(
stackInfo
.
name
,
stackInfo
.
message
,
stackInfo
.
url
,
stackInfo
.
lineno
,
frames
.
slice
(
0
,
this
.
_globalOptions
.
stackTraceLimit
),
options
);
},
_normalizeFrame
:
function
(
frame
)
{
if
(
!
frame
.
url
)
return
;
// normalize the frames data
var
normalized
=
{
filename
:
frame
.
url
,
lineno
:
frame
.
line
,
colno
:
frame
.
column
,
'function'
:
frame
.
func
||
'?'
},
context
=
this
.
_extractContextFromFrame
(
frame
),
i
;
if
(
context
)
{
var
keys
=
[
'pre_context'
,
'context_line'
,
'post_context'
];
i
=
3
;
while
(
i
--
)
normalized
[
keys
[
i
]]
=
context
[
i
];
}
normalized
.
in_app
=
!
(
// determine if an exception came from outside of our app
// first we check the global includePaths list.
!!
this
.
_globalOptions
.
includePaths
.
test
&&
!
this
.
_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
/raven
\.(
min
\.)?
js$/
.
test
(
normalized
.
filename
)
);
return
normalized
;
},
_extractContextFromFrame
:
function
(
frame
)
{
// immediately check if we should even attempt to parse a context
if
(
!
frame
.
context
||
!
this
.
_globalOptions
.
fetchContext
)
return
;
var
context
=
frame
.
context
,
pivot
=
~~
(
context
.
length
/
2
),
i
=
context
.
length
,
isMinified
=
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
=
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 light
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
];
},
_processException
:
function
(
type
,
message
,
fileurl
,
lineno
,
frames
,
options
)
{
var
stacktrace
,
fullMessage
;
if
(
!!
this
.
_globalOptions
.
ignoreErrors
.
test
&&
this
.
_globalOptions
.
ignoreErrors
.
test
(
message
))
return
;
message
+=
''
;
message
=
truncate
(
message
,
this
.
_globalOptions
.
maxMessageLength
);
fullMessage
=
(
type
?
type
+
': '
:
''
)
+
message
;
fullMessage
=
truncate
(
fullMessage
,
this
.
_globalOptions
.
maxMessageLength
);
if
(
frames
&&
frames
.
length
)
{
fileurl
=
frames
[
0
].
filename
||
fileurl
;
// Sentry expects frames oldest to newest
// and JS sends them as newest to oldest
frames
.
reverse
();
stacktrace
=
{
frames
:
frames
};
}
else
if
(
fileurl
)
{
stacktrace
=
{
frames
:
[{
filename
:
fileurl
,
lineno
:
lineno
,
in_app
:
true
}]
};
}
if
(
!!
this
.
_globalOptions
.
ignoreUrls
.
test
&&
this
.
_globalOptions
.
ignoreUrls
.
test
(
fileurl
))
return
;
if
(
!!
this
.
_globalOptions
.
whitelistUrls
.
test
&&
!
this
.
_globalOptions
.
whitelistUrls
.
test
(
fileurl
))
return
;
var
data
=
objectMerge
({
// sentry.interfaces.Exception
exception
:
{
values
:
[{
type
:
type
,
value
:
message
,
stacktrace
:
stacktrace
}]
},
culprit
:
fileurl
,
message
:
fullMessage
},
options
);
// Fire away!
this
.
_send
(
data
);
},
_trimPacket
:
function
(
data
)
{
// For now, we only want to truncate the two different messages
// but this could/should be expanded to just trim everything
var
max
=
this
.
_globalOptions
.
maxMessageLength
;
data
.
message
=
truncate
(
data
.
message
,
max
);
if
(
data
.
exception
)
{
var
exception
=
data
.
exception
.
values
[
0
];
exception
.
value
=
truncate
(
exception
.
value
,
max
);
}
return
data
;
},
_getHttpData
:
function
()
{
if
(
!
this
.
_hasDocument
||
!
document
.
location
||
!
document
.
location
.
href
)
{
return
;
}
var
httpData
=
{
headers
:
{
'User-Agent'
:
navigator
.
userAgent
}
};
httpData
.
url
=
document
.
location
.
href
;
if
(
document
.
referrer
)
{
httpData
.
headers
.
Referer
=
document
.
referrer
;
}
return
httpData
;
},
_send
:
function
(
data
)
{
var
self
=
this
;
var
globalOptions
=
this
.
_globalOptions
;
var
baseData
=
{
project
:
this
.
_globalProject
,
logger
:
globalOptions
.
logger
,
platform
:
'javascript'
},
httpData
=
this
.
_getHttpData
();
if
(
httpData
)
{
baseData
.
request
=
httpData
;
}
data
=
objectMerge
(
baseData
,
data
);
// Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
data
.
tags
=
objectMerge
(
objectMerge
({},
this
.
_globalContext
.
tags
),
data
.
tags
);
data
.
extra
=
objectMerge
(
objectMerge
({},
this
.
_globalContext
.
extra
),
data
.
extra
);
// Send along our own collected metadata with extra
data
.
extra
[
'session:duration'
]
=
now
()
-
this
.
_startTime
;
// If there are no tags/extra, strip the key from the payload alltogther.
if
(
isEmptyObject
(
data
.
tags
))
delete
data
.
tags
;
if
(
this
.
_globalContext
.
user
)
{
// sentry.interfaces.User
data
.
user
=
this
.
_globalContext
.
user
;
}
// Include the release if it's defined in globalOptions
if
(
globalOptions
.
release
)
data
.
release
=
globalOptions
.
release
;
// Include server_name if it's defined in globalOptions
if
(
globalOptions
.
serverName
)
data
.
server_name
=
globalOptions
.
serverName
;
if
(
isFunction
(
globalOptions
.
dataCallback
))
{
data
=
globalOptions
.
dataCallback
(
data
)
||
data
;
}
// Why??????????
if
(
!
data
||
isEmptyObject
(
data
))
{
return
;
}
// Check if the request should be filtered or not
if
(
isFunction
(
globalOptions
.
shouldSendCallback
)
&&
!
globalOptions
.
shouldSendCallback
(
data
))
{
return
;
}
// Send along an event_id if not explicitly passed.
// This event_id can be used to reference the error within Sentry itself.
// Set lastEventId after we know the error should actually be sent
this
.
_lastEventId
=
data
.
event_id
||
(
data
.
event_id
=
uuid4
());
// Try and clean up the packet before sending by truncating long values
data
=
this
.
_trimPacket
(
data
);
this
.
_logDebug
(
'debug'
,
'Raven about to send:'
,
data
);
if
(
!
this
.
isSetup
())
return
;
var
auth
=
{
sentry_version
:
'7'
,
sentry_client
:
'raven-js/'
+
this
.
VERSION
,
sentry_key
:
this
.
_globalKey
};
if
(
this
.
_globalSecret
)
{
auth
.
sentry_secret
=
this
.
_globalSecret
;
}
var
url
=
this
.
_globalEndpoint
;
(
globalOptions
.
transport
||
this
.
_makeRequest
).
call
(
this
,
{
url
:
url
,
auth
:
auth
,
data
:
data
,
options
:
globalOptions
,
onSuccess
:
function
success
()
{
self
.
_triggerEvent
(
'success'
,
{
data
:
data
,
src
:
url
});
},
onError
:
function
failure
()
{
self
.
_triggerEvent
(
'failure'
,
{
data
:
data
,
src
:
url
});
}
});
},
_makeImageRequest
:
function
(
opts
)
{
// Tack on sentry_data to auth options, which get urlencoded
opts
.
auth
.
sentry_data
=
JSON
.
stringify
(
opts
.
data
);
var
img
=
this
.
_newImage
(),
src
=
opts
.
url
+
'?'
+
urlencode
(
opts
.
auth
),
crossOrigin
=
opts
.
options
.
crossOrigin
;
if
(
crossOrigin
||
crossOrigin
===
''
)
{
img
.
crossOrigin
=
crossOrigin
;
}
img
.
onload
=
opts
.
onSuccess
;
img
.
onerror
=
img
.
onabort
=
opts
.
onError
;
img
.
src
=
src
;
},
_makeXhrRequest
:
function
(
opts
)
{
var
request
;
var
url
=
opts
.
url
;
function
handler
()
{
if
(
request
.
status
===
200
)
{
if
(
opts
.
onSuccess
)
{
opts
.
onSuccess
();
}
}
else
if
(
opts
.
onError
)
{
opts
.
onError
();
}
}
request
=
new
XMLHttpRequest
();
if
(
'withCredentials'
in
request
)
{
request
.
onreadystatechange
=
function
()
{
if
(
request
.
readyState
!==
4
)
{
return
;
}
handler
();
};
}
else
{
request
=
new
XDomainRequest
();
// xdomainrequest cannot go http -> https (or vice versa),
// so always use protocol relative
url
=
url
.
replace
(
/^https
?
:/
,
''
);
// onreadystatechange not supported by XDomainRequest
request
.
onload
=
handler
;
}
// NOTE: auth is intentionally sent as part of query string (NOT as custom
// HTTP header) so as to avoid preflight CORS requests
request
.
open
(
'POST'
,
url
+
'?'
+
urlencode
(
opts
.
auth
));
request
.
send
(
JSON
.
stringify
(
opts
.
data
));
},
_makeRequest
:
function
(
opts
)
{
var
hasCORS
=
'withCredentials'
in
new
XMLHttpRequest
()
||
typeof
XDomainRequest
!==
'undefined'
;
return
(
hasCORS
?
this
.
_makeXhrRequest
:
this
.
_makeImageRequest
)(
opts
);
},
// Note: this is shitty, but I can't figure out how to get
// sinon to stub document.createElement without breaking everything
// so this wrapper is just so I can stub it for tests.
_newImage
:
function
()
{
return
document
.
createElement
(
'img'
);
},
_logDebug
:
function
(
level
)
{
if
(
this
.
_originalConsoleMethods
[
level
]
&&
this
.
debug
)
{
// In IE<10 console methods do not have their own 'apply' method
Function
.
prototype
.
apply
.
call
(
this
.
_originalConsoleMethods
[
level
],
this
.
_originalConsole
,
[].
slice
.
call
(
arguments
,
1
)
);
}
},
_mergeContext
:
function
(
key
,
context
)
{
if
(
isUndefined
(
context
))
{
delete
this
.
_globalContext
[
key
];
}
else
{
this
.
_globalContext
[
key
]
=
objectMerge
(
this
.
_globalContext
[
key
]
||
{},
context
);
}
}
};
// Deprecations
Raven
.
prototype
.
setUser
=
Raven
.
prototype
.
setUserContext
;
Raven
.
prototype
.
setReleaseContext
=
Raven
.
prototype
.
setRelease
;
module
.
exports
=
Raven
;
},{
"1"
:
1
,
"4"
:
4
,
"5"
:
5
}],
3
:[
function
(
_dereq_
,
module
,
exports
){
/**
* Enforces a single instance of the Raven client, and the
* main entry point for Raven. If you are a consumer of the
* Raven library, you SHOULD load this file (vs raven.js).
**/
'use strict'
;
var
RavenConstructor
=
_dereq_
(
2
);
var
_Raven
=
window
.
Raven
;
var
Raven
=
new
RavenConstructor
();
/*
* Allow multiple versions of Raven to be installed.
* Strip Raven from the global context and returns the instance.
*
* @return {Raven}
*/
Raven
.
noConflict
=
function
()
{
window
.
Raven
=
_Raven
;
return
Raven
;
};
Raven
.
afterLoad
();
module
.
exports
=
Raven
;
},{
"2"
:
2
}],
4
:[
function
(
_dereq_
,
module
,
exports
){
'use strict'
;
var
objectPrototype
=
Object
.
prototype
;
function
isUndefined
(
what
)
{
return
what
===
void
0
;
}
function
isFunction
(
what
)
{
return
typeof
what
===
'function'
;
}
function
isString
(
what
)
{
return
objectPrototype
.
toString
.
call
(
what
)
===
'[object String]'
;
}
function
isObject
(
what
)
{
return
typeof
what
===
'object'
&&
what
!==
null
;
}
function
isEmptyObject
(
what
)
{
for
(
var
_
in
what
)
return
false
;
// eslint-disable-line guard-for-in, no-unused-vars
return
true
;
}
// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560
// with some tiny modifications
function
isError
(
what
)
{
var
toString
=
objectPrototype
.
toString
.
call
(
what
);
return
isObject
(
what
)
&&
toString
===
'[object Error]'
||
toString
===
'[object Exception]'
||
// Firefox NS_ERROR_FAILURE Exceptions
what
instanceof
Error
;
}
function
each
(
obj
,
callback
)
{
var
i
,
j
;
if
(
isUndefined
(
obj
.
length
))
{
for
(
i
in
obj
)
{
if
(
hasKey
(
obj
,
i
))
{
callback
.
call
(
null
,
i
,
obj
[
i
]);
}
}
}
else
{
j
=
obj
.
length
;
if
(
j
)
{
for
(
i
=
0
;
i
<
j
;
i
++
)
{
callback
.
call
(
null
,
i
,
obj
[
i
]);
}
}
}
}
function
objectMerge
(
obj1
,
obj2
)
{
if
(
!
obj2
)
{
return
obj1
;
}
each
(
obj2
,
function
(
key
,
value
){
obj1
[
key
]
=
value
;
});
return
obj1
;
}
function
truncate
(
str
,
max
)
{
return
!
max
||
str
.
length
<=
max
?
str
:
str
.
substr
(
0
,
max
)
+
'
\
u2026'
;
}
/**
* hasKey, a better form of hasOwnProperty
* Example: hasKey(MainHostObject, property) === true/false
*
* @param {Object} host object to check property
* @param {string} key to check
*/
function
hasKey
(
object
,
key
)
{
return
objectPrototype
.
hasOwnProperty
.
call
(
object
,
key
);
}
function
joinRegExp
(
patterns
)
{
// Combine an array of regular expressions and strings into one large regexp
// Be mad.
var
sources
=
[],
i
=
0
,
len
=
patterns
.
length
,
pattern
;
for
(;
i
<
len
;
i
++
)
{
pattern
=
patterns
[
i
];
if
(
isString
(
pattern
))
{
// If it's a string, we need to escape it
// Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
sources
.
push
(
pattern
.
replace
(
/
([
.*+?^=!:${}()|
\[\]\/\\])
/g
,
'
\\
$1'
));
}
else
if
(
pattern
&&
pattern
.
source
)
{
// If it's a regexp already, we want to extract the source
sources
.
push
(
pattern
.
source
);
}
// Intentionally skip other cases
}
return
new
RegExp
(
sources
.
join
(
'|'
),
'i'
);
}
function
urlencode
(
o
)
{
var
pairs
=
[];
each
(
o
,
function
(
key
,
value
)
{
pairs
.
push
(
encodeURIComponent
(
key
)
+
'='
+
encodeURIComponent
(
value
));
});
return
pairs
.
join
(
'&'
);
}
function
uuid4
()
{
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
);
});
}
}
module
.
exports
=
{
isUndefined
:
isUndefined
,
isFunction
:
isFunction
,
isString
:
isString
,
isObject
:
isObject
,
isEmptyObject
:
isEmptyObject
,
isError
:
isError
,
each
:
each
,
objectMerge
:
objectMerge
,
truncate
:
truncate
,
hasKey
:
hasKey
,
joinRegExp
:
joinRegExp
,
urlencode
:
urlencode
,
uuid4
:
uuid4
};
},{}],
5
:[
function
(
_dereq_
,
module
,
exports
){
'use strict'
;
var
utils
=
_dereq_
(
4
);
var
hasKey
=
utils
.
hasKey
;
var
isString
=
utils
.
isString
;
var
isUndefined
=
utils
.
isUndefined
;
/*
TraceKit - Cross brower stack traces - github.com/occ/TraceKit
MIT license
*/
var
TraceKit
=
{
remoteFetching
:
false
,
collectWindowErrors
:
true
,
// 3 lines before, the offending line, 3 lines after
linesOfContext
:
7
,
debug
:
false
};
// global reference to slice
var
_slice
=
[].
slice
;
var
UNKNOWN_FUNCTION
=
'?'
;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
var
ERROR_TYPES_RE
=
/^
(?:
Uncaught
)?((?:
Eval|Internal|Range|Reference|Syntax|Type|URI
)
Error
)\:
?(
.*
)
$/
;
function
getLocationHref
()
{
if
(
typeof
document
===
'undefined'
)
return
''
;
return
document
.
location
.
href
;
}
/**
* 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
=
(
function
reportModuleWrapper
()
{
var
handlers
=
[],
lastArgs
=
null
,
lastException
=
null
,
lastExceptionStack
=
null
;
/**
* Add a crash handler.
* @param {Function} handler
*/
function
subscribe
(
handler
)
{
installGlobalHandler
();
handlers
.
push
(
handler
);
}
/**
* Remove a crash handler.
* @param {Function} handler
*/
function
unsubscribe
(
handler
)
{
for
(
var
i
=
handlers
.
length
-
1
;
i
>=
0
;
--
i
)
{
if
(
handlers
[
i
]
===
handler
)
{
handlers
.
splice
(
i
,
1
);
}
}
}
/**
* Remove all crash handlers.
*/
function
unsubscribeAll
()
{
uninstallGlobalHandler
();
handlers
=
[];
}
/**
* Dispatch stack information to all handlers.
* @param {Object.<string, *>} stack
*/
function
notifyHandlers
(
stack
,
isWindowError
)
{
var
exception
=
null
;
if
(
isWindowError
&&
!
TraceKit
.
collectWindowErrors
)
{
return
;
}
for
(
var
i
in
handlers
)
{
if
(
hasKey
(
handlers
,
i
))
{
try
{
handlers
[
i
].
apply
(
null
,
[
stack
].
concat
(
_slice
.
call
(
arguments
,
2
)));
}
catch
(
inner
)
{
exception
=
inner
;
}
}
}
if
(
exception
)
{
throw
exception
;
}
}
var
_oldOnerrorHandler
,
_onErrorHandlerInstalled
;
/**
* 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.
* @param {?(number|string)} colNo The column number at which the error
* occurred.
* @param {?Error} ex The actual Error object.
*/
function
traceKitWindowOnError
(
message
,
url
,
lineNo
,
colNo
,
ex
)
{
var
stack
=
null
;
if
(
lastExceptionStack
)
{
TraceKit
.
computeStackTrace
.
augmentStackTraceWithInitialElement
(
lastExceptionStack
,
url
,
lineNo
,
message
);
processLastException
();
}
else
if
(
ex
)
{
// New chrome and blink send along a real error object
// Let's just report that like a normal error.
// See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror
stack
=
TraceKit
.
computeStackTrace
(
ex
);
notifyHandlers
(
stack
,
true
);
}
else
{
var
location
=
{
'url'
:
url
,
'line'
:
lineNo
,
'column'
:
colNo
};
location
.
func
=
TraceKit
.
computeStackTrace
.
guessFunctionName
(
location
.
url
,
location
.
line
);
location
.
context
=
TraceKit
.
computeStackTrace
.
gatherContext
(
location
.
url
,
location
.
line
);
var
name
=
undefined
;
var
msg
=
message
;
// must be new var or will modify original `arguments`
var
groups
;
if
(
isString
(
message
))
{
var
groups
=
message
.
match
(
ERROR_TYPES_RE
);
if
(
groups
)
{
name
=
groups
[
1
];
msg
=
groups
[
2
];
}
}
stack
=
{
'name'
:
name
,
'message'
:
msg
,
'url'
:
getLocationHref
(),
'stack'
:
[
location
]
};
notifyHandlers
(
stack
,
true
);
}
if
(
_oldOnerrorHandler
)
{
return
_oldOnerrorHandler
.
apply
(
this
,
arguments
);
}
return
false
;
}
function
installGlobalHandler
()
{
if
(
_onErrorHandlerInstalled
)
{
return
;
}
_oldOnerrorHandler
=
window
.
onerror
;
window
.
onerror
=
traceKitWindowOnError
;
_onErrorHandlerInstalled
=
true
;
}
function
uninstallGlobalHandler
()
{
if
(
!
_onErrorHandlerInstalled
)
{
return
;
}
window
.
onerror
=
_oldOnerrorHandler
;
_onErrorHandlerInstalled
=
false
;
_oldOnerrorHandler
=
undefined
;
}
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
* @param {?boolean} rethrow If false, do not re-throw the exception.
* Only used for window.onerror to not cause an infinite loop of
* rethrowing.
*/
function
report
(
ex
,
rethrow
)
{
var
args
=
_slice
.
call
(
arguments
,
1
);
if
(
lastExceptionStack
)
{
if
(
lastException
===
ex
)
{
return
;
// already caught by an inner catch block, ignore
}
else
{
processLastException
();
}
}
var
stack
=
TraceKit
.
computeStackTrace
(
ex
);
lastExceptionStack
=
stack
;
lastException
=
ex
;
lastArgs
=
args
;
// 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
===
ex
)
{
processLastException
();
}
},
(
stack
.
incomplete
?
2000
:
0
));
if
(
rethrow
!==
false
)
{
throw
ex
;
// re-throw to propagate to the top level (and cause window.onerror)
}
}
report
.
subscribe
=
subscribe
;
report
.
unsubscribe
=
unsubscribe
;
report
.
uninstall
=
unsubscribeAll
;
return
report
;
}());
/**
* TraceKit.computeStackTrace: cross-browser stack traces in JavaScript
*
* Syntax:
* 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#
*
* 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).
*
*/
TraceKit
.
computeStackTrace
=
(
function
computeStackTraceWrapper
()
{
var
sourceCache
=
{};
/**
* 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 remoteFetching is on.
return
''
;
}
try
{
var
getXHR
=
function
()
{
try
{
return
new
window
.
XMLHttpRequest
();
}
catch
(
e
)
{
// explicitly bubble up the exception if not found
return
new
window
.
ActiveXObject
(
'Microsoft.XMLHTTP'
);
}
};
var
request
=
getXHR
();
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
(
!
isString
(
url
))
return
[];
if
(
!
hasKey
(
sourceCache
,
url
))
{
// URL needs to be able to fetched within the acceptable domain. Otherwise,
// cross-domain errors will be triggered.
var
source
=
''
;
var
domain
=
''
;
try
{
domain
=
document
.
domain
;
}
catch
(
e
)
{}
if
(
url
.
indexOf
(
domain
)
!==
-
1
)
{
source
=
loadSource
(
url
);
}
sourceCache
[
url
]
=
source
?
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
=
/function
([^
(
]
*
)\(([^
)
]
*
)\)
/
,
reGuessFunction
=
/
[
'"
]?([
0-9A-Za-z$_
]
+
)[
'"
]?\s
*
[
:=
]\s
*
(
function|eval|new Function
)
/
,
line
=
''
,
maxLines
=
10
,
source
=
getSource
(
url
),
m
;
if
(
!
source
.
length
)
{
return
UNKNOWN_FUNCTION
;
}
// Walk backwards from the first line in the function until we find the line which
// matches the pattern above, which is the function definition
for
(
var
i
=
0
;
i
<
maxLines
;
++
i
)
{
line
=
source
[
lineNo
-
i
]
+
line
;
if
(
!
isUndefined
(
line
))
{
if
((
m
=
reGuessFunction
.
exec
(
line
)))
{
return
m
[
1
];
}
else
if
((
m
=
reFunctionArgNames
.
exec
(
line
)))
{
return
m
[
1
];
}
}
}
return
UNKNOWN_FUNCTION
;
}
/**
* 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
=
getSource
(
url
);
if
(
!
source
.
length
)
{
return
null
;
}
var
context
=
[],
// linesBefore & linesAfter are inclusive with the offending line.
// if linesOfContext is even, there will be one extra line
// *before* the offending line.
linesBefore
=
Math
.
floor
(
TraceKit
.
linesOfContext
/
2
),
// Add one extra line if linesOfContext is odd
linesAfter
=
linesBefore
+
(
TraceKit
.
linesOfContext
%
2
),
start
=
Math
.
max
(
0
,
line
-
linesBefore
-
1
),
end
=
Math
.
min
(
source
.
length
,
line
+
linesAfter
-
1
);
line
-=
1
;
// convert to 0-based index
for
(
var
i
=
start
;
i
<
end
;
++
i
)
{
if
(
!
isUndefined
(
source
[
i
]))
{
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
(
'"'
,
'(?:"|")'
).
replace
(
/
\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
=
0
,
j
=
urls
.
length
;
i
<
j
;
++
i
)
{
// console.log('searching', urls[i]);
if
((
source
=
getSource
(
urls
[
i
])).
length
)
{
source
=
source
.
join
(
'
\
n'
);
if
((
m
=
re
.
exec
(
source
)))
{
// console.log('Found function in ' + urls[i]);
return
{
'url'
:
urls
[
i
],
'line'
:
source
.
substring
(
0
,
m
.
index
).
split
(
'
\
n'
).
length
,
'column'
:
m
.
index
-
source
.
lastIndexOf
(
'
\
n'
,
m
.
index
)
-
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
=
getSource
(
url
),
re
=
new
RegExp
(
'
\\
b'
+
escapeRegExp
(
fragment
)
+
'
\\
b'
),
m
;
line
-=
1
;
if
(
source
&&
source
.
length
>
line
&&
(
m
=
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
)
{
if
(
typeof
document
===
'undefined'
)
return
;
var
urls
=
[
window
.
location
.
href
],
scripts
=
document
.
getElementsByTagName
(
'script'
),
body
,
code
=
''
+
func
,
codeRE
=
/^function
(?:\s
+
([\w
$
]
+
))?\s
*
\(([\w\s
,
]
*
)\)\s
*
\{\s
*
(\S[\s\S]
*
\S)\s
*
\}\s
*$/
,
eventRE
=
/^function on
([\w
$
]
+
)\s
*
\(
event
\)\s
*
\{\s
*
(\S[\s\S]
*
\S)\s
*
\}\s
*$/
,
re
,
parts
,
result
;
for
(
var
i
=
0
;
i
<
scripts
.
length
;
++
i
)
{
var
script
=
scripts
[
i
];
if
(
script
.
src
)
{
urls
.
push
(
script
.
src
);
}
}
if
(
!
(
parts
=
codeRE
.
exec
(
code
)))
{
re
=
new
RegExp
(
escapeRegExp
(
code
).
replace
(
/
\s
+/g
,
'
\\
s+'
));
}
// not sure if this is really necessary, but I don’t have a test
// corpus large enough to confirm that and it was in the original.
else
{
var
name
=
parts
[
1
]
?
'
\\
s+'
+
parts
[
1
]
:
''
,
args
=
parts
[
2
].
split
(
','
).
join
(
'
\\
s*,
\\
s*'
);
body
=
escapeRegExp
(
parts
[
3
]).
replace
(
/;$/
,
';?'
);
// semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+');
re
=
new
RegExp
(
'function'
+
name
+
'
\\
s*
\\
(
\\
s*'
+
args
+
'
\\
s*
\\
)
\\
s*{
\\
s*'
+
body
+
'
\\
s*}'
);
}
// look for a normal function definition
if
((
result
=
findSourceInUrls
(
re
,
urls
)))
{
return
result
;
}
// look for an old-school event handler function
if
((
parts
=
eventRE
.
exec
(
code
)))
{
var
event
=
parts
[
1
];
body
=
escapeCodeAsRegExpForMatchingInsideHTML
(
parts
[
2
]);
// look for a function defined in HTML as an onXXX handler
re
=
new
RegExp
(
'on'
+
event
+
'=[
\\
\'
"]
\\
s*'
+
body
+
'
\\
s*[
\\
\'
"]'
,
'i'
);
if
((
result
=
findSourceInUrls
(
re
,
urls
[
0
])))
{
return
result
;
}
// look for ???
re
=
new
RegExp
(
body
);
if
((
result
=
findSourceInUrls
(
re
,
urls
)))
{
return
result
;
}
}
return
null
;
}
// Contents of Exception in various browsers.
//
// SAFARI:
// ex.message = Can't find variable: qq
// ex.line = 59
// ex.sourceId = 580238192
// ex.sourceURL = http://...
// ex.expressionBeginOffset = 96
// ex.expressionCaretOffset = 98
// ex.expressionEndOffset = 98
// ex.name = ReferenceError
//
// FIREFOX:
// ex.message = qq is not defined
// ex.fileName = http://...
// ex.lineNumber = 59
// ex.columnNumber = 69
// ex.stack = ...stack trace... (see the example below)
// ex.name = ReferenceError
//
// CHROME:
// ex.message = qq is not defined
// ex.name = ReferenceError
// ex.type = not_defined
// ex.arguments = ['aa']
// ex.stack = ...stack trace...
//
// INTERNET EXPLORER:
// ex.message = ...
// ex.name = ReferenceError
//
// OPERA:
// ex.message = ...message... (see the example below)
// ex.name = ReferenceError
// ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message)
// ex.stacktrace = 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
(
isUndefined
(
ex
.
stack
)
||
!
ex
.
stack
)
return
;
var
chrome
=
/^
\s
*at
(
.*
?)
?\(((?:
file|https
?
|blob|chrome-extension|native|eval|<anonymous>
)
.*
?)(?:
:
(\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
,
element
,
reference
=
/^
(
.*
)
is undefined$/
.
exec
(
ex
.
message
);
for
(
var
i
=
0
,
j
=
lines
.
length
;
i
<
j
;
++
i
)
{
if
((
parts
=
chrome
.
exec
(
lines
[
i
])))
{
var
isNative
=
parts
[
2
]
&&
parts
[
2
].
indexOf
(
'native'
)
!==
-
1
;
element
=
{
'url'
:
!
isNative
?
parts
[
2
]
:
null
,
'func'
:
parts
[
1
]
||
UNKNOWN_FUNCTION
,
'args'
:
isNative
?
[
parts
[
2
]]
:
[],
'line'
:
parts
[
3
]
?
+
parts
[
3
]
:
null
,
'column'
:
parts
[
4
]
?
+
parts
[
4
]
:
null
};
}
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
=
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
;
}
if
(
!
element
.
func
&&
element
.
line
)
{
element
.
func
=
guessFunctionName
(
element
.
url
,
element
.
line
);
}
if
(
element
.
line
)
{
element
.
context
=
gatherContext
(
element
.
url
,
element
.
line
);
}
stack
.
push
(
element
);
}
if
(
!
stack
.
length
)
{
return
null
;
}
if
(
stack
[
0
].
line
&&
!
stack
[
0
].
column
&&
reference
)
{
stack
[
0
].
column
=
findSourceInLine
(
reference
[
1
],
stack
[
0
].
url
,
stack
[
0
].
line
);
}
else
if
(
!
stack
[
0
].
column
&&
!
isUndefined
(
ex
.
columnNumber
))
{
// FireFox uses this awesome columnNumber property for its top frame
// Also note, Firefox's column number is 0-based and everything else expects 1-based,
// so adding 1
stack
[
0
].
column
=
ex
.
columnNumber
+
1
;
}
return
{
'name'
:
ex
.
name
,
'message'
:
ex
.
message
,
'url'
:
getLocationHref
(),
'stack'
:
stack
};
}
/**
* 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
=
ex
.
stacktrace
;
if
(
isUndefined
(
ex
.
stacktrace
)
||
!
ex
.
stacktrace
)
return
;
var
opera10Regex
=
/ line
(\d
+
)
.*script
(?:
in
)?(\S
+
)(?:
: in function
(\S
+
))?
$/i
,
opera11Regex
=
/ line
(\d
+
)
, column
(\d
+
)\s
*
(?:
in
(?:
<anonymous function:
([^
>
]
+
)
>|
([^\)]
+
))\((
.*
)\))?
in
(
.*
)
:
\s
*$/i
,
lines
=
stacktrace
.
split
(
'
\
n'
),
stack
=
[],
parts
;
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
(
','
)
:
[]
};
}
if
(
element
)
{
if
(
!
element
.
func
&&
element
.
line
)
{
element
.
func
=
guessFunctionName
(
element
.
url
,
element
.
line
);
}
if
(
element
.
line
)
{
try
{
element
.
context
=
gatherContext
(
element
.
url
,
element
.
line
);
}
catch
(
exc
)
{}
}
if
(
!
element
.
context
)
{
element
.
context
=
[
lines
[
line
+
1
]];
}
stack
.
push
(
element
);
}
}
if
(
!
stack
.
length
)
{
return
null
;
}
return
{
'name'
:
ex
.
name
,
'message'
:
ex
.
message
,
'url'
:
getLocationHref
(),
'stack'
:
stack
};
}
/**
* 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 example is:
//
// Statement on line 3: Undefined variable: undefinedFunc
// Backtrace:
// Line 3 of linked script file://localhost/Users/andreyvit/Projects/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.report(ex); }
// ...
var
lines
=
ex
.
message
.
split
(
'
\
n'
);
if
(
lines
.
length
<
4
)
{
return
null
;
}
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'
),
inlineScriptBlocks
=
[],
parts
;
for
(
var
s
in
scripts
)
{
if
(
hasKey
(
scripts
,
s
)
&&
!
scripts
[
s
].
src
)
{
inlineScriptBlocks
.
push
(
scripts
[
s
]);
}
}
for
(
var
line
=
2
;
line
<
lines
.
length
;
line
+=
2
)
{
var
item
=
null
;
if
((
parts
=
lineRE1
.
exec
(
lines
[
line
])))
{
item
=
{
'url'
:
parts
[
2
],
'func'
:
parts
[
3
],
'args'
:
[],
'line'
:
+
parts
[
1
],
'column'
:
null
};
}
else
if
((
parts
=
lineRE2
.
exec
(
lines
[
line
])))
{
item
=
{
'url'
:
parts
[
3
],
'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 <SCRIPT> block
var
script
=
inlineScriptBlocks
[
parts
[
2
]
-
1
];
if
(
script
)
{
var
source
=
getSource
(
item
.
url
);
if
(
source
)
{
source
=
source
.
join
(
'
\
n'
);
var
pos
=
source
.
indexOf
(
script
.
innerText
);
if
(
pos
>=
0
)
{
item
.
line
=
relativeLine
+
source
.
substring
(
0
,
pos
).
split
(
'
\
n'
).
length
;
}
}
}
}
else
if
((
parts
=
lineRE3
.
exec
(
lines
[
line
])))
{
var
url
=
window
.
location
.
href
.
replace
(
/#.*$/
,
''
);
var
re
=
new
RegExp
(
escapeCodeAsRegExpForMatchingInsideHTML
(
lines
[
line
+
1
]));
var
src
=
findSourceInUrls
(
re
,
[
url
]);
item
=
{
'url'
:
url
,
'func'
:
''
,
'args'
:
[],
'line'
:
src
?
src
.
line
:
parts
[
1
],
'column'
:
null
};
}
if
(
item
)
{
if
(
!
item
.
func
)
{
item
.
func
=
guessFunctionName
(
item
.
url
,
item
.
line
);
}
var
context
=
gatherContext
(
item
.
url
,
item
.
line
);
var
midline
=
(
context
?
context
[
Math
.
floor
(
context
.
length
/
2
)]
:
null
);
if
(
context
&&
midline
.
replace
(
/^
\s
*/
,
''
)
===
lines
[
line
+
1
].
replace
(
/^
\s
*/
,
''
))
{
item
.
context
=
context
;
}
else
{
// if (context) alert("Context mismatch. Correct midline:\n" + lines[i+1] + "\n\nMidline:\n" + midline + "\n\nContext:\n" + context.join("\n") + "\n\nURL:\n" + item.url);
item
.
context
=
[
lines
[
line
+
1
]];
}
stack
.
push
(
item
);
}
}
if
(
!
stack
.
length
)
{
return
null
;
// could not parse multiline exception message as Opera stack trace
}
return
{
'name'
:
ex
.
name
,
'message'
:
lines
[
0
],
'url'
:
getLocationHref
(),
'stack'
:
stack
};
}
/**
* 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=} 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
,
message
)
{
var
initial
=
{
'url'
:
url
,
'line'
:
lineNo
};
if
(
initial
.
url
&&
initial
.
line
)
{
stackInfo
.
incomplete
=
false
;
if
(
!
initial
.
func
)
{
initial
.
func
=
guessFunctionName
(
initial
.
url
,
initial
.
line
);
}
if
(
!
initial
.
context
)
{
initial
.
context
=
gatherContext
(
initial
.
url
,
initial
.
line
);
}
var
reference
=
/ '
([^
'
]
+
)
' /
.
exec
(
message
);
if
(
reference
)
{
initial
.
column
=
findSourceInLine
(
reference
[
1
],
initial
.
url
,
initial
.
line
);
}
if
(
stackInfo
.
stack
.
length
>
0
)
{
if
(
stackInfo
.
stack
[
0
].
url
===
initial
.
url
)
{
if
(
stackInfo
.
stack
[
0
].
line
===
initial
.
line
)
{
return
false
;
// already in stack trace
}
else
if
(
!
stackInfo
.
stack
[
0
].
line
&&
stackInfo
.
stack
[
0
].
func
===
initial
.
func
)
{
stackInfo
.
stack
[
0
].
line
=
initial
.
line
;
stackInfo
.
stack
[
0
].
context
=
initial
.
context
;
return
false
;
}
}
}
stackInfo
.
stack
.
unshift
(
initial
);
stackInfo
.
partial
=
true
;
return
true
;
}
else
{
stackInfo
.
incomplete
=
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
=
/function
\s
+
([
_$a-zA-Z
\x
A0-
\u
FFFF
][
_$a-zA-Z0-9
\x
A0-
\u
FFFF
]
*
)?\s
*
\(
/i
,
stack
=
[],
funcs
=
{},
recursion
=
false
,
parts
,
item
,
source
;
for
(
var
curr
=
computeStackTraceByWalkingCallerChain
.
caller
;
curr
&&
!
recursion
;
curr
=
curr
.
caller
)
{
if
(
curr
===
computeStackTrace
||
curr
===
TraceKit
.
report
)
{
// console.log('skipping internal function');
continue
;
}
item
=
{
'url'
:
null
,
'func'
:
UNKNOWN_FUNCTION
,
'line'
:
null
,
'column'
:
null
};
if
(
curr
.
name
)
{
item
.
func
=
curr
.
name
;
}
else
if
((
parts
=
functionName
.
exec
(
curr
.
toString
())))
{
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
;
if
(
item
.
func
===
UNKNOWN_FUNCTION
)
{
item
.
func
=
guessFunctionName
(
item
.
url
,
item
.
line
);
}
var
reference
=
/ '
([^
'
]
+
)
' /
.
exec
(
ex
.
message
||
ex
.
description
);
if
(
reference
)
{
item
.
column
=
findSourceInLine
(
reference
[
1
],
source
.
url
,
source
.
line
);
}
}
if
(
funcs
[
''
+
curr
])
{
recursion
=
true
;
}
else
{
funcs
[
''
+
curr
]
=
true
;
}
stack
.
push
(
item
);
}
if
(
depth
)
{
// console.log('depth is ' + depth);
// console.log('stack is ' + stack.length);
stack
.
splice
(
0
,
depth
);
}
var
result
=
{
'name'
:
ex
.
name
,
'message'
:
ex
.
message
,
'url'
:
getLocationHref
(),
'stack'
:
stack
};
augmentStackTraceWithInitialElement
(
result
,
ex
.
sourceURL
||
ex
.
fileName
,
ex
.
line
||
ex
.
lineNumber
,
ex
.
message
||
ex
.
description
);
return
result
;
}
/**
* Computes a stack trace for an exception.
* @param {Error} ex
* @param {(string|number)=} depth
*/
function
computeStackTrace
(
ex
,
depth
)
{
var
stack
=
null
;
depth
=
(
depth
==
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
=
computeStackTraceFromStacktraceProp
(
ex
);
if
(
stack
)
{
return
stack
;
}
}
catch
(
e
)
{
if
(
TraceKit
.
debug
)
{
throw
e
;
}
}
try
{
stack
=
computeStackTraceFromStackProp
(
ex
);
if
(
stack
)
{
return
stack
;
}
}
catch
(
e
)
{
if
(
TraceKit
.
debug
)
{
throw
e
;
}
}
try
{
stack
=
computeStackTraceFromOperaMultiLineMessage
(
ex
);
if
(
stack
)
{
return
stack
;
}
}
catch
(
e
)
{
if
(
TraceKit
.
debug
)
{
throw
e
;
}
}
try
{
stack
=
computeStackTraceByWalkingCallerChain
(
ex
,
depth
+
1
);
if
(
stack
)
{
return
stack
;
}
}
catch
(
e
)
{
if
(
TraceKit
.
debug
)
{
throw
e
;
}
}
return
{
'name'
:
ex
.
name
,
'message'
:
ex
.
message
,
'url'
:
getLocationHref
()
};
}
computeStackTrace
.
augmentStackTraceWithInitialElement
=
augmentStackTraceWithInitialElement
;
computeStackTrace
.
computeStackTraceFromStackProp
=
computeStackTraceFromStackProp
;
computeStackTrace
.
guessFunctionName
=
guessFunctionName
;
computeStackTrace
.
gatherContext
=
gatherContext
;
return
computeStackTrace
;
}());
module
.
exports
=
TraceKit
;
},{
"4"
:
4
}]},{},[
3
])(
3
)
});
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment