-
Notifications
You must be signed in to change notification settings - Fork 1
/
js-agent.js
2267 lines (1898 loc) · 66.3 KB
/
js-agent.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// modules are defined as an array
// [ module function, map of requires ]
//
// map of requireuires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the require for previous bundles
(function (modules, cache, entry) { // eslint-disable-line no-extra-parens
// Save the require from previous bundle to this closure if any
var previousRequire = typeof __nr_require === 'function' && __nr_require
function newRequire (name, jumped) {
if (!cache[name]) {
if (!modules[name]) {
// if we cannot find the the module within our internal map or
// cache jump to the current global require ie. the last bundle
// that was added to the page.
var currentRequire = typeof __nr_require === 'function' && __nr_require
if (!jumped && currentRequire) return currentRequire(name, true)
// If there are other bundles on this page the require from the
// previous one is saved to 'previousRequire'. Repeat this as
// many times as there are bundles until the module is found or
// we exhaust the require chain.
if (previousRequire) return previousRequire(name, true)
throw new Error("Cannot find module '" + name + "'")
}
var m = cache[name] = {exports: {}}
modules[name][0].call(m.exports, function (x) {
var id = modules[name][1][x]
return newRequire(id || x)
}, m, m.exports)
}
return cache[name].exports
}
for (var i = 0; i < entry.length; i++) newRequire(entry[i])
// Override the current require with this new one
return newRequire
})
({1:[function(require,module,exports){
// Safely add an event listener to window in any browser
module.exports = function (sType, callback) {
if ('addEventListener' in window) {
return addEventListener(sType, callback, false)
} else if ('attachEvent' in window) {
return attachEvent('on' + sType, callback)
}
}
},{}],2:[function(require,module,exports){
// This product includes Apache 2.0 licensed source derived from 'episodes'
// by Steve Sounders. Repository: http://code.google.com/p/episodes/
//
// Copyright 2010 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// See the source code here:
// http://code.google.com/p/episodes/
//
// This product includes MIT licensed source generated from 'Browserify'
// by James Halliday. Repository: https://github.com/substack/node-browserify.
//
// This product includes MIT licensed source derived from 'TraceKit'
// by Onur Cakmak. Repository: https://github.com/occ/TraceKit
//
// TraceKit - Cross brower stack traces - github.com/occ/TraceKit
//
// Copyright (c) 2013 Onur Can Cakmak [email protected] and all TraceKit
// contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// All other components of this product are
// Copyright (c) 2010-2013 New Relic, Inc. All rights reserved.
//
// Certain inventions disclosed in this file may be claimed within
// patents owned or patent applications filed by New Relic, Inc. or third
// parties.
//
// Subject to the terms of this notice, New Relic grants you a
// nonexclusive, nontransferable license, without the right to
// sublicense, to (a) install and execute one copy of these files on any
// number of workstations owned or controlled by you and (b) distribute
// verbatim copies of these files to third parties. As a condition to the
// foregoing grant, you must provide this notice along with each copy you
// distribute and you must not remove, alter, or obscure this notice. All
// other use, reproduction, modification, distribution, or other
// exploitation of these files is strictly prohibited, except as may be set
// forth in a separate written license agreement between you and New
// Relic. The terms of any such license agreement will control over this
// notice. The license stated above will be automatically terminated and
// revoked if you exceed its scope or violate any of the terms of this
// notice.
//
// This License does not grant permission to use the trade names,
// trademarks, service marks, or product names of New Relic, except as
// required for reasonable and customary use in describing the origin of
// this file and reproducing the content of this notice. You may not
// mark or brand this file with any trade name, trademarks, service
// marks, or product names other than the original brand (if any)
// provided by New Relic.
//
// Unless otherwise expressly agreed by New Relic in a separate written
// license agreement, these files are provided AS IS, WITHOUT WARRANTY OF
// ANY KIND, including without any implied warranties of MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE, or NON-INFRINGEMENT. As a
// condition to your use of these files, you are solely responsible for
// such use. New Relic will have no liability to you for direct,
// indirect, consequential, incidental, special, or punitive damages or
// for lost profits or data.
//
//
// 检查是否是自己的属性
var mapOwn = require('map-own')
// 在当前js未找到相关模块
// 初步猜测handle模块是事件的handle模块
var handle = require('handle')
// 收集聚合数据
var aggregatedData = {}
module.exports = {
store: store,
take: take,
get: get
}
// Items with the same type and name get aggregated together
// params are example data from the aggregated items
// metrics are the numeric values to be aggregated
function store (type, name, params, newMetrics) {
// hook for session traces to be able to use data upon aggregation
handle('bstAgg', [type, name, params, newMetrics])
if (!aggregatedData[type]) aggregatedData[type] = {}
var bucket = aggregatedData[type][name]
if (!bucket) {
bucket = aggregatedData[type][name] = { params: params || {} }
}
bucket.metrics = aggregateMetrics(newMetrics, bucket.metrics)
return bucket
}
function aggregateMetrics (newMetrics, oldMetrics) {
if (!oldMetrics) oldMetrics = {count: 0}
oldMetrics.count += 1
mapOwn(newMetrics, function (key, value) {
oldMetrics[key] = updateMetric(value, oldMetrics[key])
})
return oldMetrics
}
function updateMetric (value, metric) {
// When there is only one data point, the c (count), min, max, and sos (sum of squares) params are superfluous.
if (!metric) return {t: value}
// but on the second data point, we need to calculate the other values before aggregating in new values
if (metric && !metric.c) {
metric = {
t: metric.t,
min: metric.t,
max: metric.t,
sos: metric.t * metric.t,
c: 1
}
}
metric.c += 1
metric.t += value
metric.sos += value * value
if (value > metric.max) metric.max = value
if (value < metric.min) metric.min = value
return metric
}
function get (type, name) {
// if name is passed, get a single bucket
if (name) return aggregatedData[type] && aggregatedData[type][name]
// else, get all buckets of that type
return aggregatedData[type]
}
// Like get, but for many types and it deletes the retrieved content from the aggregatedData
function take (types) {
var results = {}
var type = ''
var hasData = false
for (var i = 0; i < types.length; i++) {
type = types[i]
results[type] = toArray(aggregatedData[type])
if (results[type].length) hasData = true
delete aggregatedData[type]
}
return hasData ? results : null
}
function toArray (obj) {
if (typeof obj !== 'object') return []
return mapOwn(obj, getValue)
}
function getValue (key, value) {
return value
}
},{"handle":false,"map-own":30}],3:[function(require,module,exports){
var register = require('./register-handler')
var harvest = require('./harvest')
var agg = require('./aggregator')
var single = require('./single')
var fetch = require('./fetch')
var mapOwn = require('map-own')
var loader = require('loader')
var handle = require('handle')
var cycle = 0
harvest.on('jserrors', function () {
return { body: agg.take([ 'cm' ]) }
})
var api = {
finished: single(finished),
setPageViewName: setPageViewName,
addToTrace: addToTrace,
inlineHit: inlineHit
}
// Hook all of the api functions up to the queues/stubs created in loader/api.js
mapOwn(api, function (fnName, fn) {
register('api-' + fnName, fn, 'api')
})
// All API functions get passed the time they were called as their
// first parameter. These functions can be called asynchronously.
function setPageViewName (t, name, host) {
if (typeof name !== 'string') return
if (name.charAt(0) !== '/') name = '/' + name
loader.customTransaction = (host || 'http://custom.transaction') + name
}
function finished (t, providedTime) {
var time = (providedTime || t)
agg.store('cm', 'finished', { name: 'finished' }, { time: time - loader.offset })
addToTrace(t, { name: 'finished', start: time, origin: 'nr' })
handle('api-addPageAction', [ time, 'finished' ], null, 'api')
}
function addToTrace (t, evt) {
if (!(evt && typeof evt === 'object' && evt.name && evt.start)) return
var report = {
n: evt.name,
s: evt.start - loader.offset,
e: (evt.end || evt.start) - loader.offset,
o: evt.origin || '',
t: 'api'
}
handle('bstApi', [report])
}
// NREUM.inlineHit(request_name, queue_time, app_time, total_be_time, dom_time, fe_time)
//
// request_name - the 'web page' name or service name
// queue_time - the amount of time spent in the app tier queue
// app_time - the amount of time spent in the application code
// total_be_time - the total roundtrip time of the remote service call
// dom_time - the time spent processing the result of the service call (or user defined)
// fe_time - the time spent rendering the result of the service call (or user defined)
function inlineHit (t, request_name, queue_time, app_time, total_be_time, dom_time, fe_time) {
request_name = window.encodeURIComponent(request_name)
cycle += 1
if (!loader.info.beacon) return
var url = loader.proto + loader.info.beacon + '/1/' + loader.info.licenseKey
url += '?a=' + loader.info.applicationID + '&'
url += 't=' + request_name + '&'
url += 'qt=' + ~~queue_time + '&'
url += 'ap=' + ~~app_time + '&'
url += 'be=' + ~~total_be_time + '&'
url += 'dc=' + ~~dom_time + '&'
url += 'fe=' + ~~fe_time + '&'
url += 'c=' + cycle
fetch.img(url)
}
},{"./aggregator":2,"./fetch":7,"./harvest":8,"./register-handler":13,"./single":15,"handle":false,"loader":false,"map-own":30}],4:[function(require,module,exports){
var baseEE = require('ee')
var mapOwn = require('map-own')
var handlers = require('./register-handler').handlers
module.exports = function drain (group) {
var bufferedEventsInGroup = baseEE.backlog[group]
var groupHandlers = handlers[group]
if (groupHandlers) {
// don't cache length, buffer can grow while processing
for (var i = 0; bufferedEventsInGroup && i < bufferedEventsInGroup.length; ++i) {
emitEvent(bufferedEventsInGroup[i], groupHandlers)
}
mapOwn(groupHandlers, function (eventType, handlerRegistrationList) {
mapOwn(handlerRegistrationList, function (i, registration) {
// registration is an array of: [targetEE, eventHandler]
registration[0].on(eventType, registration[1])
})
})
}
delete handlers[group]
// Keep the group as a property so we know it was created and drained
baseEE.backlog[group] = null
}
function emitEvent (evt, groupHandlers) {
var type = evt[1]
mapOwn(groupHandlers[type], function (i, registration) {
var sourceEE = evt[0]
var ee = registration[0]
if (ee === sourceEE) {
var handler = registration[1]
var ctx = evt[3]
var args = evt[2]
handler.apply(ctx, args)
}
})
}
},{"./register-handler":13,"ee":false,"map-own":30}],5:[function(require,module,exports){
var mapOwn = require('map-own')
var stringify = require('./stringify')
// Characters that are safe in a qs, but get encoded.
var charMap = {
'%2C': ',',
'%3A': ':',
'%2F': '/',
'%40': '@',
'%24': '$',
'%3B': ';'
}
var charList = mapOwn(charMap, function (k) { return k })
var safeEncoded = new RegExp(charList.join('|'), 'g')
function real (c) {
return charMap[c]
}
// Encode as URI Component, then unescape anything that is ok in the
// query string position.
function qs (value) {
if (value === null || value === undefined) return 'null'
return encodeURIComponent(value).replace(safeEncoded, real)
}
module.exports = {obj: obj, fromArray: fromArray, qs: qs, param: param}
function fromArray (qs, maxBytes) {
var bytes = 0
for (var i = 0; i < qs.length; i++) {
bytes += qs[i].length
if (bytes > maxBytes) return qs.slice(0, i).join('')
}
return qs.join('')
}
function obj (payload, maxBytes) {
var total = 0
var result = ''
mapOwn(payload, function (feature, dataArray) {
var intermediate = []
var next
var i
if (typeof dataArray === 'string') {
next = '&' + feature + '=' + qs(dataArray)
total += next.length
result += next
} else if (dataArray.length) {
total += 9
for (i = 0; i < dataArray.length; i++) {
next = qs(stringify(dataArray[i]))
// TODO: Consider more complete ways of handling too much error data.
total += next.length
if (typeof maxBytes !== 'undefined' && total >= maxBytes) break
intermediate.push(next)
}
result += '&' + feature + '=%5B' + intermediate.join(',') + '%5D'
}
})
return result
}
// Constructs an HTTP parameter to add to the BAM router URL
function param (name, value) {
if (value && typeof (value) === 'string') {
return '&' + name + '=' + qs(value)
}
return ''
}
},{"./stringify":18,"map-own":30}],6:[function(require,module,exports){
var mapOwn = require('map-own')
var ee = require('ee')
var drain = require('./drain')
module.exports = function activateFeatures (flags) {
if (!(flags && typeof flags === 'object')) return
mapOwn(flags, function (flag, val) {
if (!val || activatedFeatures[flag]) return
ee.emit('feat-' + flag, [])
activatedFeatures[flag] = true
})
drain('feature')
}
var activatedFeatures = module.exports.active = {}
},{"./drain":4,"ee":false,"map-own":30}],7:[function(require,module,exports){
var fetch = module.exports = {}
fetch.jsonp = function (url, jsonp) {
var element = document.createElement('script')
element.type = 'text/javascript'
element.src = url + '&jsonp=' + jsonp
var firstScript = document.getElementsByTagName('script')[0]
firstScript.parentNode.insertBefore(element, firstScript)
return element
}
fetch.xhr = function (url, body) {
var request = new XMLHttpRequest()
request.open('POST', url, true)
// Allows cookies to be set and got on the server.
if ('withCredentials' in request) request.withCredentials = true
request.setRequestHeader('content-type', 'text/plain')
request.send(body)
return request
}
fetch.img = function (url) {
var element = new Image()
element.src = url
return element
}
fetch.beacon = function (url, body) {
return navigator.sendBeacon(url, body)
}
},{}],8:[function(require,module,exports){
var single = require('./single')
var mapOwn = require('map-own')
var timing = require('./nav-timing')
var encode = require('./encode')
var stringify = require('./stringify')
var fetch = require('./fetch')
var reduce = require('reduce')
var aggregator = require('./aggregator')
var stopwatch = require('./stopwatch')
var nr = require('loader')
var version = '885.a559836'
var jsonp = 'NREUM.setToken'
var _events = {}
var haveSendBeacon = !!navigator.sendBeacon
// requiring ie version updates the IE version on the NR object
require('./ie-version')
var xhrUsable = nr.xhrWrappable && (nr.ieVersion > 9 || nr.ieVersion === 0)
module.exports = {
sendBeacon: single(sendBeacon), // wrapping this in single makes it so that it can only be called once from outside
sendFinal: sendAllFromUnload,
pingErrors: pingErrors,
sendX: sendToEndpoint,
on: on,
xhrUsable: xhrUsable
}
// nr is injected into all send methods. This allows for easier testing
// we could require('loader') instead
function sendBeacon (nr) {
if (!nr.info.beacon) return
if (nr.info.queueTime) aggregator.store('measures', 'qt', { value: nr.info.queueTime })
if (nr.info.applicationTime) aggregator.store('measures', 'ap', { value: nr.info.applicationTime })
// some time in the past some code will have called stopwatch.mark('starttime', Date.now())
// calling measure like this will create a metric that measures the time differential between
// the two marks.
stopwatch.measure('be', 'starttime', 'firstbyte')
stopwatch.measure('fe', 'firstbyte', 'onload')
stopwatch.measure('dc', 'firstbyte', 'domContent')
var measuresMetrics = aggregator.get('measures')
var measuresQueryString = mapOwn(measuresMetrics, function (metricName, measure) {
return '&' + metricName + '=' + measure.params.value
}).join('')
if (measuresQueryString) {
// currently we only have one version of our protocol
// in the future we may add more
var protocol = '1'
var chunksForQueryString = [baseQueryString(nr)]
chunksForQueryString.push(measuresQueryString)
chunksForQueryString.push(encode.param('tt', nr.info.ttGuid))
chunksForQueryString.push(encode.param('us', nr.info.user))
chunksForQueryString.push(encode.param('ac', nr.info.account))
chunksForQueryString.push(encode.param('pr', nr.info.product))
chunksForQueryString.push(encode.param('f', stringify(mapOwn(nr.features, function (k, v) { return k }))))
if (window.performance && typeof (window.performance.timing) !== 'undefined') {
var navTimingApiData = ({
timing: timing.addPT(window.performance.timing, {}),
navigation: timing.addPN(window.performance.navigation, {})
})
chunksForQueryString.push(encode.param('perf', stringify(navTimingApiData)))
}
chunksForQueryString.push(encode.param('xx', nr.info.extra))
chunksForQueryString.push(encode.param('ua', nr.info.userAttributes))
chunksForQueryString.push(encode.param('at', nr.info.atts))
var customJsAttributes = stringify(nr.info.jsAttributes)
chunksForQueryString.push(encode.param('ja', customJsAttributes === '{}' ? null : customJsAttributes))
var queryString = encode.fromArray(chunksForQueryString, nr.maxBytes)
fetch.jsonp(
nr.proto + nr.info.beacon + '/' + protocol + '/' + nr.info.licenseKey + queryString,
jsonp
)
}
}
function sendAllFromUnload (nr) {
var sents = mapOwn(_events, function (endpoint) {
return sendToEndpoint(endpoint, nr, { unload: true })
})
return reduce(sents, or)
}
function or (a, b) { return a || b }
function sendToEndpoint (endpoint, nr, opts) {
return send(nr, endpoint, createPayload(endpoint), opts || {})
}
function createPayload (type) {
var makeBody = add({})
var makeQueryString = add({})
var listeners = (_events[type] || [])
for (var i = 0; i < listeners.length; i++) {
var singlePayload = listeners[i]()
if (singlePayload.body) mapOwn(singlePayload.body, makeBody)
if (singlePayload.qs) mapOwn(singlePayload.qs, makeQueryString)
}
return { body: makeBody(), qs: makeQueryString() }
}
function send (nr, endpoint, payload, opts) {
if (!(nr.info.errorBeacon && payload.body)) return false
var url = 'https://' + nr.info.errorBeacon + '/' + endpoint + '/1/' + nr.info.licenseKey + baseQueryString(nr)
if (payload.qs) url += encode.obj(payload.qs, nr.maxBytes)
var method
var useBody
var body
switch (endpoint) {
case 'jserrors':
useBody = false
method = haveSendBeacon ? fetch.beacon : fetch.img
break
default:
if (opts.needResponse) {
useBody = true
method = fetch.xhr
} else if (opts.unload) {
useBody = haveSendBeacon
method = haveSendBeacon ? fetch.beacon : fetch.img
} else {
if (haveSendBeacon) {
useBody = true
method = fetch.beacon
} else if (xhrUsable) {
useBody = true
method = fetch.xhr
} else if (endpoint === 'events') {
method = fetch.img
} else {
return false
}
}
break
}
if (useBody && endpoint === 'events') {
body = payload.body.e
} else if (useBody) {
body = stringify(payload.body)
} else {
url += encode.obj(payload.body, nr.maxBytes)
}
return method(url, body)
}
function pingErrors (nr) {
if (!(nr && nr.info && nr.info.errorBeacon && nr.ieVersion)) return
var url = 'https://' + nr.info.errorBeacon + '/jserrors/ping/' + nr.info.licenseKey + baseQueryString(nr)
fetch.img(url)
}
// Constructs the transaction name param for the beacon URL.
// Prefers the obfuscated transaction name over the plain text.
// Falls back to making up a name.
function transactionNameParam (nr) {
if (nr.info.transactionName) return encode.param('to', nr.info.transactionName)
return encode.param('t', nr.info.tNamePlain || 'Unnamed Transaction')
}
function on (type, fn) {
var listeners = (_events[type] || (_events[type] = []))
listeners.push(fn)
}
// The stuff that gets sent every time.
function baseQueryString (nr) {
return ([
'?a=' + nr.info.applicationID,
encode.param('sa', (nr.info.sa ? '' + nr.info.sa : '')),
encode.param('v', version),
transactionNameParam(nr),
encode.param('ct', nr.customTransaction),
'&rst=' + (new Date().getTime() - nr.offset)
].join(''))
}
function add (payload) {
var hasData = false
return function (key, val) {
if (val && val.length) {
payload[key] = val
hasData = true
}
if (hasData) return payload
}
}
},{"./aggregator":2,"./encode":5,"./fetch":7,"./ie-version":9,"./nav-timing":12,"./single":15,"./stopwatch":17,"./stringify":18,"loader":false,"map-own":30,"reduce":32}],9:[function(require,module,exports){
var nr = require('loader')
var div = document.createElement('div')
div.innerHTML = '<!--[if lte IE 6]><div></div><![endif]-->' +
'<!--[if lte IE 7]><div></div><![endif]-->' +
'<!--[if lte IE 8]><div></div><![endif]-->' +
'<!--[if lte IE 9]><div></div><![endif]-->'
var len = div.getElementsByTagName('div').length
if (len === 4) nr.ieVersion = 6
else if (len === 3) nr.ieVersion = 7
else if (len === 2) nr.ieVersion = 8
else if (len === 1) nr.ieVersion = 9
else nr.ieVersion = 0
module.exports = nr.ieVersion
},{"loader":false}],10:[function(require,module,exports){
var sHash = require('./s-hash')
var addE = require('./add-e')
var startTime = require('./start-time')
var stopwatch = require('./stopwatch')
var single = require('./single')
var harvest = require('./harvest')
var registerHandler = require('./register-handler')
var activateFeatures = require('./feature-flags')
var nr = require('loader')
var ffVersion = require('../loader/firefox-version')
var drain = require('./drain')
// api loads registers several event listeners, but does not have any exports
require('./api')
var autorun = typeof (window.NREUM.autorun) !== 'undefined' ? window.NREUM.autorun : true
// Features are activated using the legacy setToken function name via JSONP
window.NREUM.setToken = activateFeatures
if (require('./ie-version') === 6) nr.maxBytes = 2000
else nr.maxBytes = 30000
var oneFinalHarvest = single(finalHarvest)
//
// Firefox has a bug wherein a slow-loading resource loaded from the 'pagehide'
// or 'unload' event will delay the 'load' event firing on the next page load.
// In Firefox versions that support sendBeacon, this doesn't matter, because
// we'll use it instead of an image load for our final harvest.
//
// Some Safari versions never fire the 'unload' event for pages that are being
// put into the WebKit page cache, so we *need* to use the pagehide event for
// the final submission from Safari.
//
// Generally speaking, we will try to submit our final harvest from either
// pagehide or unload, whichever comes first, but in Firefox, we need to avoid
// attempting to submit from pagehide to ensure that we don't slow down loading
// of the next page.
//
if (!ffVersion || navigator.sendBeacon) {
addE('pagehide', oneFinalHarvest)
} else {
addE('beforeunload', oneFinalHarvest)
}
addE('unload', oneFinalHarvest)
registerHandler('mark', stopwatch.mark, 'api')
stopwatch.mark('done')
drain('api')
if (autorun) harvest.sendBeacon(nr)
// Set a cookie when the page unloads. Consume this cookie on the next page to get a 'start time'.
// The navigation start time cookie is removed when the browser supports the web timing API.
// Doesn't work in some browsers (Opera).
function finalHarvest (e) {
harvest.sendFinal(nr, false)
// write navigation start time cookie if needed
if (startTime.navCookie) {
document.cookie = 'NREUM=s=' + Number(new Date()) + '&r=' + sHash(document.location.href) + '&p=' + sHash(document.referrer) + '; path=/'
}
}
},{"../loader/firefox-version":29,"./add-e":1,"./api":3,"./drain":4,"./feature-flags":6,"./harvest":8,"./ie-version":9,"./register-handler":13,"./s-hash":14,"./single":15,"./start-time":16,"./stopwatch":17,"loader":false}],11:[function(require,module,exports){
module.exports = function interval (fn, ms) {
setTimeout(function tick () {
try {
fn()
} finally {
setTimeout(tick, ms)
}
}, ms)
}
},{}],12:[function(require,module,exports){
// We don't use JSON.stringify directly on the performance timing data for these reasons:
// * Chrome has extra data in the performance object that we don't want to send all the time (wasteful)
// * Firefox fails to stringify the native object due to - http://code.google.com/p/v8/issues/detail?id=1223
// * The variable names are long and wasteful to transmit
// Add Performance Timing values to the given object.
// * Values are written relative to an offset to reduce their length (i.e. number of characters).
// * The offset is sent with the data
// * 0's are not included unless the value is a 'relative zero'
//
module.exports = {
addPT: addPT,
addPN: addPN
}
function addPT (pt, v) {
var offset = pt.navigationStart
v.of = offset
addRel(pt.navigationStart, offset, v, 'n')
addRel(pt.unloadEventStart, offset, v, 'u')
addRel(pt.unloadEventEnd, offset, v, 'ue')
addRel(pt.domLoading, offset, v, 'dl')
addRel(pt.domInteractive, offset, v, 'di')
addRel(pt.domContentLoadedEventStart, offset, v, 'ds')
addRel(pt.domContentLoadedEventEnd, offset, v, 'de')
addRel(pt.domComplete, offset, v, 'dc')
addRel(pt.loadEventStart, offset, v, 'l')
addRel(pt.loadEventEnd, offset, v, 'le')
addRel(pt.redirectStart, offset, v, 'r')
addRel(pt.redirectEnd, offset, v, 're')
addRel(pt.fetchStart, offset, v, 'f')
addRel(pt.domainLookupStart, offset, v, 'dn')
addRel(pt.domainLookupEnd, offset, v, 'dne')
addRel(pt.connectStart, offset, v, 'c')
addRel(pt.connectEnd, offset, v, 'ce')
addRel(pt.secureConnectionStart, offset, v, 's')
addRel(pt.requestStart, offset, v, 'rq')
addRel(pt.responseStart, offset, v, 'rp')
addRel(pt.responseEnd, offset, v, 'rpe')
return v
}
// Add Performance Navigation values to the given object
function addPN (pn, v) {
addRel(pn.type, 0, v, 'ty')
addRel(pn.redirectCount, 0, v, 'rc')
return v
}
function addRel (value, offset, obj, prop) {
if (typeof (value) === 'number' && (value > 0)) obj[prop] = Math.round(value - offset)
}
},{}],13:[function(require,module,exports){
var handleEE = require('handle').ee
module.exports = defaultRegister
defaultRegister.on = registerWithSpecificEmitter
var handlers = defaultRegister.handlers = {}
function defaultRegister (type, handler, group, ee) {
registerWithSpecificEmitter(ee || handleEE, type, handler, group)
}
function registerWithSpecificEmitter (ee, type, handler, group) {
if (!group) group = 'feature'
if (!ee) ee = handleEE
var groupHandlers = handlers[group] = handlers[group] || {}
var list = groupHandlers[type] = groupHandlers[type] || []
list.push([ee, handler])
}
},{"handle":false}],14:[function(require,module,exports){
module.exports = sHash
function sHash (s) {
var i
var h = 0
for (i = 0; i < s.length; i++) {
h += ((i + 1) * s.charCodeAt(i))
}
return Math.abs(h)
}
},{}],15:[function(require,module,exports){
var slice = require('lodash._slice')
module.exports = single
function single (fn) {
var called = false
var res
return function () {
if (called) return res
called = true
res = fn.apply(this, slice(arguments))
return res
}
}
},{"lodash._slice":31}],16:[function(require,module,exports){
// Use various techniques to determine the time at which this page started and whether to capture navigation timing information
var sHash = require('./s-hash')
var stopwatch = require('./stopwatch')
var nr = require('loader')
var ffVersion = require('../loader/firefox-version')
module.exports = { navCookie: true }
findStartTime()
function findStartTime () {
var starttime = findStartWebTiming() || findStartCookie()
if (!starttime) return
stopwatch.mark('starttime', starttime)
// Refine nr.offset
nr.offset = starttime
}
// Find the start time from the Web Timing 'performance' object.
// http://test.w3.org/webperf/specs/NavigationTiming/
// http://blog.chromium.org/2010/07/do-you-know-how-slow-your-web-page-is.html
function findStartWebTiming () {
// FF 7/8 has a bug with the navigation start time, so use cookie instead of native interface
if (ffVersion && ffVersion < 9) return
if (typeof (window.performance) !== 'undefined' && window.performance.timing && typeof (window.performance.timing.navigationStart) !== 'undefined') {
// note that we don't need to use a cookie to record navigation start time
module.exports.navCookie = false
return window.performance.timing.navigationStart
}
}
// Find the start time based on a cookie set by Episodes in the unload handler.
function findStartCookie () {
var aCookies = document.cookie.split(' ')
for (var i = 0; i < aCookies.length; i++) {
if (aCookies[i].indexOf('NREUM=') === 0) {
var startPage
var referrerPage
var aSubCookies = aCookies[i].substring('NREUM='.length).split('&')
var startTime
var bReferrerMatch
for (var j = 0; j < aSubCookies.length; j++) {
if (aSubCookies[j].indexOf('s=') === 0) {
startTime = aSubCookies[j].substring(2)
} else if (aSubCookies[j].indexOf('p=') === 0) {
referrerPage = aSubCookies[j].substring(2)
// if the sub-cookie is not the last cookie it will have a trailing ';'
if (referrerPage.charAt(referrerPage.length - 1) === ';') {
referrerPage = referrerPage.substr(0, referrerPage.length - 1)
}
} else if (aSubCookies[j].indexOf('r=') === 0) {
startPage = aSubCookies[j].substring(2)
// if the sub-cookie is not the last cookie it will have a trailing ';'
if (startPage.charAt(startPage.length - 1) === ';') {
startPage = startPage.substr(0, startPage.length - 1)
}
}
}
if (startPage) {
var docReferrer = sHash(document.referrer)
bReferrerMatch = (docReferrer == startPage) // eslint-disable-line
if (!bReferrerMatch) {
// Navigation did not start at the page that was just exited, check for re-load
// (i.e. the page just exited is the current page and the referring pages match)
bReferrerMatch = sHash(document.location.href) == startPage && docReferrer == referrerPage // eslint-disable-line
}
}
if (bReferrerMatch && startTime) {
var now = new Date().getTime()
if ((now - startTime) > 60000) {
return
}
return startTime
}
}
}
}
},{"../loader/firefox-version":29,"./s-hash":14,"./stopwatch":17,"loader":false}],17:[function(require,module,exports){
var aggregator = require('./aggregator')
var marks = {}
module.exports = {
mark: mark,
measure: measure
}
function mark (markName, markTime) {
if (typeof markTime === 'undefined') markTime = new Date().getTime()
marks[markName] = markTime