mixpanel-browser 2.75.0 → 2.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +14 -0
- package/.github/dependabot.yml +8 -0
- package/.github/workflows/integration-tests.yml +4 -4
- package/.github/workflows/unit-tests.yml +4 -4
- package/CHANGELOG.md +14 -0
- package/build.sh +10 -8
- package/dist/async-modules/mixpanel-recorder-DLKbUIEE.js +23669 -0
- package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js +2 -0
- package/dist/async-modules/mixpanel-recorder-wIWnMDLA.min.js.map +1 -0
- package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js +2 -0
- package/dist/async-modules/mixpanel-targeting-CTcftSJC.min.js.map +1 -0
- package/dist/async-modules/mixpanel-targeting-CmVvUyFM.js +2520 -0
- package/dist/mixpanel-core.cjs.d.ts +70 -1
- package/dist/mixpanel-core.cjs.js +724 -426
- package/dist/mixpanel-recorder.js +791 -41
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-targeting.js +6 -62
- package/dist/mixpanel-targeting.min.js +1 -1
- package/dist/mixpanel-targeting.min.js.map +1 -1
- package/dist/mixpanel-with-async-modules.cjs.d.ts +70 -1
- package/dist/mixpanel-with-async-modules.cjs.js +724 -426
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +70 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +724 -426
- package/dist/mixpanel-with-recorder.d.ts +70 -1
- package/dist/mixpanel-with-recorder.js +1471 -450
- package/dist/mixpanel-with-recorder.min.d.ts +70 -1
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +70 -1
- package/dist/mixpanel.amd.js +1473 -504
- package/dist/mixpanel.cjs.d.ts +70 -1
- package/dist/mixpanel.cjs.js +1473 -504
- package/dist/mixpanel.globals.js +724 -426
- package/dist/mixpanel.min.js +189 -182
- package/dist/mixpanel.module.d.ts +70 -1
- package/dist/mixpanel.module.js +1473 -504
- package/dist/mixpanel.umd.d.ts +70 -1
- package/dist/mixpanel.umd.js +1473 -504
- package/dist/rrweb-bundled.js +61 -9
- package/dist/rrweb-compiled.js +56 -9
- package/logo.svg +5 -0
- package/package.json +6 -4
- package/rollup.config.mjs +163 -46
- package/src/autocapture/index.js +10 -27
- package/src/config.js +9 -3
- package/src/flags/index.js +1 -2
- package/src/index.d.ts +70 -1
- package/src/mixpanel-core.js +77 -112
- package/src/recorder/index.js +1 -1
- package/src/recorder/recorder.js +5 -1
- package/src/recorder/rrweb-network-plugin.js +649 -0
- package/src/recorder/session-recording.js +36 -12
- package/src/recorder/utils.js +27 -1
- package/src/recorder-manager.js +324 -0
- package/src/request-batcher.js +1 -1
- package/src/targeting/event-matcher.js +2 -57
- package/src/targeting/index.js +1 -1
- package/src/targeting/loader.js +1 -1
- package/src/utils.js +13 -1
- package/testServer.js +69 -1
- package/src/globals.js +0 -14
- /package/src/loaders/{loader-module-with-async-recorder.d.ts → loader-module-with-async-modules.d.ts} +0 -0
|
@@ -0,0 +1,2520 @@
|
|
|
1
|
+
(function () {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
5
|
+
var win;
|
|
6
|
+
if (typeof(window) === 'undefined') {
|
|
7
|
+
var loc = {
|
|
8
|
+
hostname: ''
|
|
9
|
+
};
|
|
10
|
+
win = {
|
|
11
|
+
crypto: {randomUUID: function() {throw Error('unsupported');}},
|
|
12
|
+
navigator: { userAgent: '', onLine: true },
|
|
13
|
+
document: {
|
|
14
|
+
createElement: function() { return {}; },
|
|
15
|
+
location: loc,
|
|
16
|
+
referrer: ''
|
|
17
|
+
},
|
|
18
|
+
screen: { width: 0, height: 0 },
|
|
19
|
+
location: loc,
|
|
20
|
+
addEventListener: function() {},
|
|
21
|
+
removeEventListener: function() {},
|
|
22
|
+
dispatchEvent: function() {},
|
|
23
|
+
CustomEvent: function () {}
|
|
24
|
+
};
|
|
25
|
+
} else {
|
|
26
|
+
win = window;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var Config = {
|
|
30
|
+
LIB_VERSION: '2.77.0'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Window global names for async modules
|
|
34
|
+
var TARGETING_GLOBAL_NAME = '__mp_targeting';
|
|
35
|
+
|
|
36
|
+
var setImmediate = win['setImmediate'];
|
|
37
|
+
var builtInProp, cycle, schedulingQueue,
|
|
38
|
+
ToString = Object.prototype.toString,
|
|
39
|
+
timer = (typeof setImmediate !== 'undefined') ?
|
|
40
|
+
function timer(fn) { return setImmediate(fn); } :
|
|
41
|
+
setTimeout;
|
|
42
|
+
|
|
43
|
+
// dammit, IE8.
|
|
44
|
+
try {
|
|
45
|
+
Object.defineProperty({},'x',{});
|
|
46
|
+
builtInProp = function builtInProp(obj,name,val,config) {
|
|
47
|
+
return Object.defineProperty(obj,name,{
|
|
48
|
+
value: val,
|
|
49
|
+
writable: true,
|
|
50
|
+
configurable: config !== false
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
builtInProp = function builtInProp(obj,name,val) {
|
|
56
|
+
obj[name] = val;
|
|
57
|
+
return obj;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Note: using a queue instead of array for efficiency
|
|
62
|
+
schedulingQueue = (function Queue() {
|
|
63
|
+
var first, last, item;
|
|
64
|
+
|
|
65
|
+
function Item(fn,self) {
|
|
66
|
+
this.fn = fn;
|
|
67
|
+
this.self = self;
|
|
68
|
+
this.next = void 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
add: function add(fn,self) {
|
|
73
|
+
item = new Item(fn,self);
|
|
74
|
+
if (last) {
|
|
75
|
+
last.next = item;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
first = item;
|
|
79
|
+
}
|
|
80
|
+
last = item;
|
|
81
|
+
item = void 0;
|
|
82
|
+
},
|
|
83
|
+
drain: function drain() {
|
|
84
|
+
var f = first;
|
|
85
|
+
first = last = cycle = void 0;
|
|
86
|
+
|
|
87
|
+
while (f) {
|
|
88
|
+
f.fn.call(f.self);
|
|
89
|
+
f = f.next;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
})();
|
|
94
|
+
|
|
95
|
+
function schedule(fn,self) {
|
|
96
|
+
schedulingQueue.add(fn,self);
|
|
97
|
+
if (!cycle) {
|
|
98
|
+
cycle = timer(schedulingQueue.drain);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// promise duck typing
|
|
103
|
+
function isThenable(o) {
|
|
104
|
+
var _then, oType = typeof o;
|
|
105
|
+
|
|
106
|
+
if (o !== null && (oType === 'object' || oType === 'function')) {
|
|
107
|
+
_then = o.then;
|
|
108
|
+
}
|
|
109
|
+
return typeof _then === 'function' ? _then : false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function notify() {
|
|
113
|
+
for (var i=0; i<this.chain.length; i++) {
|
|
114
|
+
notifyIsolated(
|
|
115
|
+
this,
|
|
116
|
+
(this.state === 1) ? this.chain[i].success : this.chain[i].failure,
|
|
117
|
+
this.chain[i]
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
this.chain.length = 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// NOTE: This is a separate function to isolate
|
|
124
|
+
// the `try..catch` so that other code can be
|
|
125
|
+
// optimized better
|
|
126
|
+
function notifyIsolated(self,cb,chain) {
|
|
127
|
+
var ret, _then;
|
|
128
|
+
try {
|
|
129
|
+
if (cb === false) {
|
|
130
|
+
chain.reject(self.msg);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
if (cb === true) {
|
|
134
|
+
ret = self.msg;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
ret = cb.call(void 0,self.msg);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (ret === chain.promise) {
|
|
141
|
+
chain.reject(TypeError('Promise-chain cycle'));
|
|
142
|
+
}
|
|
143
|
+
// eslint-disable-next-line no-cond-assign
|
|
144
|
+
else if (_then = isThenable(ret)) {
|
|
145
|
+
_then.call(ret,chain.resolve,chain.reject);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
chain.resolve(ret);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
chain.reject(err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolve(msg) {
|
|
158
|
+
var _then, self = this;
|
|
159
|
+
|
|
160
|
+
// already triggered?
|
|
161
|
+
if (self.triggered) { return; }
|
|
162
|
+
|
|
163
|
+
self.triggered = true;
|
|
164
|
+
|
|
165
|
+
// unwrap
|
|
166
|
+
if (self.def) {
|
|
167
|
+
self = self.def;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// eslint-disable-next-line no-cond-assign
|
|
172
|
+
if (_then = isThenable(msg)) {
|
|
173
|
+
schedule(function(){
|
|
174
|
+
var defWrapper = new MakeDefWrapper(self);
|
|
175
|
+
try {
|
|
176
|
+
_then.call(msg,
|
|
177
|
+
function $resolve$(){ resolve.apply(defWrapper,arguments); },
|
|
178
|
+
function $reject$(){ reject.apply(defWrapper,arguments); }
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
reject.call(defWrapper,err);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
self.msg = msg;
|
|
188
|
+
self.state = 1;
|
|
189
|
+
if (self.chain.length > 0) {
|
|
190
|
+
schedule(notify,self);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
reject.call(new MakeDefWrapper(self),err);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function reject(msg) {
|
|
200
|
+
var self = this;
|
|
201
|
+
|
|
202
|
+
// already triggered?
|
|
203
|
+
if (self.triggered) { return; }
|
|
204
|
+
|
|
205
|
+
self.triggered = true;
|
|
206
|
+
|
|
207
|
+
// unwrap
|
|
208
|
+
if (self.def) {
|
|
209
|
+
self = self.def;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
self.msg = msg;
|
|
213
|
+
self.state = 2;
|
|
214
|
+
if (self.chain.length > 0) {
|
|
215
|
+
schedule(notify,self);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function iteratePromises(Constructor,arr,resolver,rejecter) {
|
|
220
|
+
for (var idx=0; idx<arr.length; idx++) {
|
|
221
|
+
(function IIFE(idx){
|
|
222
|
+
Constructor.resolve(arr[idx])
|
|
223
|
+
.then(
|
|
224
|
+
function $resolver$(msg){
|
|
225
|
+
resolver(idx,msg);
|
|
226
|
+
},
|
|
227
|
+
rejecter
|
|
228
|
+
);
|
|
229
|
+
})(idx);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function MakeDefWrapper(self) {
|
|
234
|
+
this.def = self;
|
|
235
|
+
this.triggered = false;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function MakeDef(self) {
|
|
239
|
+
this.promise = self;
|
|
240
|
+
this.state = 0;
|
|
241
|
+
this.triggered = false;
|
|
242
|
+
this.chain = [];
|
|
243
|
+
this.msg = void 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function NpoPromise(executor) {
|
|
247
|
+
if (typeof executor !== 'function') {
|
|
248
|
+
throw TypeError('Not a function');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this['__NPO__'] !== 0) {
|
|
252
|
+
throw TypeError('Not a promise');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// instance shadowing the inherited "brand"
|
|
256
|
+
// to signal an already "initialized" promise
|
|
257
|
+
this['__NPO__'] = 1;
|
|
258
|
+
|
|
259
|
+
var def = new MakeDef(this);
|
|
260
|
+
|
|
261
|
+
this['then'] = function then(success,failure) {
|
|
262
|
+
var o = {
|
|
263
|
+
success: typeof success === 'function' ? success : true,
|
|
264
|
+
failure: typeof failure === 'function' ? failure : false
|
|
265
|
+
};
|
|
266
|
+
// Note: `then(..)` itself can be borrowed to be used against
|
|
267
|
+
// a different promise constructor for making the chained promise,
|
|
268
|
+
// by substituting a different `this` binding.
|
|
269
|
+
o.promise = new this.constructor(function extractChain(resolve,reject) {
|
|
270
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
271
|
+
throw TypeError('Not a function');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
o.resolve = resolve;
|
|
275
|
+
o.reject = reject;
|
|
276
|
+
});
|
|
277
|
+
def.chain.push(o);
|
|
278
|
+
|
|
279
|
+
if (def.state !== 0) {
|
|
280
|
+
schedule(notify,def);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return o.promise;
|
|
284
|
+
};
|
|
285
|
+
this['catch'] = function $catch$(failure) {
|
|
286
|
+
return this.then(void 0,failure);
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
executor.call(
|
|
291
|
+
void 0,
|
|
292
|
+
function publicResolve(msg){
|
|
293
|
+
resolve.call(def,msg);
|
|
294
|
+
},
|
|
295
|
+
function publicReject(msg) {
|
|
296
|
+
reject.call(def,msg);
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
reject.call(def,err);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var PromisePrototype = builtInProp({},'constructor',NpoPromise,
|
|
306
|
+
/*configurable=*/false
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Note: Android 4 cannot use `Object.defineProperty(..)` here
|
|
310
|
+
NpoPromise.prototype = PromisePrototype;
|
|
311
|
+
|
|
312
|
+
// built-in "brand" to signal an "uninitialized" promise
|
|
313
|
+
builtInProp(PromisePrototype,'__NPO__',0,
|
|
314
|
+
/*configurable=*/false
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
builtInProp(NpoPromise,'resolve',function Promise$resolve(msg) {
|
|
318
|
+
var Constructor = this;
|
|
319
|
+
|
|
320
|
+
// spec mandated checks
|
|
321
|
+
// note: best "isPromise" check that's practical for now
|
|
322
|
+
if (msg && typeof msg === 'object' && msg['__NPO__'] === 1) {
|
|
323
|
+
return msg;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return new Constructor(function executor(resolve,reject){
|
|
327
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
328
|
+
throw TypeError('Not a function');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
resolve(msg);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
builtInProp(NpoPromise,'reject',function Promise$reject(msg) {
|
|
336
|
+
return new this(function executor(resolve,reject){
|
|
337
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
338
|
+
throw TypeError('Not a function');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
reject(msg);
|
|
342
|
+
});
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
builtInProp(NpoPromise,'all',function Promise$all(arr) {
|
|
346
|
+
var Constructor = this;
|
|
347
|
+
|
|
348
|
+
// spec mandated checks
|
|
349
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
350
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
351
|
+
}
|
|
352
|
+
if (arr.length === 0) {
|
|
353
|
+
return Constructor.resolve([]);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return new Constructor(function executor(resolve,reject){
|
|
357
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
358
|
+
throw TypeError('Not a function');
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
var len = arr.length, msgs = Array(len), count = 0;
|
|
362
|
+
|
|
363
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg) {
|
|
364
|
+
msgs[idx] = msg;
|
|
365
|
+
if (++count === len) {
|
|
366
|
+
resolve(msgs);
|
|
367
|
+
}
|
|
368
|
+
},reject);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
builtInProp(NpoPromise,'race',function Promise$race(arr) {
|
|
373
|
+
var Constructor = this;
|
|
374
|
+
|
|
375
|
+
// spec mandated checks
|
|
376
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
377
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return new Constructor(function executor(resolve,reject){
|
|
381
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
382
|
+
throw TypeError('Not a function');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg){
|
|
386
|
+
resolve(msg);
|
|
387
|
+
},reject);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1) ;
|
|
391
|
+
|
|
392
|
+
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
393
|
+
|
|
394
|
+
/*
|
|
395
|
+
* Saved references to long variable names, so that closure compiler can
|
|
396
|
+
* minimize file size.
|
|
397
|
+
*/
|
|
398
|
+
|
|
399
|
+
var ArrayProto = Array.prototype,
|
|
400
|
+
FuncProto = Function.prototype,
|
|
401
|
+
ObjProto = Object.prototype,
|
|
402
|
+
slice = ArrayProto.slice,
|
|
403
|
+
toString = ObjProto.toString,
|
|
404
|
+
hasOwnProperty = ObjProto.hasOwnProperty;
|
|
405
|
+
win.console;
|
|
406
|
+
var navigator = win.navigator,
|
|
407
|
+
document = win.document,
|
|
408
|
+
windowOpera = win.opera,
|
|
409
|
+
screen = win.screen,
|
|
410
|
+
userAgent = navigator.userAgent;
|
|
411
|
+
|
|
412
|
+
var nativeBind = FuncProto.bind,
|
|
413
|
+
nativeForEach = ArrayProto.forEach,
|
|
414
|
+
nativeIndexOf = ArrayProto.indexOf,
|
|
415
|
+
nativeMap = ArrayProto.map,
|
|
416
|
+
nativeIsArray = Array.isArray,
|
|
417
|
+
breaker = {};
|
|
418
|
+
|
|
419
|
+
var _ = {
|
|
420
|
+
trim: function(str) {
|
|
421
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim#Polyfill
|
|
422
|
+
return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
// UNDERSCORE
|
|
428
|
+
// Embed part of the Underscore Library
|
|
429
|
+
_.bind = function(func, context) {
|
|
430
|
+
var args, bound;
|
|
431
|
+
if (nativeBind && func.bind === nativeBind) {
|
|
432
|
+
return nativeBind.apply(func, slice.call(arguments, 1));
|
|
433
|
+
}
|
|
434
|
+
if (!_.isFunction(func)) {
|
|
435
|
+
throw new TypeError();
|
|
436
|
+
}
|
|
437
|
+
args = slice.call(arguments, 2);
|
|
438
|
+
bound = function() {
|
|
439
|
+
if (!(this instanceof bound)) {
|
|
440
|
+
return func.apply(context, args.concat(slice.call(arguments)));
|
|
441
|
+
}
|
|
442
|
+
var ctor = {};
|
|
443
|
+
ctor.prototype = func.prototype;
|
|
444
|
+
var self = new ctor();
|
|
445
|
+
ctor.prototype = null;
|
|
446
|
+
var result = func.apply(self, args.concat(slice.call(arguments)));
|
|
447
|
+
if (Object(result) === result) {
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
return self;
|
|
451
|
+
};
|
|
452
|
+
return bound;
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* @param {*=} obj
|
|
457
|
+
* @param {function(...*)=} iterator
|
|
458
|
+
* @param {Object=} context
|
|
459
|
+
*/
|
|
460
|
+
_.each = function(obj, iterator, context) {
|
|
461
|
+
if (obj === null || obj === undefined) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
if (nativeForEach && obj.forEach === nativeForEach) {
|
|
465
|
+
obj.forEach(iterator, context);
|
|
466
|
+
} else if (obj.length === +obj.length) {
|
|
467
|
+
for (var i = 0, l = obj.length; i < l; i++) {
|
|
468
|
+
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
for (var key in obj) {
|
|
474
|
+
if (hasOwnProperty.call(obj, key)) {
|
|
475
|
+
if (iterator.call(context, obj[key], key, obj) === breaker) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
_.extend = function(obj) {
|
|
484
|
+
_.each(slice.call(arguments, 1), function(source) {
|
|
485
|
+
for (var prop in source) {
|
|
486
|
+
if (source[prop] !== void 0) {
|
|
487
|
+
obj[prop] = source[prop];
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
return obj;
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
_.isArray = nativeIsArray || function(obj) {
|
|
495
|
+
return toString.call(obj) === '[object Array]';
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
_.isFunction = function(f) {
|
|
499
|
+
return typeof f === 'function';
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
_.isArguments = function(obj) {
|
|
503
|
+
return !!(obj && hasOwnProperty.call(obj, 'callee'));
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
_.toArray = function(iterable) {
|
|
507
|
+
if (!iterable) {
|
|
508
|
+
return [];
|
|
509
|
+
}
|
|
510
|
+
if (iterable.toArray) {
|
|
511
|
+
return iterable.toArray();
|
|
512
|
+
}
|
|
513
|
+
if (_.isArray(iterable)) {
|
|
514
|
+
return slice.call(iterable);
|
|
515
|
+
}
|
|
516
|
+
if (_.isArguments(iterable)) {
|
|
517
|
+
return slice.call(iterable);
|
|
518
|
+
}
|
|
519
|
+
return _.values(iterable);
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
_.map = function(arr, callback, context) {
|
|
523
|
+
if (nativeMap && arr.map === nativeMap) {
|
|
524
|
+
return arr.map(callback, context);
|
|
525
|
+
} else {
|
|
526
|
+
var results = [];
|
|
527
|
+
_.each(arr, function(item) {
|
|
528
|
+
results.push(callback.call(context, item));
|
|
529
|
+
});
|
|
530
|
+
return results;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
_.keys = function(obj) {
|
|
535
|
+
var results = [];
|
|
536
|
+
if (obj === null) {
|
|
537
|
+
return results;
|
|
538
|
+
}
|
|
539
|
+
_.each(obj, function(value, key) {
|
|
540
|
+
results[results.length] = key;
|
|
541
|
+
});
|
|
542
|
+
return results;
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
_.values = function(obj) {
|
|
546
|
+
var results = [];
|
|
547
|
+
if (obj === null) {
|
|
548
|
+
return results;
|
|
549
|
+
}
|
|
550
|
+
_.each(obj, function(value) {
|
|
551
|
+
results[results.length] = value;
|
|
552
|
+
});
|
|
553
|
+
return results;
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
_.include = function(obj, target) {
|
|
557
|
+
var found = false;
|
|
558
|
+
if (obj === null) {
|
|
559
|
+
return found;
|
|
560
|
+
}
|
|
561
|
+
if (nativeIndexOf && obj.indexOf === nativeIndexOf) {
|
|
562
|
+
return obj.indexOf(target) != -1;
|
|
563
|
+
}
|
|
564
|
+
_.each(obj, function(value) {
|
|
565
|
+
if (found || (found = (value === target))) {
|
|
566
|
+
return breaker;
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
return found;
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
_.includes = function(str, needle) {
|
|
573
|
+
return str.indexOf(needle) !== -1;
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// Underscore Addons
|
|
577
|
+
_.inherit = function(subclass, superclass) {
|
|
578
|
+
subclass.prototype = new superclass();
|
|
579
|
+
subclass.prototype.constructor = subclass;
|
|
580
|
+
subclass.superclass = superclass.prototype;
|
|
581
|
+
return subclass;
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
_.isObject = function(obj) {
|
|
585
|
+
return (obj === Object(obj) && !_.isArray(obj));
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
_.isEmptyObject = function(obj) {
|
|
589
|
+
if (_.isObject(obj)) {
|
|
590
|
+
for (var key in obj) {
|
|
591
|
+
if (hasOwnProperty.call(obj, key)) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
return false;
|
|
598
|
+
};
|
|
599
|
+
|
|
600
|
+
_.isUndefined = function(obj) {
|
|
601
|
+
return obj === void 0;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
_.isString = function(obj) {
|
|
605
|
+
return toString.call(obj) == '[object String]';
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
_.isDate = function(obj) {
|
|
609
|
+
return toString.call(obj) == '[object Date]';
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
_.isNumber = function(obj) {
|
|
613
|
+
return toString.call(obj) == '[object Number]';
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
_.isElement = function(obj) {
|
|
617
|
+
return !!(obj && obj.nodeType === 1);
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
_.encodeDates = function(obj) {
|
|
621
|
+
_.each(obj, function(v, k) {
|
|
622
|
+
if (_.isDate(v)) {
|
|
623
|
+
obj[k] = _.formatDate(v);
|
|
624
|
+
} else if (_.isObject(v)) {
|
|
625
|
+
obj[k] = _.encodeDates(v); // recurse
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
return obj;
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
_.timestamp = function() {
|
|
632
|
+
Date.now = Date.now || function() {
|
|
633
|
+
return +new Date;
|
|
634
|
+
};
|
|
635
|
+
return Date.now();
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
_.formatDate = function(d) {
|
|
639
|
+
// YYYY-MM-DDTHH:MM:SS in UTC
|
|
640
|
+
function pad(n) {
|
|
641
|
+
return n < 10 ? '0' + n : n;
|
|
642
|
+
}
|
|
643
|
+
return d.getUTCFullYear() + '-' +
|
|
644
|
+
pad(d.getUTCMonth() + 1) + '-' +
|
|
645
|
+
pad(d.getUTCDate()) + 'T' +
|
|
646
|
+
pad(d.getUTCHours()) + ':' +
|
|
647
|
+
pad(d.getUTCMinutes()) + ':' +
|
|
648
|
+
pad(d.getUTCSeconds());
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
_.strip_empty_properties = function(p) {
|
|
652
|
+
var ret = {};
|
|
653
|
+
_.each(p, function(v, k) {
|
|
654
|
+
if (_.isString(v) && v.length > 0) {
|
|
655
|
+
ret[k] = v;
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
return ret;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
/*
|
|
662
|
+
* this function returns a copy of object after truncating it. If
|
|
663
|
+
* passed an Array or Object it will iterate through obj and
|
|
664
|
+
* truncate all the values recursively.
|
|
665
|
+
*/
|
|
666
|
+
_.truncate = function(obj, length) {
|
|
667
|
+
var ret;
|
|
668
|
+
|
|
669
|
+
if (typeof(obj) === 'string') {
|
|
670
|
+
ret = obj.slice(0, length);
|
|
671
|
+
} else if (_.isArray(obj)) {
|
|
672
|
+
ret = [];
|
|
673
|
+
_.each(obj, function(val) {
|
|
674
|
+
ret.push(_.truncate(val, length));
|
|
675
|
+
});
|
|
676
|
+
} else if (_.isObject(obj)) {
|
|
677
|
+
ret = {};
|
|
678
|
+
_.each(obj, function(val, key) {
|
|
679
|
+
ret[key] = _.truncate(val, length);
|
|
680
|
+
});
|
|
681
|
+
} else {
|
|
682
|
+
ret = obj;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
return ret;
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
_.JSONEncode = (function() {
|
|
689
|
+
return function(mixed_val) {
|
|
690
|
+
var value = mixed_val;
|
|
691
|
+
var quote = function(string) {
|
|
692
|
+
var escapable = /[\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; // eslint-disable-line no-control-regex
|
|
693
|
+
var meta = { // table of character substitutions
|
|
694
|
+
'\b': '\\b',
|
|
695
|
+
'\t': '\\t',
|
|
696
|
+
'\n': '\\n',
|
|
697
|
+
'\f': '\\f',
|
|
698
|
+
'\r': '\\r',
|
|
699
|
+
'"': '\\"',
|
|
700
|
+
'\\': '\\\\'
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
escapable.lastIndex = 0;
|
|
704
|
+
return escapable.test(string) ?
|
|
705
|
+
'"' + string.replace(escapable, function(a) {
|
|
706
|
+
var c = meta[a];
|
|
707
|
+
return typeof c === 'string' ? c :
|
|
708
|
+
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
|
709
|
+
}) + '"' :
|
|
710
|
+
'"' + string + '"';
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
var str = function(key, holder) {
|
|
714
|
+
var gap = '';
|
|
715
|
+
var indent = ' ';
|
|
716
|
+
var i = 0; // The loop counter.
|
|
717
|
+
var k = ''; // The member key.
|
|
718
|
+
var v = ''; // The member value.
|
|
719
|
+
var length = 0;
|
|
720
|
+
var mind = gap;
|
|
721
|
+
var partial = [];
|
|
722
|
+
var value = holder[key];
|
|
723
|
+
|
|
724
|
+
// If the value has a toJSON method, call it to obtain a replacement value.
|
|
725
|
+
if (value && typeof value === 'object' &&
|
|
726
|
+
typeof value.toJSON === 'function') {
|
|
727
|
+
value = value.toJSON(key);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// What happens next depends on the value's type.
|
|
731
|
+
switch (typeof value) {
|
|
732
|
+
case 'string':
|
|
733
|
+
return quote(value);
|
|
734
|
+
|
|
735
|
+
case 'number':
|
|
736
|
+
// JSON numbers must be finite. Encode non-finite numbers as null.
|
|
737
|
+
return isFinite(value) ? String(value) : 'null';
|
|
738
|
+
|
|
739
|
+
case 'boolean':
|
|
740
|
+
case 'null':
|
|
741
|
+
// If the value is a boolean or null, convert it to a string. Note:
|
|
742
|
+
// typeof null does not produce 'null'. The case is included here in
|
|
743
|
+
// the remote chance that this gets fixed someday.
|
|
744
|
+
|
|
745
|
+
return String(value);
|
|
746
|
+
|
|
747
|
+
case 'object':
|
|
748
|
+
// If the type is 'object', we might be dealing with an object or an array or
|
|
749
|
+
// null.
|
|
750
|
+
// Due to a specification blunder in ECMAScript, typeof null is 'object',
|
|
751
|
+
// so watch out for that case.
|
|
752
|
+
if (!value) {
|
|
753
|
+
return 'null';
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// Make an array to hold the partial results of stringifying this object value.
|
|
757
|
+
gap += indent;
|
|
758
|
+
partial = [];
|
|
759
|
+
|
|
760
|
+
// Is the value an array?
|
|
761
|
+
if (toString.apply(value) === '[object Array]') {
|
|
762
|
+
// The value is an array. Stringify every element. Use null as a placeholder
|
|
763
|
+
// for non-JSON values.
|
|
764
|
+
|
|
765
|
+
length = value.length;
|
|
766
|
+
for (i = 0; i < length; i += 1) {
|
|
767
|
+
partial[i] = str(i, value) || 'null';
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Join all of the elements together, separated with commas, and wrap them in
|
|
771
|
+
// brackets.
|
|
772
|
+
v = partial.length === 0 ? '[]' :
|
|
773
|
+
gap ? '[\n' + gap +
|
|
774
|
+
partial.join(',\n' + gap) + '\n' +
|
|
775
|
+
mind + ']' :
|
|
776
|
+
'[' + partial.join(',') + ']';
|
|
777
|
+
gap = mind;
|
|
778
|
+
return v;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Iterate through all of the keys in the object.
|
|
782
|
+
for (k in value) {
|
|
783
|
+
if (hasOwnProperty.call(value, k)) {
|
|
784
|
+
v = str(k, value);
|
|
785
|
+
if (v) {
|
|
786
|
+
partial.push(quote(k) + (gap ? ': ' : ':') + v);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// Join all of the member texts together, separated with commas,
|
|
792
|
+
// and wrap them in braces.
|
|
793
|
+
v = partial.length === 0 ? '{}' :
|
|
794
|
+
gap ? '{' + partial.join(',') + '' +
|
|
795
|
+
mind + '}' : '{' + partial.join(',') + '}';
|
|
796
|
+
gap = mind;
|
|
797
|
+
return v;
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
|
|
801
|
+
// Make a fake root object containing our value under the key of ''.
|
|
802
|
+
// Return the result of stringifying the value.
|
|
803
|
+
return str('', {
|
|
804
|
+
'': value
|
|
805
|
+
});
|
|
806
|
+
};
|
|
807
|
+
})();
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* From https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js
|
|
811
|
+
* Slightly modified to throw a real Error rather than a POJO
|
|
812
|
+
*/
|
|
813
|
+
_.JSONDecode = (function() {
|
|
814
|
+
var at, // The index of the current character
|
|
815
|
+
ch, // The current character
|
|
816
|
+
escapee = {
|
|
817
|
+
'"': '"',
|
|
818
|
+
'\\': '\\',
|
|
819
|
+
'/': '/',
|
|
820
|
+
'b': '\b',
|
|
821
|
+
'f': '\f',
|
|
822
|
+
'n': '\n',
|
|
823
|
+
'r': '\r',
|
|
824
|
+
't': '\t'
|
|
825
|
+
},
|
|
826
|
+
text,
|
|
827
|
+
error = function(m) {
|
|
828
|
+
var e = new SyntaxError(m);
|
|
829
|
+
e.at = at;
|
|
830
|
+
e.text = text;
|
|
831
|
+
throw e;
|
|
832
|
+
},
|
|
833
|
+
next = function(c) {
|
|
834
|
+
// If a c parameter is provided, verify that it matches the current character.
|
|
835
|
+
if (c && c !== ch) {
|
|
836
|
+
error('Expected \'' + c + '\' instead of \'' + ch + '\'');
|
|
837
|
+
}
|
|
838
|
+
// Get the next character. When there are no more characters,
|
|
839
|
+
// return the empty string.
|
|
840
|
+
ch = text.charAt(at);
|
|
841
|
+
at += 1;
|
|
842
|
+
return ch;
|
|
843
|
+
},
|
|
844
|
+
number = function() {
|
|
845
|
+
// Parse a number value.
|
|
846
|
+
var number,
|
|
847
|
+
string = '';
|
|
848
|
+
|
|
849
|
+
if (ch === '-') {
|
|
850
|
+
string = '-';
|
|
851
|
+
next('-');
|
|
852
|
+
}
|
|
853
|
+
while (ch >= '0' && ch <= '9') {
|
|
854
|
+
string += ch;
|
|
855
|
+
next();
|
|
856
|
+
}
|
|
857
|
+
if (ch === '.') {
|
|
858
|
+
string += '.';
|
|
859
|
+
while (next() && ch >= '0' && ch <= '9') {
|
|
860
|
+
string += ch;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (ch === 'e' || ch === 'E') {
|
|
864
|
+
string += ch;
|
|
865
|
+
next();
|
|
866
|
+
if (ch === '-' || ch === '+') {
|
|
867
|
+
string += ch;
|
|
868
|
+
next();
|
|
869
|
+
}
|
|
870
|
+
while (ch >= '0' && ch <= '9') {
|
|
871
|
+
string += ch;
|
|
872
|
+
next();
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
number = +string;
|
|
876
|
+
if (!isFinite(number)) {
|
|
877
|
+
error('Bad number');
|
|
878
|
+
} else {
|
|
879
|
+
return number;
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
string = function() {
|
|
884
|
+
// Parse a string value.
|
|
885
|
+
var hex,
|
|
886
|
+
i,
|
|
887
|
+
string = '',
|
|
888
|
+
uffff;
|
|
889
|
+
// When parsing for string values, we must look for " and \ characters.
|
|
890
|
+
if (ch === '"') {
|
|
891
|
+
while (next()) {
|
|
892
|
+
if (ch === '"') {
|
|
893
|
+
next();
|
|
894
|
+
return string;
|
|
895
|
+
}
|
|
896
|
+
if (ch === '\\') {
|
|
897
|
+
next();
|
|
898
|
+
if (ch === 'u') {
|
|
899
|
+
uffff = 0;
|
|
900
|
+
for (i = 0; i < 4; i += 1) {
|
|
901
|
+
hex = parseInt(next(), 16);
|
|
902
|
+
if (!isFinite(hex)) {
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
uffff = uffff * 16 + hex;
|
|
906
|
+
}
|
|
907
|
+
string += String.fromCharCode(uffff);
|
|
908
|
+
} else if (typeof escapee[ch] === 'string') {
|
|
909
|
+
string += escapee[ch];
|
|
910
|
+
} else {
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
} else {
|
|
914
|
+
string += ch;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
error('Bad string');
|
|
919
|
+
},
|
|
920
|
+
white = function() {
|
|
921
|
+
// Skip whitespace.
|
|
922
|
+
while (ch && ch <= ' ') {
|
|
923
|
+
next();
|
|
924
|
+
}
|
|
925
|
+
},
|
|
926
|
+
word = function() {
|
|
927
|
+
// true, false, or null.
|
|
928
|
+
switch (ch) {
|
|
929
|
+
case 't':
|
|
930
|
+
next('t');
|
|
931
|
+
next('r');
|
|
932
|
+
next('u');
|
|
933
|
+
next('e');
|
|
934
|
+
return true;
|
|
935
|
+
case 'f':
|
|
936
|
+
next('f');
|
|
937
|
+
next('a');
|
|
938
|
+
next('l');
|
|
939
|
+
next('s');
|
|
940
|
+
next('e');
|
|
941
|
+
return false;
|
|
942
|
+
case 'n':
|
|
943
|
+
next('n');
|
|
944
|
+
next('u');
|
|
945
|
+
next('l');
|
|
946
|
+
next('l');
|
|
947
|
+
return null;
|
|
948
|
+
}
|
|
949
|
+
error('Unexpected "' + ch + '"');
|
|
950
|
+
},
|
|
951
|
+
value, // Placeholder for the value function.
|
|
952
|
+
array = function() {
|
|
953
|
+
// Parse an array value.
|
|
954
|
+
var array = [];
|
|
955
|
+
|
|
956
|
+
if (ch === '[') {
|
|
957
|
+
next('[');
|
|
958
|
+
white();
|
|
959
|
+
if (ch === ']') {
|
|
960
|
+
next(']');
|
|
961
|
+
return array; // empty array
|
|
962
|
+
}
|
|
963
|
+
while (ch) {
|
|
964
|
+
array.push(value());
|
|
965
|
+
white();
|
|
966
|
+
if (ch === ']') {
|
|
967
|
+
next(']');
|
|
968
|
+
return array;
|
|
969
|
+
}
|
|
970
|
+
next(',');
|
|
971
|
+
white();
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
error('Bad array');
|
|
975
|
+
},
|
|
976
|
+
object = function() {
|
|
977
|
+
// Parse an object value.
|
|
978
|
+
var key,
|
|
979
|
+
object = {};
|
|
980
|
+
|
|
981
|
+
if (ch === '{') {
|
|
982
|
+
next('{');
|
|
983
|
+
white();
|
|
984
|
+
if (ch === '}') {
|
|
985
|
+
next('}');
|
|
986
|
+
return object; // empty object
|
|
987
|
+
}
|
|
988
|
+
while (ch) {
|
|
989
|
+
key = string();
|
|
990
|
+
white();
|
|
991
|
+
next(':');
|
|
992
|
+
if (Object.hasOwnProperty.call(object, key)) {
|
|
993
|
+
error('Duplicate key "' + key + '"');
|
|
994
|
+
}
|
|
995
|
+
object[key] = value();
|
|
996
|
+
white();
|
|
997
|
+
if (ch === '}') {
|
|
998
|
+
next('}');
|
|
999
|
+
return object;
|
|
1000
|
+
}
|
|
1001
|
+
next(',');
|
|
1002
|
+
white();
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
error('Bad object');
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
value = function() {
|
|
1009
|
+
// Parse a JSON value. It could be an object, an array, a string,
|
|
1010
|
+
// a number, or a word.
|
|
1011
|
+
white();
|
|
1012
|
+
switch (ch) {
|
|
1013
|
+
case '{':
|
|
1014
|
+
return object();
|
|
1015
|
+
case '[':
|
|
1016
|
+
return array();
|
|
1017
|
+
case '"':
|
|
1018
|
+
return string();
|
|
1019
|
+
case '-':
|
|
1020
|
+
return number();
|
|
1021
|
+
default:
|
|
1022
|
+
return ch >= '0' && ch <= '9' ? number() : word();
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
// Return the json_parse function. It will have access to all of the
|
|
1027
|
+
// above functions and variables.
|
|
1028
|
+
return function(source) {
|
|
1029
|
+
var result;
|
|
1030
|
+
|
|
1031
|
+
text = source;
|
|
1032
|
+
at = 0;
|
|
1033
|
+
ch = ' ';
|
|
1034
|
+
result = value();
|
|
1035
|
+
white();
|
|
1036
|
+
if (ch) {
|
|
1037
|
+
error('Syntax error');
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return result;
|
|
1041
|
+
};
|
|
1042
|
+
})();
|
|
1043
|
+
|
|
1044
|
+
_.base64Encode = function(data) {
|
|
1045
|
+
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|
1046
|
+
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
|
|
1047
|
+
ac = 0,
|
|
1048
|
+
enc = '',
|
|
1049
|
+
tmp_arr = [];
|
|
1050
|
+
|
|
1051
|
+
if (!data) {
|
|
1052
|
+
return data;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
data = _.utf8Encode(data);
|
|
1056
|
+
|
|
1057
|
+
do { // pack three octets into four hexets
|
|
1058
|
+
o1 = data.charCodeAt(i++);
|
|
1059
|
+
o2 = data.charCodeAt(i++);
|
|
1060
|
+
o3 = data.charCodeAt(i++);
|
|
1061
|
+
|
|
1062
|
+
bits = o1 << 16 | o2 << 8 | o3;
|
|
1063
|
+
|
|
1064
|
+
h1 = bits >> 18 & 0x3f;
|
|
1065
|
+
h2 = bits >> 12 & 0x3f;
|
|
1066
|
+
h3 = bits >> 6 & 0x3f;
|
|
1067
|
+
h4 = bits & 0x3f;
|
|
1068
|
+
|
|
1069
|
+
// use hexets to index into b64, and append result to encoded string
|
|
1070
|
+
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
|
|
1071
|
+
} while (i < data.length);
|
|
1072
|
+
|
|
1073
|
+
enc = tmp_arr.join('');
|
|
1074
|
+
|
|
1075
|
+
switch (data.length % 3) {
|
|
1076
|
+
case 1:
|
|
1077
|
+
enc = enc.slice(0, -2) + '==';
|
|
1078
|
+
break;
|
|
1079
|
+
case 2:
|
|
1080
|
+
enc = enc.slice(0, -1) + '=';
|
|
1081
|
+
break;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
return enc;
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
_.utf8Encode = function(string) {
|
|
1088
|
+
string = (string + '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
1089
|
+
|
|
1090
|
+
var utftext = '',
|
|
1091
|
+
start,
|
|
1092
|
+
end;
|
|
1093
|
+
var stringl = 0,
|
|
1094
|
+
n;
|
|
1095
|
+
|
|
1096
|
+
start = end = 0;
|
|
1097
|
+
stringl = string.length;
|
|
1098
|
+
|
|
1099
|
+
for (n = 0; n < stringl; n++) {
|
|
1100
|
+
var c1 = string.charCodeAt(n);
|
|
1101
|
+
var enc = null;
|
|
1102
|
+
|
|
1103
|
+
if (c1 < 128) {
|
|
1104
|
+
end++;
|
|
1105
|
+
} else if ((c1 > 127) && (c1 < 2048)) {
|
|
1106
|
+
enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128);
|
|
1107
|
+
} else {
|
|
1108
|
+
enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128);
|
|
1109
|
+
}
|
|
1110
|
+
if (enc !== null) {
|
|
1111
|
+
if (end > start) {
|
|
1112
|
+
utftext += string.substring(start, end);
|
|
1113
|
+
}
|
|
1114
|
+
utftext += enc;
|
|
1115
|
+
start = end = n + 1;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
if (end > start) {
|
|
1120
|
+
utftext += string.substring(start, string.length);
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
return utftext;
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
_.UUID = function() {
|
|
1127
|
+
try {
|
|
1128
|
+
// use native Crypto API when available
|
|
1129
|
+
return win['crypto']['randomUUID']();
|
|
1130
|
+
} catch (err) {
|
|
1131
|
+
// fall back to generating our own UUID
|
|
1132
|
+
// based on https://gist.github.com/scwood/3bff42cc005cc20ab7ec98f0d8e1d59d
|
|
1133
|
+
var uuid = new Array(36);
|
|
1134
|
+
for (var i = 0; i < 36; i++) {
|
|
1135
|
+
uuid[i] = Math.floor(Math.random() * 16);
|
|
1136
|
+
}
|
|
1137
|
+
uuid[14] = 4; // set bits 12-15 of time-high-and-version to 0100
|
|
1138
|
+
uuid[19] = uuid[19] &= -5; // set bit 6 of clock-seq-and-reserved to zero
|
|
1139
|
+
uuid[19] = uuid[19] |= (1 << 3); // set bit 7 of clock-seq-and-reserved to one
|
|
1140
|
+
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
|
|
1141
|
+
|
|
1142
|
+
return _.map(uuid, function(x) {
|
|
1143
|
+
return x.toString(16);
|
|
1144
|
+
}).join('');
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// _.isBlockedUA()
|
|
1149
|
+
// This is to block various web spiders from executing our JS and
|
|
1150
|
+
// sending false tracking data
|
|
1151
|
+
var BLOCKED_UA_STRS = [
|
|
1152
|
+
'ahrefsbot',
|
|
1153
|
+
'ahrefssiteaudit',
|
|
1154
|
+
'amazonbot',
|
|
1155
|
+
'baiduspider',
|
|
1156
|
+
'bingbot',
|
|
1157
|
+
'bingpreview',
|
|
1158
|
+
'chrome-lighthouse',
|
|
1159
|
+
'facebookexternal',
|
|
1160
|
+
'petalbot',
|
|
1161
|
+
'pinterest',
|
|
1162
|
+
'screaming frog',
|
|
1163
|
+
'yahoo! slurp',
|
|
1164
|
+
'yandex',
|
|
1165
|
+
|
|
1166
|
+
// a whole bunch of goog-specific crawlers
|
|
1167
|
+
// https://developers.google.com/search/docs/advanced/crawling/overview-google-crawlers
|
|
1168
|
+
'adsbot-google',
|
|
1169
|
+
'apis-google',
|
|
1170
|
+
'duplexweb-google',
|
|
1171
|
+
'feedfetcher-google',
|
|
1172
|
+
'google favicon',
|
|
1173
|
+
'google web preview',
|
|
1174
|
+
'google-read-aloud',
|
|
1175
|
+
'googlebot',
|
|
1176
|
+
'googleweblight',
|
|
1177
|
+
'mediapartners-google',
|
|
1178
|
+
'storebot-google'
|
|
1179
|
+
];
|
|
1180
|
+
_.isBlockedUA = function(ua) {
|
|
1181
|
+
var i;
|
|
1182
|
+
ua = ua.toLowerCase();
|
|
1183
|
+
for (i = 0; i < BLOCKED_UA_STRS.length; i++) {
|
|
1184
|
+
if (ua.indexOf(BLOCKED_UA_STRS[i]) !== -1) {
|
|
1185
|
+
return true;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
return false;
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* @param {Object=} formdata
|
|
1193
|
+
* @param {string=} arg_separator
|
|
1194
|
+
*/
|
|
1195
|
+
_.HTTPBuildQuery = function(formdata, arg_separator) {
|
|
1196
|
+
var use_val, use_key, tmp_arr = [];
|
|
1197
|
+
|
|
1198
|
+
if (_.isUndefined(arg_separator)) {
|
|
1199
|
+
arg_separator = '&';
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
_.each(formdata, function(val, key) {
|
|
1203
|
+
use_val = encodeURIComponent(val.toString());
|
|
1204
|
+
use_key = encodeURIComponent(key);
|
|
1205
|
+
tmp_arr[tmp_arr.length] = use_key + '=' + use_val;
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
return tmp_arr.join(arg_separator);
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
_.getQueryParam = function(url, param) {
|
|
1212
|
+
// Expects a raw URL
|
|
1213
|
+
|
|
1214
|
+
param = param.replace(/[[]/g, '\\[').replace(/[\]]/g, '\\]');
|
|
1215
|
+
var regexS = '[\\?&]' + param + '=([^&#]*)',
|
|
1216
|
+
regex = new RegExp(regexS),
|
|
1217
|
+
results = regex.exec(url);
|
|
1218
|
+
if (results === null || (results && typeof(results[1]) !== 'string' && results[1].length)) {
|
|
1219
|
+
return '';
|
|
1220
|
+
} else {
|
|
1221
|
+
var result = results[1];
|
|
1222
|
+
try {
|
|
1223
|
+
result = decodeURIComponent(result);
|
|
1224
|
+
} catch(err) {
|
|
1225
|
+
}
|
|
1226
|
+
return result.replace(/\+/g, ' ');
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
// _.cookie
|
|
1232
|
+
// Methods partially borrowed from quirksmode.org/js/cookies.html
|
|
1233
|
+
_.cookie = {
|
|
1234
|
+
get: function(name) {
|
|
1235
|
+
var nameEQ = name + '=';
|
|
1236
|
+
var ca = document.cookie.split(';');
|
|
1237
|
+
for (var i = 0; i < ca.length; i++) {
|
|
1238
|
+
var c = ca[i];
|
|
1239
|
+
while (c.charAt(0) == ' ') {
|
|
1240
|
+
c = c.substring(1, c.length);
|
|
1241
|
+
}
|
|
1242
|
+
if (c.indexOf(nameEQ) === 0) {
|
|
1243
|
+
return decodeURIComponent(c.substring(nameEQ.length, c.length));
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
return null;
|
|
1247
|
+
},
|
|
1248
|
+
|
|
1249
|
+
parse: function(name) {
|
|
1250
|
+
var cookie;
|
|
1251
|
+
try {
|
|
1252
|
+
cookie = _.JSONDecode(_.cookie.get(name)) || {};
|
|
1253
|
+
} catch (err) {
|
|
1254
|
+
// noop
|
|
1255
|
+
}
|
|
1256
|
+
return cookie;
|
|
1257
|
+
},
|
|
1258
|
+
|
|
1259
|
+
set_seconds: function(name, value, seconds, is_cross_subdomain, is_secure, is_cross_site, domain_override) {
|
|
1260
|
+
var cdomain = '',
|
|
1261
|
+
expires = '',
|
|
1262
|
+
secure = '';
|
|
1263
|
+
|
|
1264
|
+
if (domain_override) {
|
|
1265
|
+
cdomain = '; domain=' + domain_override;
|
|
1266
|
+
} else if (is_cross_subdomain) {
|
|
1267
|
+
var domain = extract_domain(document.location.hostname);
|
|
1268
|
+
cdomain = domain ? '; domain=.' + domain : '';
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
if (seconds) {
|
|
1272
|
+
var date = new Date();
|
|
1273
|
+
date.setTime(date.getTime() + (seconds * 1000));
|
|
1274
|
+
expires = '; expires=' + date.toGMTString();
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
if (is_cross_site) {
|
|
1278
|
+
is_secure = true;
|
|
1279
|
+
secure = '; SameSite=None';
|
|
1280
|
+
}
|
|
1281
|
+
if (is_secure) {
|
|
1282
|
+
secure += '; secure';
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
document.cookie = name + '=' + encodeURIComponent(value) + expires + '; path=/' + cdomain + secure;
|
|
1286
|
+
},
|
|
1287
|
+
|
|
1288
|
+
set: function(name, value, days, is_cross_subdomain, is_secure, is_cross_site, domain_override) {
|
|
1289
|
+
var cdomain = '', expires = '', secure = '';
|
|
1290
|
+
|
|
1291
|
+
if (domain_override) {
|
|
1292
|
+
cdomain = '; domain=' + domain_override;
|
|
1293
|
+
} else if (is_cross_subdomain) {
|
|
1294
|
+
var domain = extract_domain(document.location.hostname);
|
|
1295
|
+
cdomain = domain ? '; domain=.' + domain : '';
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
if (days) {
|
|
1299
|
+
var date = new Date();
|
|
1300
|
+
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
|
1301
|
+
expires = '; expires=' + date.toGMTString();
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
if (is_cross_site) {
|
|
1305
|
+
is_secure = true;
|
|
1306
|
+
secure = '; SameSite=None';
|
|
1307
|
+
}
|
|
1308
|
+
if (is_secure) {
|
|
1309
|
+
secure += '; secure';
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
var new_cookie_val = name + '=' + encodeURIComponent(value) + expires + '; path=/' + cdomain + secure;
|
|
1313
|
+
document.cookie = new_cookie_val;
|
|
1314
|
+
return new_cookie_val;
|
|
1315
|
+
},
|
|
1316
|
+
|
|
1317
|
+
remove: function(name, is_cross_subdomain, domain_override) {
|
|
1318
|
+
_.cookie.set(name, '', -1, is_cross_subdomain, false, false, domain_override);
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
|
|
1322
|
+
var _testStorageSupported = function (storage) {
|
|
1323
|
+
var supported = true;
|
|
1324
|
+
try {
|
|
1325
|
+
var key = '__mplss_' + cheap_guid(8),
|
|
1326
|
+
val = 'xyz';
|
|
1327
|
+
storage.setItem(key, val);
|
|
1328
|
+
if (storage.getItem(key) !== val) {
|
|
1329
|
+
supported = false;
|
|
1330
|
+
}
|
|
1331
|
+
storage.removeItem(key);
|
|
1332
|
+
} catch (err) {
|
|
1333
|
+
supported = false;
|
|
1334
|
+
}
|
|
1335
|
+
return supported;
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
var _localStorageSupported = null;
|
|
1339
|
+
var localStorageSupported = function(storage, forceCheck) {
|
|
1340
|
+
if (_localStorageSupported !== null && !forceCheck) {
|
|
1341
|
+
return _localStorageSupported;
|
|
1342
|
+
}
|
|
1343
|
+
return _localStorageSupported = _testStorageSupported(storage || win.localStorage);
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
var _sessionStorageSupported = null;
|
|
1347
|
+
var sessionStorageSupported = function(storage, forceCheck) {
|
|
1348
|
+
if (_sessionStorageSupported !== null && !forceCheck) {
|
|
1349
|
+
return _sessionStorageSupported;
|
|
1350
|
+
}
|
|
1351
|
+
return _sessionStorageSupported = _testStorageSupported(storage || win.sessionStorage);
|
|
1352
|
+
};
|
|
1353
|
+
|
|
1354
|
+
function _storageWrapper(storage, name, is_supported_fn) {
|
|
1355
|
+
var log_error = function(msg) {
|
|
1356
|
+
};
|
|
1357
|
+
|
|
1358
|
+
return {
|
|
1359
|
+
is_supported: function(forceCheck) {
|
|
1360
|
+
var supported = is_supported_fn(storage, forceCheck);
|
|
1361
|
+
return supported;
|
|
1362
|
+
},
|
|
1363
|
+
error: log_error,
|
|
1364
|
+
get: function(key) {
|
|
1365
|
+
try {
|
|
1366
|
+
return storage.getItem(key);
|
|
1367
|
+
} catch (err) {
|
|
1368
|
+
}
|
|
1369
|
+
return null;
|
|
1370
|
+
},
|
|
1371
|
+
parse: function(key) {
|
|
1372
|
+
try {
|
|
1373
|
+
return _.JSONDecode(storage.getItem(key)) || {};
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
// noop
|
|
1376
|
+
}
|
|
1377
|
+
return null;
|
|
1378
|
+
},
|
|
1379
|
+
set: function(key, value) {
|
|
1380
|
+
try {
|
|
1381
|
+
storage.setItem(key, value);
|
|
1382
|
+
} catch (err) {
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
remove: function(key) {
|
|
1386
|
+
try {
|
|
1387
|
+
storage.removeItem(key);
|
|
1388
|
+
} catch (err) {
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
// Safari errors out accessing localStorage/sessionStorage when cookies are disabled,
|
|
1395
|
+
// so create dummy storage wrappers that silently fail as a fallback.
|
|
1396
|
+
var windowLocalStorage = null, windowSessionStorage = null;
|
|
1397
|
+
try {
|
|
1398
|
+
windowLocalStorage = win.localStorage;
|
|
1399
|
+
windowSessionStorage = win.sessionStorage;
|
|
1400
|
+
// eslint-disable-next-line no-empty
|
|
1401
|
+
} catch (_err) {}
|
|
1402
|
+
|
|
1403
|
+
_.localStorage = _storageWrapper(windowLocalStorage, 'localStorage', localStorageSupported);
|
|
1404
|
+
_.sessionStorage = _storageWrapper(windowSessionStorage, 'sessionStorage', sessionStorageSupported);
|
|
1405
|
+
|
|
1406
|
+
_.register_event = (function() {
|
|
1407
|
+
// written by Dean Edwards, 2005
|
|
1408
|
+
// with input from Tino Zijdel - crisp@xs4all.nl
|
|
1409
|
+
// with input from Carl Sverre - mail@carlsverre.com
|
|
1410
|
+
// with input from Mixpanel
|
|
1411
|
+
// http://dean.edwards.name/weblog/2005/10/add-event/
|
|
1412
|
+
// https://gist.github.com/1930440
|
|
1413
|
+
|
|
1414
|
+
/**
|
|
1415
|
+
* @param {Object} element
|
|
1416
|
+
* @param {string} type
|
|
1417
|
+
* @param {function(...*)} handler
|
|
1418
|
+
* @param {boolean=} oldSchool
|
|
1419
|
+
* @param {boolean=} useCapture
|
|
1420
|
+
*/
|
|
1421
|
+
var register_event = function(element, type, handler, oldSchool, useCapture) {
|
|
1422
|
+
if (!element) {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
if (element.addEventListener && !oldSchool) {
|
|
1427
|
+
element.addEventListener(type, handler, !!useCapture);
|
|
1428
|
+
} else {
|
|
1429
|
+
var ontype = 'on' + type;
|
|
1430
|
+
var old_handler = element[ontype]; // can be undefined
|
|
1431
|
+
element[ontype] = makeHandler(element, handler, old_handler);
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
|
|
1435
|
+
function makeHandler(element, new_handler, old_handlers) {
|
|
1436
|
+
var handler = function(event) {
|
|
1437
|
+
event = event || fixEvent(win.event);
|
|
1438
|
+
|
|
1439
|
+
// this basically happens in firefox whenever another script
|
|
1440
|
+
// overwrites the onload callback and doesn't pass the event
|
|
1441
|
+
// object to previously defined callbacks. All the browsers
|
|
1442
|
+
// that don't define window.event implement addEventListener
|
|
1443
|
+
// so the dom_loaded handler will still be fired as usual.
|
|
1444
|
+
if (!event) {
|
|
1445
|
+
return undefined;
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
var ret = true;
|
|
1449
|
+
var old_result, new_result;
|
|
1450
|
+
|
|
1451
|
+
if (_.isFunction(old_handlers)) {
|
|
1452
|
+
old_result = old_handlers(event);
|
|
1453
|
+
}
|
|
1454
|
+
new_result = new_handler.call(element, event);
|
|
1455
|
+
|
|
1456
|
+
if ((false === old_result) || (false === new_result)) {
|
|
1457
|
+
ret = false;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
return ret;
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
return handler;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function fixEvent(event) {
|
|
1467
|
+
if (event) {
|
|
1468
|
+
event.preventDefault = fixEvent.preventDefault;
|
|
1469
|
+
event.stopPropagation = fixEvent.stopPropagation;
|
|
1470
|
+
}
|
|
1471
|
+
return event;
|
|
1472
|
+
}
|
|
1473
|
+
fixEvent.preventDefault = function() {
|
|
1474
|
+
this.returnValue = false;
|
|
1475
|
+
};
|
|
1476
|
+
fixEvent.stopPropagation = function() {
|
|
1477
|
+
this.cancelBubble = true;
|
|
1478
|
+
};
|
|
1479
|
+
|
|
1480
|
+
return register_event;
|
|
1481
|
+
})();
|
|
1482
|
+
|
|
1483
|
+
|
|
1484
|
+
var TOKEN_MATCH_REGEX = new RegExp('^(\\w*)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?"?([^\\]"]*)"?\\]$');
|
|
1485
|
+
|
|
1486
|
+
_.dom_query = (function() {
|
|
1487
|
+
/* document.getElementsBySelector(selector)
|
|
1488
|
+
- returns an array of element objects from the current document
|
|
1489
|
+
matching the CSS selector. Selectors can contain element names,
|
|
1490
|
+
class names and ids and can be nested. For example:
|
|
1491
|
+
|
|
1492
|
+
elements = document.getElementsBySelector('div#main p a.external')
|
|
1493
|
+
|
|
1494
|
+
Will return an array of all 'a' elements with 'external' in their
|
|
1495
|
+
class attribute that are contained inside 'p' elements that are
|
|
1496
|
+
contained inside the 'div' element which has id="main"
|
|
1497
|
+
|
|
1498
|
+
New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
|
|
1499
|
+
See http://www.w3.org/TR/css3-selectors/#attribute-selectors
|
|
1500
|
+
|
|
1501
|
+
Version 0.4 - Simon Willison, March 25th 2003
|
|
1502
|
+
-- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
|
|
1503
|
+
-- Opera 7 fails
|
|
1504
|
+
|
|
1505
|
+
Version 0.5 - Carl Sverre, Jan 7th 2013
|
|
1506
|
+
-- Now uses jQuery-esque `hasClass` for testing class name
|
|
1507
|
+
equality. This fixes a bug related to '-' characters being
|
|
1508
|
+
considered not part of a 'word' in regex.
|
|
1509
|
+
*/
|
|
1510
|
+
|
|
1511
|
+
function getAllChildren(e) {
|
|
1512
|
+
// Returns all children of element. Workaround required for IE5/Windows. Ugh.
|
|
1513
|
+
return e.all ? e.all : e.getElementsByTagName('*');
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
var bad_whitespace = /[\t\r\n]/g;
|
|
1517
|
+
|
|
1518
|
+
function hasClass(elem, selector) {
|
|
1519
|
+
var className = ' ' + selector + ' ';
|
|
1520
|
+
return ((' ' + elem.className + ' ').replace(bad_whitespace, ' ').indexOf(className) >= 0);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
function getElementsBySelector(selector) {
|
|
1524
|
+
// Attempt to fail gracefully in lesser browsers
|
|
1525
|
+
if (!document.getElementsByTagName) {
|
|
1526
|
+
return [];
|
|
1527
|
+
}
|
|
1528
|
+
// Split selector in to tokens
|
|
1529
|
+
var tokens = selector.split(' ');
|
|
1530
|
+
var token, bits, tagName, found, foundCount, i, j, k, elements, currentContextIndex;
|
|
1531
|
+
var currentContext = [document];
|
|
1532
|
+
for (i = 0; i < tokens.length; i++) {
|
|
1533
|
+
token = tokens[i].replace(/^\s+/, '').replace(/\s+$/, '');
|
|
1534
|
+
if (token.indexOf('#') > -1) {
|
|
1535
|
+
// Token is an ID selector
|
|
1536
|
+
bits = token.split('#');
|
|
1537
|
+
tagName = bits[0];
|
|
1538
|
+
var id = bits[1];
|
|
1539
|
+
var element = document.getElementById(id);
|
|
1540
|
+
if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {
|
|
1541
|
+
// element not found or tag with that ID not found, return false
|
|
1542
|
+
return [];
|
|
1543
|
+
}
|
|
1544
|
+
// Set currentContext to contain just this element
|
|
1545
|
+
currentContext = [element];
|
|
1546
|
+
continue; // Skip to next token
|
|
1547
|
+
}
|
|
1548
|
+
if (token.indexOf('.') > -1) {
|
|
1549
|
+
// Token contains a class selector
|
|
1550
|
+
bits = token.split('.');
|
|
1551
|
+
tagName = bits[0];
|
|
1552
|
+
var className = bits[1];
|
|
1553
|
+
if (!tagName) {
|
|
1554
|
+
tagName = '*';
|
|
1555
|
+
}
|
|
1556
|
+
// Get elements matching tag, filter them for class selector
|
|
1557
|
+
found = [];
|
|
1558
|
+
foundCount = 0;
|
|
1559
|
+
for (j = 0; j < currentContext.length; j++) {
|
|
1560
|
+
if (tagName == '*') {
|
|
1561
|
+
elements = getAllChildren(currentContext[j]);
|
|
1562
|
+
} else {
|
|
1563
|
+
elements = currentContext[j].getElementsByTagName(tagName);
|
|
1564
|
+
}
|
|
1565
|
+
for (k = 0; k < elements.length; k++) {
|
|
1566
|
+
found[foundCount++] = elements[k];
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
currentContext = [];
|
|
1570
|
+
currentContextIndex = 0;
|
|
1571
|
+
for (j = 0; j < found.length; j++) {
|
|
1572
|
+
if (found[j].className &&
|
|
1573
|
+
_.isString(found[j].className) && // some SVG elements have classNames which are not strings
|
|
1574
|
+
hasClass(found[j], className)
|
|
1575
|
+
) {
|
|
1576
|
+
currentContext[currentContextIndex++] = found[j];
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
continue; // Skip to next token
|
|
1580
|
+
}
|
|
1581
|
+
// Code to deal with attribute selectors
|
|
1582
|
+
var token_match = token.match(TOKEN_MATCH_REGEX);
|
|
1583
|
+
if (token_match) {
|
|
1584
|
+
tagName = token_match[1];
|
|
1585
|
+
var attrName = token_match[2];
|
|
1586
|
+
var attrOperator = token_match[3];
|
|
1587
|
+
var attrValue = token_match[4];
|
|
1588
|
+
if (!tagName) {
|
|
1589
|
+
tagName = '*';
|
|
1590
|
+
}
|
|
1591
|
+
// Grab all of the tagName elements within current context
|
|
1592
|
+
found = [];
|
|
1593
|
+
foundCount = 0;
|
|
1594
|
+
for (j = 0; j < currentContext.length; j++) {
|
|
1595
|
+
if (tagName == '*') {
|
|
1596
|
+
elements = getAllChildren(currentContext[j]);
|
|
1597
|
+
} else {
|
|
1598
|
+
elements = currentContext[j].getElementsByTagName(tagName);
|
|
1599
|
+
}
|
|
1600
|
+
for (k = 0; k < elements.length; k++) {
|
|
1601
|
+
found[foundCount++] = elements[k];
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
currentContext = [];
|
|
1605
|
+
currentContextIndex = 0;
|
|
1606
|
+
var checkFunction; // This function will be used to filter the elements
|
|
1607
|
+
switch (attrOperator) {
|
|
1608
|
+
case '=': // Equality
|
|
1609
|
+
checkFunction = function(e) {
|
|
1610
|
+
return (e.getAttribute(attrName) == attrValue);
|
|
1611
|
+
};
|
|
1612
|
+
break;
|
|
1613
|
+
case '~': // Match one of space seperated words
|
|
1614
|
+
checkFunction = function(e) {
|
|
1615
|
+
return (e.getAttribute(attrName).match(new RegExp('\\b' + attrValue + '\\b')));
|
|
1616
|
+
};
|
|
1617
|
+
break;
|
|
1618
|
+
case '|': // Match start with value followed by optional hyphen
|
|
1619
|
+
checkFunction = function(e) {
|
|
1620
|
+
return (e.getAttribute(attrName).match(new RegExp('^' + attrValue + '-?')));
|
|
1621
|
+
};
|
|
1622
|
+
break;
|
|
1623
|
+
case '^': // Match starts with value
|
|
1624
|
+
checkFunction = function(e) {
|
|
1625
|
+
return (e.getAttribute(attrName).indexOf(attrValue) === 0);
|
|
1626
|
+
};
|
|
1627
|
+
break;
|
|
1628
|
+
case '$': // Match ends with value - fails with "Warning" in Opera 7
|
|
1629
|
+
checkFunction = function(e) {
|
|
1630
|
+
return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length);
|
|
1631
|
+
};
|
|
1632
|
+
break;
|
|
1633
|
+
case '*': // Match ends with value
|
|
1634
|
+
checkFunction = function(e) {
|
|
1635
|
+
return (e.getAttribute(attrName).indexOf(attrValue) > -1);
|
|
1636
|
+
};
|
|
1637
|
+
break;
|
|
1638
|
+
default:
|
|
1639
|
+
// Just test for existence of attribute
|
|
1640
|
+
checkFunction = function(e) {
|
|
1641
|
+
return e.getAttribute(attrName);
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
currentContext = [];
|
|
1645
|
+
currentContextIndex = 0;
|
|
1646
|
+
for (j = 0; j < found.length; j++) {
|
|
1647
|
+
if (checkFunction(found[j])) {
|
|
1648
|
+
currentContext[currentContextIndex++] = found[j];
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
|
|
1652
|
+
continue; // Skip to next token
|
|
1653
|
+
}
|
|
1654
|
+
// If we get here, token is JUST an element (not a class or ID selector)
|
|
1655
|
+
tagName = token;
|
|
1656
|
+
found = [];
|
|
1657
|
+
foundCount = 0;
|
|
1658
|
+
for (j = 0; j < currentContext.length; j++) {
|
|
1659
|
+
elements = currentContext[j].getElementsByTagName(tagName);
|
|
1660
|
+
for (k = 0; k < elements.length; k++) {
|
|
1661
|
+
found[foundCount++] = elements[k];
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
currentContext = found;
|
|
1665
|
+
}
|
|
1666
|
+
return currentContext;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
return function(query) {
|
|
1670
|
+
if (_.isElement(query)) {
|
|
1671
|
+
return [query];
|
|
1672
|
+
} else if (_.isObject(query) && !_.isUndefined(query.length)) {
|
|
1673
|
+
return query;
|
|
1674
|
+
} else {
|
|
1675
|
+
return getElementsBySelector.call(this, query);
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
})();
|
|
1679
|
+
|
|
1680
|
+
var CAMPAIGN_KEYWORDS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'utm_id', 'utm_source_platform','utm_campaign_id', 'utm_creative_format', 'utm_marketing_tactic'];
|
|
1681
|
+
var CLICK_IDS = ['dclid', 'fbclid', 'gclid', 'ko_click_id', 'li_fat_id', 'msclkid', 'sccid', 'ttclid', 'twclid', 'wbraid'];
|
|
1682
|
+
|
|
1683
|
+
_.info = {
|
|
1684
|
+
campaignParams: function(default_value) {
|
|
1685
|
+
var kw = '',
|
|
1686
|
+
params = {};
|
|
1687
|
+
_.each(CAMPAIGN_KEYWORDS, function(kwkey) {
|
|
1688
|
+
kw = _.getQueryParam(document.URL, kwkey);
|
|
1689
|
+
if (kw.length) {
|
|
1690
|
+
params[kwkey] = kw;
|
|
1691
|
+
} else if (default_value !== undefined) {
|
|
1692
|
+
params[kwkey] = default_value;
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
return params;
|
|
1697
|
+
},
|
|
1698
|
+
|
|
1699
|
+
clickParams: function() {
|
|
1700
|
+
var id = '',
|
|
1701
|
+
params = {};
|
|
1702
|
+
_.each(CLICK_IDS, function(idkey) {
|
|
1703
|
+
id = _.getQueryParam(document.URL, idkey);
|
|
1704
|
+
if (id.length) {
|
|
1705
|
+
params[idkey] = id;
|
|
1706
|
+
}
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
return params;
|
|
1710
|
+
},
|
|
1711
|
+
|
|
1712
|
+
marketingParams: function() {
|
|
1713
|
+
return _.extend(_.info.campaignParams(), _.info.clickParams());
|
|
1714
|
+
},
|
|
1715
|
+
|
|
1716
|
+
searchEngine: function(referrer) {
|
|
1717
|
+
if (referrer.search('https?://(.*)google.([^/?]*)') === 0) {
|
|
1718
|
+
return 'google';
|
|
1719
|
+
} else if (referrer.search('https?://(.*)bing.com') === 0) {
|
|
1720
|
+
return 'bing';
|
|
1721
|
+
} else if (referrer.search('https?://(.*)yahoo.com') === 0) {
|
|
1722
|
+
return 'yahoo';
|
|
1723
|
+
} else if (referrer.search('https?://(.*)duckduckgo.com') === 0) {
|
|
1724
|
+
return 'duckduckgo';
|
|
1725
|
+
} else {
|
|
1726
|
+
return null;
|
|
1727
|
+
}
|
|
1728
|
+
},
|
|
1729
|
+
|
|
1730
|
+
searchInfo: function(referrer) {
|
|
1731
|
+
var search = _.info.searchEngine(referrer),
|
|
1732
|
+
param = (search != 'yahoo') ? 'q' : 'p',
|
|
1733
|
+
ret = {};
|
|
1734
|
+
|
|
1735
|
+
if (search !== null) {
|
|
1736
|
+
ret['$search_engine'] = search;
|
|
1737
|
+
|
|
1738
|
+
var keyword = _.getQueryParam(referrer, param);
|
|
1739
|
+
if (keyword.length) {
|
|
1740
|
+
ret['mp_keyword'] = keyword;
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
return ret;
|
|
1745
|
+
},
|
|
1746
|
+
|
|
1747
|
+
/**
|
|
1748
|
+
* This function detects which browser is running this script.
|
|
1749
|
+
* The order of the checks are important since many user agents
|
|
1750
|
+
* include key words used in later checks.
|
|
1751
|
+
*/
|
|
1752
|
+
browser: function(user_agent, vendor, opera) {
|
|
1753
|
+
vendor = vendor || ''; // vendor is undefined for at least IE9
|
|
1754
|
+
if (opera || _.includes(user_agent, ' OPR/')) {
|
|
1755
|
+
if (_.includes(user_agent, 'Mini')) {
|
|
1756
|
+
return 'Opera Mini';
|
|
1757
|
+
}
|
|
1758
|
+
return 'Opera';
|
|
1759
|
+
} else if (/(BlackBerry|PlayBook|BB10)/i.test(user_agent)) {
|
|
1760
|
+
return 'BlackBerry';
|
|
1761
|
+
} else if (_.includes(user_agent, 'IEMobile') || _.includes(user_agent, 'WPDesktop')) {
|
|
1762
|
+
return 'Internet Explorer Mobile';
|
|
1763
|
+
} else if (_.includes(user_agent, 'SamsungBrowser/')) {
|
|
1764
|
+
// https://developer.samsung.com/internet/user-agent-string-format
|
|
1765
|
+
return 'Samsung Internet';
|
|
1766
|
+
} else if (_.includes(user_agent, 'Edge') || _.includes(user_agent, 'Edg/')) {
|
|
1767
|
+
return 'Microsoft Edge';
|
|
1768
|
+
} else if (_.includes(user_agent, 'FBIOS')) {
|
|
1769
|
+
return 'Facebook Mobile';
|
|
1770
|
+
} else if (_.includes(user_agent, 'Whale/')) {
|
|
1771
|
+
// https://user-agents.net/browsers/whale-browser
|
|
1772
|
+
return 'Whale Browser';
|
|
1773
|
+
} else if (_.includes(user_agent, 'Chrome')) {
|
|
1774
|
+
return 'Chrome';
|
|
1775
|
+
} else if (_.includes(user_agent, 'CriOS')) {
|
|
1776
|
+
return 'Chrome iOS';
|
|
1777
|
+
} else if (_.includes(user_agent, 'UCWEB') || _.includes(user_agent, 'UCBrowser')) {
|
|
1778
|
+
return 'UC Browser';
|
|
1779
|
+
} else if (_.includes(user_agent, 'FxiOS')) {
|
|
1780
|
+
return 'Firefox iOS';
|
|
1781
|
+
} else if (_.includes(vendor, 'Apple')) {
|
|
1782
|
+
if (_.includes(user_agent, 'Mobile')) {
|
|
1783
|
+
return 'Mobile Safari';
|
|
1784
|
+
}
|
|
1785
|
+
return 'Safari';
|
|
1786
|
+
} else if (_.includes(user_agent, 'Android')) {
|
|
1787
|
+
return 'Android Mobile';
|
|
1788
|
+
} else if (_.includes(user_agent, 'Konqueror')) {
|
|
1789
|
+
return 'Konqueror';
|
|
1790
|
+
} else if (_.includes(user_agent, 'Firefox')) {
|
|
1791
|
+
return 'Firefox';
|
|
1792
|
+
} else if (_.includes(user_agent, 'MSIE') || _.includes(user_agent, 'Trident/')) {
|
|
1793
|
+
return 'Internet Explorer';
|
|
1794
|
+
} else if (_.includes(user_agent, 'Gecko')) {
|
|
1795
|
+
return 'Mozilla';
|
|
1796
|
+
} else {
|
|
1797
|
+
return '';
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* This function detects which browser version is running this script,
|
|
1803
|
+
* parsing major and minor version (e.g., 42.1). User agent strings from:
|
|
1804
|
+
* http://www.useragentstring.com/pages/useragentstring.php
|
|
1805
|
+
*/
|
|
1806
|
+
browserVersion: function(userAgent, vendor, opera) {
|
|
1807
|
+
var browser = _.info.browser(userAgent, vendor, opera);
|
|
1808
|
+
var versionRegexs = {
|
|
1809
|
+
'Internet Explorer Mobile': /rv:(\d+(\.\d+)?)/,
|
|
1810
|
+
'Microsoft Edge': /Edge?\/(\d+(\.\d+)?)/,
|
|
1811
|
+
'Chrome': /Chrome\/(\d+(\.\d+)?)/,
|
|
1812
|
+
'Chrome iOS': /CriOS\/(\d+(\.\d+)?)/,
|
|
1813
|
+
'UC Browser' : /(UCBrowser|UCWEB)\/(\d+(\.\d+)?)/,
|
|
1814
|
+
'Safari': /Version\/(\d+(\.\d+)?)/,
|
|
1815
|
+
'Mobile Safari': /Version\/(\d+(\.\d+)?)/,
|
|
1816
|
+
'Opera': /(Opera|OPR)\/(\d+(\.\d+)?)/,
|
|
1817
|
+
'Firefox': /Firefox\/(\d+(\.\d+)?)/,
|
|
1818
|
+
'Firefox iOS': /FxiOS\/(\d+(\.\d+)?)/,
|
|
1819
|
+
'Konqueror': /Konqueror:(\d+(\.\d+)?)/,
|
|
1820
|
+
'BlackBerry': /BlackBerry (\d+(\.\d+)?)/,
|
|
1821
|
+
'Android Mobile': /android\s(\d+(\.\d+)?)/,
|
|
1822
|
+
'Samsung Internet': /SamsungBrowser\/(\d+(\.\d+)?)/,
|
|
1823
|
+
'Internet Explorer': /(rv:|MSIE )(\d+(\.\d+)?)/,
|
|
1824
|
+
'Mozilla': /rv:(\d+(\.\d+)?)/,
|
|
1825
|
+
'Whale Browser': /Whale\/(\d+(\.\d+)?)/
|
|
1826
|
+
};
|
|
1827
|
+
var regex = versionRegexs[browser];
|
|
1828
|
+
if (regex === undefined) {
|
|
1829
|
+
return null;
|
|
1830
|
+
}
|
|
1831
|
+
var matches = userAgent.match(regex);
|
|
1832
|
+
if (!matches) {
|
|
1833
|
+
return null;
|
|
1834
|
+
}
|
|
1835
|
+
return parseFloat(matches[matches.length - 2]);
|
|
1836
|
+
},
|
|
1837
|
+
|
|
1838
|
+
os: function() {
|
|
1839
|
+
var a = userAgent;
|
|
1840
|
+
if (/Windows/i.test(a)) {
|
|
1841
|
+
if (/Phone/.test(a) || /WPDesktop/.test(a)) {
|
|
1842
|
+
return 'Windows Phone';
|
|
1843
|
+
}
|
|
1844
|
+
return 'Windows';
|
|
1845
|
+
} else if (/(iPhone|iPad|iPod)/.test(a)) {
|
|
1846
|
+
return 'iOS';
|
|
1847
|
+
} else if (/Android/.test(a)) {
|
|
1848
|
+
return 'Android';
|
|
1849
|
+
} else if (/(BlackBerry|PlayBook|BB10)/i.test(a)) {
|
|
1850
|
+
return 'BlackBerry';
|
|
1851
|
+
} else if (/Mac/i.test(a)) {
|
|
1852
|
+
return 'Mac OS X';
|
|
1853
|
+
} else if (/Linux/.test(a)) {
|
|
1854
|
+
return 'Linux';
|
|
1855
|
+
} else if (/CrOS/.test(a)) {
|
|
1856
|
+
return 'Chrome OS';
|
|
1857
|
+
} else {
|
|
1858
|
+
return '';
|
|
1859
|
+
}
|
|
1860
|
+
},
|
|
1861
|
+
|
|
1862
|
+
device: function(user_agent) {
|
|
1863
|
+
if (/Windows Phone/i.test(user_agent) || /WPDesktop/.test(user_agent)) {
|
|
1864
|
+
return 'Windows Phone';
|
|
1865
|
+
} else if (/iPad/.test(user_agent)) {
|
|
1866
|
+
return 'iPad';
|
|
1867
|
+
} else if (/iPod/.test(user_agent)) {
|
|
1868
|
+
return 'iPod Touch';
|
|
1869
|
+
} else if (/iPhone/.test(user_agent)) {
|
|
1870
|
+
return 'iPhone';
|
|
1871
|
+
} else if (/(BlackBerry|PlayBook|BB10)/i.test(user_agent)) {
|
|
1872
|
+
return 'BlackBerry';
|
|
1873
|
+
} else if (/Android/.test(user_agent)) {
|
|
1874
|
+
return 'Android';
|
|
1875
|
+
} else {
|
|
1876
|
+
return '';
|
|
1877
|
+
}
|
|
1878
|
+
},
|
|
1879
|
+
|
|
1880
|
+
referringDomain: function(referrer) {
|
|
1881
|
+
var split = referrer.split('/');
|
|
1882
|
+
if (split.length >= 3) {
|
|
1883
|
+
return split[2];
|
|
1884
|
+
}
|
|
1885
|
+
return '';
|
|
1886
|
+
},
|
|
1887
|
+
|
|
1888
|
+
currentUrl: function() {
|
|
1889
|
+
return win.location.href;
|
|
1890
|
+
},
|
|
1891
|
+
|
|
1892
|
+
properties: function(extra_props) {
|
|
1893
|
+
if (typeof extra_props !== 'object') {
|
|
1894
|
+
extra_props = {};
|
|
1895
|
+
}
|
|
1896
|
+
return _.extend(_.strip_empty_properties({
|
|
1897
|
+
'$os': _.info.os(),
|
|
1898
|
+
'$browser': _.info.browser(userAgent, navigator.vendor, windowOpera),
|
|
1899
|
+
'$referrer': document.referrer,
|
|
1900
|
+
'$referring_domain': _.info.referringDomain(document.referrer),
|
|
1901
|
+
'$device': _.info.device(userAgent)
|
|
1902
|
+
}), {
|
|
1903
|
+
'$current_url': _.info.currentUrl(),
|
|
1904
|
+
'$browser_version': _.info.browserVersion(userAgent, navigator.vendor, windowOpera),
|
|
1905
|
+
'$screen_height': screen.height,
|
|
1906
|
+
'$screen_width': screen.width,
|
|
1907
|
+
'mp_lib': 'web',
|
|
1908
|
+
'$lib_version': Config.LIB_VERSION,
|
|
1909
|
+
'$insert_id': cheap_guid(),
|
|
1910
|
+
'time': _.timestamp() / 1000 // epoch time in seconds
|
|
1911
|
+
}, _.strip_empty_properties(extra_props));
|
|
1912
|
+
},
|
|
1913
|
+
|
|
1914
|
+
people_properties: function() {
|
|
1915
|
+
return _.extend(_.strip_empty_properties({
|
|
1916
|
+
'$os': _.info.os(),
|
|
1917
|
+
'$browser': _.info.browser(userAgent, navigator.vendor, windowOpera)
|
|
1918
|
+
}), {
|
|
1919
|
+
'$browser_version': _.info.browserVersion(userAgent, navigator.vendor, windowOpera)
|
|
1920
|
+
});
|
|
1921
|
+
},
|
|
1922
|
+
|
|
1923
|
+
mpPageViewProperties: function() {
|
|
1924
|
+
return _.strip_empty_properties({
|
|
1925
|
+
'current_page_title': document.title,
|
|
1926
|
+
'current_domain': win.location.hostname,
|
|
1927
|
+
'current_url_path': win.location.pathname,
|
|
1928
|
+
'current_url_protocol': win.location.protocol,
|
|
1929
|
+
'current_url_search': win.location.search
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
|
|
1934
|
+
var cheap_guid = function(maxlen) {
|
|
1935
|
+
var guid = Math.random().toString(36).substring(2, 10) + Math.random().toString(36).substring(2, 10);
|
|
1936
|
+
return maxlen ? guid.substring(0, maxlen) : guid;
|
|
1937
|
+
};
|
|
1938
|
+
|
|
1939
|
+
// naive way to extract domain name (example.com) from full hostname (my.sub.example.com)
|
|
1940
|
+
var SIMPLE_DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]*\.[a-z]+$/i;
|
|
1941
|
+
// this next one attempts to account for some ccSLDs, e.g. extracting oxford.ac.uk from www.oxford.ac.uk
|
|
1942
|
+
var DOMAIN_MATCH_REGEX = /[a-z0-9][a-z0-9-]+\.[a-z.]{2,6}$/i;
|
|
1943
|
+
/**
|
|
1944
|
+
* Attempts to extract main domain name from full hostname, using a few blunt heuristics. For
|
|
1945
|
+
* common TLDs like .com/.org that always have a simple SLD.TLD structure (example.com), we
|
|
1946
|
+
* simply extract the last two .-separated parts of the hostname (SIMPLE_DOMAIN_MATCH_REGEX).
|
|
1947
|
+
* For others, we attempt to account for short ccSLD+TLD combos (.ac.uk) with the legacy
|
|
1948
|
+
* DOMAIN_MATCH_REGEX (kept to maintain backwards compatibility with existing Mixpanel
|
|
1949
|
+
* integrations). The only _reliable_ way to extract domain from hostname is with an up-to-date
|
|
1950
|
+
* list like at https://publicsuffix.org/ so for cases that this helper fails at, the SDK
|
|
1951
|
+
* offers the 'cookie_domain' config option to set it explicitly.
|
|
1952
|
+
* @example
|
|
1953
|
+
* extract_domain('my.sub.example.com')
|
|
1954
|
+
* // 'example.com'
|
|
1955
|
+
*/
|
|
1956
|
+
var extract_domain = function(hostname) {
|
|
1957
|
+
var domain_regex = DOMAIN_MATCH_REGEX;
|
|
1958
|
+
var parts = hostname.split('.');
|
|
1959
|
+
var tld = parts[parts.length - 1];
|
|
1960
|
+
if (tld.length > 4 || tld === 'com' || tld === 'org') {
|
|
1961
|
+
domain_regex = SIMPLE_DOMAIN_MATCH_REGEX;
|
|
1962
|
+
}
|
|
1963
|
+
var matches = hostname.match(domain_regex);
|
|
1964
|
+
return matches ? matches[0] : '';
|
|
1965
|
+
};
|
|
1966
|
+
|
|
1967
|
+
var JSONStringify = null, JSONParse = null;
|
|
1968
|
+
if (typeof JSON !== 'undefined') {
|
|
1969
|
+
JSONStringify = JSON.stringify;
|
|
1970
|
+
JSONParse = JSON.parse;
|
|
1971
|
+
}
|
|
1972
|
+
JSONStringify = JSONStringify || _.JSONEncode;
|
|
1973
|
+
JSONParse = JSONParse || _.JSONDecode;
|
|
1974
|
+
|
|
1975
|
+
// UNMINIFIED EXPORTS (for closure compiler)
|
|
1976
|
+
_['info'] = _.info;
|
|
1977
|
+
_['info']['browser'] = _.info.browser;
|
|
1978
|
+
_['info']['browserVersion'] = _.info.browserVersion;
|
|
1979
|
+
_['info']['device'] = _.info.device;
|
|
1980
|
+
_['info']['properties'] = _.info.properties;
|
|
1981
|
+
_['isBlockedUA'] = _.isBlockedUA;
|
|
1982
|
+
_['isEmptyObject'] = _.isEmptyObject;
|
|
1983
|
+
_['isObject'] = _.isObject;
|
|
1984
|
+
_['JSONDecode'] = _.JSONDecode;
|
|
1985
|
+
_['JSONEncode'] = _.JSONEncode;
|
|
1986
|
+
_['toArray'] = _.toArray;
|
|
1987
|
+
_['NPO'] = NpoPromise;
|
|
1988
|
+
|
|
1989
|
+
function getDefaultExportFromCjs (x) {
|
|
1990
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
var logic$1 = {exports: {}};
|
|
1994
|
+
|
|
1995
|
+
/* globals define,module */
|
|
1996
|
+
var logic = logic$1.exports;
|
|
1997
|
+
|
|
1998
|
+
var hasRequiredLogic;
|
|
1999
|
+
|
|
2000
|
+
function requireLogic () {
|
|
2001
|
+
if (hasRequiredLogic) return logic$1.exports;
|
|
2002
|
+
hasRequiredLogic = 1;
|
|
2003
|
+
(function (module, exports) {
|
|
2004
|
+
(function(root, factory) {
|
|
2005
|
+
{
|
|
2006
|
+
module.exports = factory();
|
|
2007
|
+
}
|
|
2008
|
+
}(logic, function() {
|
|
2009
|
+
/* globals console:false */
|
|
2010
|
+
|
|
2011
|
+
if ( ! Array.isArray) {
|
|
2012
|
+
Array.isArray = function(arg) {
|
|
2013
|
+
return Object.prototype.toString.call(arg) === "[object Array]";
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
/**
|
|
2018
|
+
* Return an array that contains no duplicates (original not modified)
|
|
2019
|
+
* @param {array} array Original reference array
|
|
2020
|
+
* @return {array} New array with no duplicates
|
|
2021
|
+
*/
|
|
2022
|
+
function arrayUnique(array) {
|
|
2023
|
+
var a = [];
|
|
2024
|
+
for (var i=0, l=array.length; i<l; i++) {
|
|
2025
|
+
if (a.indexOf(array[i]) === -1) {
|
|
2026
|
+
a.push(array[i]);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
return a;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
var jsonLogic = {};
|
|
2033
|
+
var operations = {
|
|
2034
|
+
"==": function(a, b) {
|
|
2035
|
+
return a == b;
|
|
2036
|
+
},
|
|
2037
|
+
"===": function(a, b) {
|
|
2038
|
+
return a === b;
|
|
2039
|
+
},
|
|
2040
|
+
"!=": function(a, b) {
|
|
2041
|
+
return a != b;
|
|
2042
|
+
},
|
|
2043
|
+
"!==": function(a, b) {
|
|
2044
|
+
return a !== b;
|
|
2045
|
+
},
|
|
2046
|
+
">": function(a, b) {
|
|
2047
|
+
return a > b;
|
|
2048
|
+
},
|
|
2049
|
+
">=": function(a, b) {
|
|
2050
|
+
return a >= b;
|
|
2051
|
+
},
|
|
2052
|
+
"<": function(a, b, c) {
|
|
2053
|
+
return (c === undefined) ? a < b : (a < b) && (b < c);
|
|
2054
|
+
},
|
|
2055
|
+
"<=": function(a, b, c) {
|
|
2056
|
+
return (c === undefined) ? a <= b : (a <= b) && (b <= c);
|
|
2057
|
+
},
|
|
2058
|
+
"!!": function(a) {
|
|
2059
|
+
return jsonLogic.truthy(a);
|
|
2060
|
+
},
|
|
2061
|
+
"!": function(a) {
|
|
2062
|
+
return !jsonLogic.truthy(a);
|
|
2063
|
+
},
|
|
2064
|
+
"%": function(a, b) {
|
|
2065
|
+
return a % b;
|
|
2066
|
+
},
|
|
2067
|
+
"log": function(a) {
|
|
2068
|
+
console.log(a); return a;
|
|
2069
|
+
},
|
|
2070
|
+
"in": function(a, b) {
|
|
2071
|
+
if (!b || typeof b.indexOf === "undefined") return false;
|
|
2072
|
+
return (b.indexOf(a) !== -1);
|
|
2073
|
+
},
|
|
2074
|
+
"cat": function() {
|
|
2075
|
+
return Array.prototype.join.call(arguments, "");
|
|
2076
|
+
},
|
|
2077
|
+
"substr": function(source, start, end) {
|
|
2078
|
+
if (end < 0) {
|
|
2079
|
+
// JavaScript doesn't support negative end, this emulates PHP behavior
|
|
2080
|
+
var temp = String(source).substr(start);
|
|
2081
|
+
return temp.substr(0, temp.length + end);
|
|
2082
|
+
}
|
|
2083
|
+
return String(source).substr(start, end);
|
|
2084
|
+
},
|
|
2085
|
+
"+": function() {
|
|
2086
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
2087
|
+
return parseFloat(a, 10) + parseFloat(b, 10);
|
|
2088
|
+
}, 0);
|
|
2089
|
+
},
|
|
2090
|
+
"*": function() {
|
|
2091
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
2092
|
+
return parseFloat(a, 10) * parseFloat(b, 10);
|
|
2093
|
+
});
|
|
2094
|
+
},
|
|
2095
|
+
"-": function(a, b) {
|
|
2096
|
+
if (b === undefined) {
|
|
2097
|
+
return -a;
|
|
2098
|
+
} else {
|
|
2099
|
+
return a - b;
|
|
2100
|
+
}
|
|
2101
|
+
},
|
|
2102
|
+
"/": function(a, b) {
|
|
2103
|
+
return a / b;
|
|
2104
|
+
},
|
|
2105
|
+
"min": function() {
|
|
2106
|
+
return Math.min.apply(this, arguments);
|
|
2107
|
+
},
|
|
2108
|
+
"max": function() {
|
|
2109
|
+
return Math.max.apply(this, arguments);
|
|
2110
|
+
},
|
|
2111
|
+
"merge": function() {
|
|
2112
|
+
return Array.prototype.reduce.call(arguments, function(a, b) {
|
|
2113
|
+
return a.concat(b);
|
|
2114
|
+
}, []);
|
|
2115
|
+
},
|
|
2116
|
+
"var": function(a, b) {
|
|
2117
|
+
var not_found = (b === undefined) ? null : b;
|
|
2118
|
+
var data = this;
|
|
2119
|
+
if (typeof a === "undefined" || a==="" || a===null) {
|
|
2120
|
+
return data;
|
|
2121
|
+
}
|
|
2122
|
+
var sub_props = String(a).split(".");
|
|
2123
|
+
for (var i = 0; i < sub_props.length; i++) {
|
|
2124
|
+
if (data === null || data === undefined) {
|
|
2125
|
+
return not_found;
|
|
2126
|
+
}
|
|
2127
|
+
// Descending into data
|
|
2128
|
+
data = data[sub_props[i]];
|
|
2129
|
+
if (data === undefined) {
|
|
2130
|
+
return not_found;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
return data;
|
|
2134
|
+
},
|
|
2135
|
+
"missing": function() {
|
|
2136
|
+
/*
|
|
2137
|
+
Missing can receive many keys as many arguments, like {"missing:[1,2]}
|
|
2138
|
+
Missing can also receive *one* argument that is an array of keys,
|
|
2139
|
+
which typically happens if it's actually acting on the output of another command
|
|
2140
|
+
(like 'if' or 'merge')
|
|
2141
|
+
*/
|
|
2142
|
+
|
|
2143
|
+
var missing = [];
|
|
2144
|
+
var keys = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
|
2145
|
+
|
|
2146
|
+
for (var i = 0; i < keys.length; i++) {
|
|
2147
|
+
var key = keys[i];
|
|
2148
|
+
var value = jsonLogic.apply({"var": key}, this);
|
|
2149
|
+
if (value === null || value === "") {
|
|
2150
|
+
missing.push(key);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
return missing;
|
|
2155
|
+
},
|
|
2156
|
+
"missing_some": function(need_count, options) {
|
|
2157
|
+
// missing_some takes two arguments, how many (minimum) items must be present, and an array of keys (just like 'missing') to check for presence.
|
|
2158
|
+
var are_missing = jsonLogic.apply({"missing": options}, this);
|
|
2159
|
+
|
|
2160
|
+
if (options.length - are_missing.length >= need_count) {
|
|
2161
|
+
return [];
|
|
2162
|
+
} else {
|
|
2163
|
+
return are_missing;
|
|
2164
|
+
}
|
|
2165
|
+
},
|
|
2166
|
+
};
|
|
2167
|
+
|
|
2168
|
+
jsonLogic.is_logic = function(logic) {
|
|
2169
|
+
return (
|
|
2170
|
+
typeof logic === "object" && // An object
|
|
2171
|
+
logic !== null && // but not null
|
|
2172
|
+
! Array.isArray(logic) && // and not an array
|
|
2173
|
+
Object.keys(logic).length === 1 // with exactly one key
|
|
2174
|
+
);
|
|
2175
|
+
};
|
|
2176
|
+
|
|
2177
|
+
/*
|
|
2178
|
+
This helper will defer to the JsonLogic spec as a tie-breaker when different language interpreters define different behavior for the truthiness of primitives. E.g., PHP considers empty arrays to be falsy, but Javascript considers them to be truthy. JsonLogic, as an ecosystem, needs one consistent answer.
|
|
2179
|
+
|
|
2180
|
+
Spec and rationale here: http://jsonlogic.com/truthy
|
|
2181
|
+
*/
|
|
2182
|
+
jsonLogic.truthy = function(value) {
|
|
2183
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
2184
|
+
return false;
|
|
2185
|
+
}
|
|
2186
|
+
return !! value;
|
|
2187
|
+
};
|
|
2188
|
+
|
|
2189
|
+
|
|
2190
|
+
jsonLogic.get_operator = function(logic) {
|
|
2191
|
+
return Object.keys(logic)[0];
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
jsonLogic.get_values = function(logic) {
|
|
2195
|
+
return logic[jsonLogic.get_operator(logic)];
|
|
2196
|
+
};
|
|
2197
|
+
|
|
2198
|
+
jsonLogic.apply = function(logic, data) {
|
|
2199
|
+
// Does this array contain logic? Only one way to find out.
|
|
2200
|
+
if (Array.isArray(logic)) {
|
|
2201
|
+
return logic.map(function(l) {
|
|
2202
|
+
return jsonLogic.apply(l, data);
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
// You've recursed to a primitive, stop!
|
|
2206
|
+
if ( ! jsonLogic.is_logic(logic) ) {
|
|
2207
|
+
return logic;
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
var op = jsonLogic.get_operator(logic);
|
|
2211
|
+
var values = logic[op];
|
|
2212
|
+
var i;
|
|
2213
|
+
var current;
|
|
2214
|
+
var scopedLogic;
|
|
2215
|
+
var scopedData;
|
|
2216
|
+
var initial;
|
|
2217
|
+
|
|
2218
|
+
// easy syntax for unary operators, like {"var" : "x"} instead of strict {"var" : ["x"]}
|
|
2219
|
+
if ( ! Array.isArray(values)) {
|
|
2220
|
+
values = [values];
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// 'if', 'and', and 'or' violate the normal rule of depth-first calculating consequents, let each manage recursion as needed.
|
|
2224
|
+
if (op === "if" || op == "?:") {
|
|
2225
|
+
/* 'if' should be called with a odd number of parameters, 3 or greater
|
|
2226
|
+
This works on the pattern:
|
|
2227
|
+
if( 0 ){ 1 }else{ 2 };
|
|
2228
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else{ 4 };
|
|
2229
|
+
if( 0 ){ 1 }else if( 2 ){ 3 }else if( 4 ){ 5 }else{ 6 };
|
|
2230
|
+
|
|
2231
|
+
The implementation is:
|
|
2232
|
+
For pairs of values (0,1 then 2,3 then 4,5 etc)
|
|
2233
|
+
If the first evaluates truthy, evaluate and return the second
|
|
2234
|
+
If the first evaluates falsy, jump to the next pair (e.g, 0,1 to 2,3)
|
|
2235
|
+
given one parameter, evaluate and return it. (it's an Else and all the If/ElseIf were false)
|
|
2236
|
+
given 0 parameters, return NULL (not great practice, but there was no Else)
|
|
2237
|
+
*/
|
|
2238
|
+
for (i = 0; i < values.length - 1; i += 2) {
|
|
2239
|
+
if ( jsonLogic.truthy( jsonLogic.apply(values[i], data) ) ) {
|
|
2240
|
+
return jsonLogic.apply(values[i+1], data);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (values.length === i+1) {
|
|
2244
|
+
return jsonLogic.apply(values[i], data);
|
|
2245
|
+
}
|
|
2246
|
+
return null;
|
|
2247
|
+
} else if (op === "and") { // Return first falsy, or last
|
|
2248
|
+
for (i=0; i < values.length; i+=1) {
|
|
2249
|
+
current = jsonLogic.apply(values[i], data);
|
|
2250
|
+
if ( ! jsonLogic.truthy(current)) {
|
|
2251
|
+
return current;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
return current; // Last
|
|
2255
|
+
} else if (op === "or") {// Return first truthy, or last
|
|
2256
|
+
for (i=0; i < values.length; i+=1) {
|
|
2257
|
+
current = jsonLogic.apply(values[i], data);
|
|
2258
|
+
if ( jsonLogic.truthy(current) ) {
|
|
2259
|
+
return current;
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
return current; // Last
|
|
2263
|
+
} else if (op === "filter") {
|
|
2264
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2265
|
+
scopedLogic = values[1];
|
|
2266
|
+
|
|
2267
|
+
if ( ! Array.isArray(scopedData)) {
|
|
2268
|
+
return [];
|
|
2269
|
+
}
|
|
2270
|
+
// Return only the elements from the array in the first argument,
|
|
2271
|
+
// that return truthy when passed to the logic in the second argument.
|
|
2272
|
+
// For parity with JavaScript, reindex the returned array
|
|
2273
|
+
return scopedData.filter(function(datum) {
|
|
2274
|
+
return jsonLogic.truthy( jsonLogic.apply(scopedLogic, datum));
|
|
2275
|
+
});
|
|
2276
|
+
} else if (op === "map") {
|
|
2277
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2278
|
+
scopedLogic = values[1];
|
|
2279
|
+
|
|
2280
|
+
if ( ! Array.isArray(scopedData)) {
|
|
2281
|
+
return [];
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
return scopedData.map(function(datum) {
|
|
2285
|
+
return jsonLogic.apply(scopedLogic, datum);
|
|
2286
|
+
});
|
|
2287
|
+
} else if (op === "reduce") {
|
|
2288
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2289
|
+
scopedLogic = values[1];
|
|
2290
|
+
initial = typeof values[2] !== "undefined" ? jsonLogic.apply(values[2], data) : null;
|
|
2291
|
+
|
|
2292
|
+
if ( ! Array.isArray(scopedData)) {
|
|
2293
|
+
return initial;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
return scopedData.reduce(
|
|
2297
|
+
function(accumulator, current) {
|
|
2298
|
+
return jsonLogic.apply(
|
|
2299
|
+
scopedLogic,
|
|
2300
|
+
{current: current, accumulator: accumulator}
|
|
2301
|
+
);
|
|
2302
|
+
},
|
|
2303
|
+
initial
|
|
2304
|
+
);
|
|
2305
|
+
} else if (op === "all") {
|
|
2306
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2307
|
+
scopedLogic = values[1];
|
|
2308
|
+
// All of an empty set is false. Note, some and none have correct fallback after the for loop
|
|
2309
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
2310
|
+
return false;
|
|
2311
|
+
}
|
|
2312
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
2313
|
+
if ( ! jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
2314
|
+
return false; // First falsy, short circuit
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
return true; // All were truthy
|
|
2318
|
+
} else if (op === "none") {
|
|
2319
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2320
|
+
scopedLogic = values[1];
|
|
2321
|
+
|
|
2322
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
2323
|
+
return true;
|
|
2324
|
+
}
|
|
2325
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
2326
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
2327
|
+
return false; // First truthy, short circuit
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
return true; // None were truthy
|
|
2331
|
+
} else if (op === "some") {
|
|
2332
|
+
scopedData = jsonLogic.apply(values[0], data);
|
|
2333
|
+
scopedLogic = values[1];
|
|
2334
|
+
|
|
2335
|
+
if ( ! Array.isArray(scopedData) || ! scopedData.length) {
|
|
2336
|
+
return false;
|
|
2337
|
+
}
|
|
2338
|
+
for (i=0; i < scopedData.length; i+=1) {
|
|
2339
|
+
if ( jsonLogic.truthy( jsonLogic.apply(scopedLogic, scopedData[i]) )) {
|
|
2340
|
+
return true; // First truthy, short circuit
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
return false; // None were truthy
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2346
|
+
// Everyone else gets immediate depth-first recursion
|
|
2347
|
+
values = values.map(function(val) {
|
|
2348
|
+
return jsonLogic.apply(val, data);
|
|
2349
|
+
});
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
// The operation is called with "data" bound to its "this" and "values" passed as arguments.
|
|
2353
|
+
// Structured commands like % or > can name formal arguments while flexible commands (like missing or merge) can operate on the pseudo-array arguments
|
|
2354
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
|
|
2355
|
+
if (operations.hasOwnProperty(op) && typeof operations[op] === "function") {
|
|
2356
|
+
return operations[op].apply(data, values);
|
|
2357
|
+
} else if (op.indexOf(".") > 0) { // Contains a dot, and not in the 0th position
|
|
2358
|
+
var sub_ops = String(op).split(".");
|
|
2359
|
+
var operation = operations;
|
|
2360
|
+
for (i = 0; i < sub_ops.length; i++) {
|
|
2361
|
+
if (!operation.hasOwnProperty(sub_ops[i])) {
|
|
2362
|
+
throw new Error("Unrecognized operation " + op +
|
|
2363
|
+
" (failed at " + sub_ops.slice(0, i+1).join(".") + ")");
|
|
2364
|
+
}
|
|
2365
|
+
// Descending into operations
|
|
2366
|
+
operation = operation[sub_ops[i]];
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
return operation.apply(data, values);
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2372
|
+
throw new Error("Unrecognized operation " + op );
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2375
|
+
jsonLogic.uses_data = function(logic) {
|
|
2376
|
+
var collection = [];
|
|
2377
|
+
|
|
2378
|
+
if (jsonLogic.is_logic(logic)) {
|
|
2379
|
+
var op = jsonLogic.get_operator(logic);
|
|
2380
|
+
var values = logic[op];
|
|
2381
|
+
|
|
2382
|
+
if ( ! Array.isArray(values)) {
|
|
2383
|
+
values = [values];
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
if (op === "var") {
|
|
2387
|
+
// This doesn't cover the case where the arg to var is itself a rule.
|
|
2388
|
+
collection.push(values[0]);
|
|
2389
|
+
} else {
|
|
2390
|
+
// Recursion!
|
|
2391
|
+
values.forEach(function(val) {
|
|
2392
|
+
collection.push.apply(collection, jsonLogic.uses_data(val) );
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
return arrayUnique(collection);
|
|
2398
|
+
};
|
|
2399
|
+
|
|
2400
|
+
jsonLogic.add_operation = function(name, code) {
|
|
2401
|
+
operations[name] = code;
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
jsonLogic.rm_operation = function(name) {
|
|
2405
|
+
delete operations[name];
|
|
2406
|
+
};
|
|
2407
|
+
|
|
2408
|
+
jsonLogic.rule_like = function(rule, pattern) {
|
|
2409
|
+
// console.log("Is ". JSON.stringify(rule) . " like " . JSON.stringify(pattern) . "?");
|
|
2410
|
+
if (pattern === rule) {
|
|
2411
|
+
return true;
|
|
2412
|
+
} // TODO : Deep object equivalency?
|
|
2413
|
+
if (pattern === "@") {
|
|
2414
|
+
return true;
|
|
2415
|
+
} // Wildcard!
|
|
2416
|
+
if (pattern === "number") {
|
|
2417
|
+
return (typeof rule === "number");
|
|
2418
|
+
}
|
|
2419
|
+
if (pattern === "string") {
|
|
2420
|
+
return (typeof rule === "string");
|
|
2421
|
+
}
|
|
2422
|
+
if (pattern === "array") {
|
|
2423
|
+
// !logic test might be superfluous in JavaScript
|
|
2424
|
+
return Array.isArray(rule) && ! jsonLogic.is_logic(rule);
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
if (jsonLogic.is_logic(pattern)) {
|
|
2428
|
+
if (jsonLogic.is_logic(rule)) {
|
|
2429
|
+
var pattern_op = jsonLogic.get_operator(pattern);
|
|
2430
|
+
var rule_op = jsonLogic.get_operator(rule);
|
|
2431
|
+
|
|
2432
|
+
if (pattern_op === "@" || pattern_op === rule_op) {
|
|
2433
|
+
// echo "\nOperators match, go deeper\n";
|
|
2434
|
+
return jsonLogic.rule_like(
|
|
2435
|
+
jsonLogic.get_values(rule, false),
|
|
2436
|
+
jsonLogic.get_values(pattern, false)
|
|
2437
|
+
);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
return false; // pattern is logic, rule isn't, can't be eq
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
if (Array.isArray(pattern)) {
|
|
2444
|
+
if (Array.isArray(rule)) {
|
|
2445
|
+
if (pattern.length !== rule.length) {
|
|
2446
|
+
return false;
|
|
2447
|
+
}
|
|
2448
|
+
/*
|
|
2449
|
+
Note, array order MATTERS, because we're using this array test logic to consider arguments, where order can matter. (e.g., + is commutative, but '-' or 'if' or 'var' are NOT)
|
|
2450
|
+
*/
|
|
2451
|
+
for (var i = 0; i < pattern.length; i += 1) {
|
|
2452
|
+
// If any fail, we fail
|
|
2453
|
+
if ( ! jsonLogic.rule_like(rule[i], pattern[i])) {
|
|
2454
|
+
return false;
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
return true; // If they *all* passed, we pass
|
|
2458
|
+
} else {
|
|
2459
|
+
return false; // Pattern is array, rule isn't
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
// Not logic, not array, not a === match for rule.
|
|
2464
|
+
return false;
|
|
2465
|
+
};
|
|
2466
|
+
|
|
2467
|
+
return jsonLogic;
|
|
2468
|
+
}));
|
|
2469
|
+
} (logic$1));
|
|
2470
|
+
return logic$1.exports;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
var logicExports = requireLogic();
|
|
2474
|
+
var jsonLogic = /*@__PURE__*/getDefaultExportFromCjs(logicExports);
|
|
2475
|
+
|
|
2476
|
+
/**
|
|
2477
|
+
* Check if an event matches the given criteria
|
|
2478
|
+
* @param {string} eventName - The name of the event being checked
|
|
2479
|
+
* @param {Object} properties - Event properties to evaluate against property filters
|
|
2480
|
+
* @param {Object} criteria - Criteria to match against, with:
|
|
2481
|
+
* - event_name: string - Required event name (case-sensitive match)
|
|
2482
|
+
* - property_filters: Object - Optional JsonLogic filters for properties
|
|
2483
|
+
* @returns {Object} Result object with:
|
|
2484
|
+
* - matches: boolean - Whether the event matches the criteria
|
|
2485
|
+
* - error: string|undefined - Error message if evaluation failed
|
|
2486
|
+
*/
|
|
2487
|
+
var eventMatchesCriteria = function(eventName, properties, criteria) {
|
|
2488
|
+
// Check exact event name match (case-sensitive)
|
|
2489
|
+
if (eventName !== criteria.event_name) {
|
|
2490
|
+
return { matches: false };
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
// Evaluate property filters using JsonLogic
|
|
2494
|
+
var propertyFilters = criteria.property_filters;
|
|
2495
|
+
var filtersMatch = true; // default to true if no filters
|
|
2496
|
+
|
|
2497
|
+
if (propertyFilters && !_.isEmptyObject(propertyFilters)) {
|
|
2498
|
+
try {
|
|
2499
|
+
// Use properties as-is for case-sensitive matching
|
|
2500
|
+
filtersMatch = jsonLogic.apply(propertyFilters, properties || {});
|
|
2501
|
+
} catch (error) {
|
|
2502
|
+
return {
|
|
2503
|
+
matches: false,
|
|
2504
|
+
error: error.toString()
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
return { matches: filtersMatch };
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2512
|
+
// Create targeting library object
|
|
2513
|
+
var targetingLibrary = {};
|
|
2514
|
+
targetingLibrary['eventMatchesCriteria'] = eventMatchesCriteria;
|
|
2515
|
+
|
|
2516
|
+
// Set global Promise (use bracket notation to prevent minification)
|
|
2517
|
+
// This is the ONE AND ONLY global - matches recorder pattern
|
|
2518
|
+
win[TARGETING_GLOBAL_NAME] = Promise.resolve(targetingLibrary);
|
|
2519
|
+
|
|
2520
|
+
})();
|