This repository was archived by the owner on Feb 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 408
/
Copy pathutils.ts
306 lines (273 loc) · 9.87 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
// Hack since TypeScript isn't compiling this for a worker.
declare var WorkerGlobalScope;
export var zoneSymbol: (name: string) => string = Zone['__symbol__'];
const _global = typeof window == 'undefined' ? global : window;
export function bindArguments(args: any[], source: string): any[] {
for (var i = args.length - 1; i >= 0; i--) {
if (typeof args[i] === 'function') {
args[i] = Zone.current.wrap(args[i], source + '_' + i);
}
}
return args;
};
export function patchPrototype(prototype, fnNames) {
var source = prototype.constructor['name'];
for (var i = 0; i < fnNames.length; i++) {
var name = fnNames[i];
var delegate = prototype[name];
if (delegate) {
prototype[name] = ((delegate: Function) => {
return function() {
return delegate.apply(this, bindArguments(<any>arguments, source + '.' + name));
};
})(delegate);
}
}
};
export var isWebWorker: boolean =
(typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope);
export var isNode: boolean =
(typeof process !== 'undefined' && {}.toString.call(process) === '[object process]');
export var isBrowser: boolean =
!isNode && !isWebWorker && !!(window && window['HTMLElement']);
export function patchProperty(obj, prop) {
var desc = Object.getOwnPropertyDescriptor(obj, prop) || {
enumerable: true,
configurable: true
};
// A property descriptor cannot have getter/setter and be writable
// deleting the writable and value properties avoids this error:
//
// TypeError: property descriptors must not specify a value or be writable when a
// getter or setter has been specified
delete desc.writable;
delete desc.value;
// substr(2) cuz 'onclick' -> 'click', etc
var eventName = prop.substr(2);
var _prop = '_' + prop;
desc.set = function (fn) {
if (this[_prop]) {
this.removeEventListener(eventName, this[_prop]);
}
if (typeof fn === 'function') {
this[_prop] = fn;
this.addEventListener(eventName, fn, false);
} else {
this[_prop] = null;
}
};
desc.get = function () {
return this[_prop];
};
Object.defineProperty(obj, prop, desc);
};
export function patchOnProperties(obj: any, properties: string[]) {
var onProperties = [];
for (var prop in obj) {
if (prop.substr(0, 2) == 'on') {
onProperties.push(prop);
}
}
for(var j = 0; j < onProperties.length; j++) {
patchProperty(obj, onProperties[j]);
}
if (properties) {
for(var i = 0; i < properties.length; i++) {
patchProperty(obj, 'on' + properties[i]);
}
}
};
const EVENT_TASKS = zoneSymbol('eventTasks');
const ADD_EVENT_LISTENER = 'addEventListener';
const REMOVE_EVENT_LISTENER = 'removeEventListener';
const SYMBOL_ADD_EVENT_LISTENER = zoneSymbol(ADD_EVENT_LISTENER);
const SYMBOL_REMOVE_EVENT_LISTENER = zoneSymbol(REMOVE_EVENT_LISTENER);
interface ListenerTaskMeta extends TaskData {
useCapturing: boolean,
eventName: string,
handler: any,
target: any,
name: any
}
function findExistingRegisteredTask(target: any, handler: any, name: string, capture: boolean,
remove: boolean): Task {
var eventTasks: Task[] = target[EVENT_TASKS];
if (eventTasks) {
for (var i = 0; i < eventTasks.length; i++) {
var eventTask = eventTasks[i];
var data = <ListenerTaskMeta>eventTask.data;
if (data.handler === handler
&& data.useCapturing === capture
&& data.eventName === name)
{
if (remove) {
eventTasks.splice(i, 1);
}
return eventTask;
}
}
}
return null;
}
function attachRegisteredEvent(target: any, eventTask: Task): void {
var eventTasks: Task[] = target[EVENT_TASKS];
if (!eventTasks) {
eventTasks = target[EVENT_TASKS] = [];
}
eventTasks.push(eventTask);
}
function scheduleEventListener(eventTask: Task): any {
var meta = <ListenerTaskMeta>eventTask.data;
attachRegisteredEvent(meta.target, eventTask);
return meta.target[SYMBOL_ADD_EVENT_LISTENER](meta.eventName, eventTask.invoke,
meta.useCapturing);
}
function cancelEventListener(eventTask: Task): void {
var meta = <ListenerTaskMeta>eventTask.data;
findExistingRegisteredTask(meta.target, eventTask.invoke, meta.eventName,
meta.useCapturing, true);
meta.target[SYMBOL_REMOVE_EVENT_LISTENER](meta.eventName, eventTask.invoke,
meta.useCapturing);
}
function zoneAwareAddEventListener(self: any, args: any[]) {
var eventName: string = args[0];
var handler: EventListenerOrEventListenerObject = args[1];
var useCapturing: boolean = args[2] || false;
// - Inside a Web Worker, `this` is undefined, the context is `global`
// - When `addEventListener` is called on the global context in strict mode, `this` is undefined
// see https://github.com/angular/zone.js/issues/190
var target = self || _global;
var delegate: EventListener = null;
if (typeof handler == 'function') {
delegate = <EventListener>handler;
} else if (handler && (<EventListenerObject>handler).handleEvent) {
delegate = (event) => (<EventListenerObject>handler).handleEvent(event);
}
// Ignore special listeners of IE11 & Edge dev tools, see https://github.com/angular/zone.js/issues/150
if (!delegate || handler && handler.toString() === "[object FunctionWrapper]") {
return target[SYMBOL_ADD_EVENT_LISTENER](eventName, handler, useCapturing);
}
var eventTask: Task
= findExistingRegisteredTask(target, handler, eventName, useCapturing, false);
if (eventTask) {
// we already registered, so this will have noop.
return target[SYMBOL_ADD_EVENT_LISTENER](eventName, eventTask.invoke, useCapturing);
}
var zone: Zone = Zone.current;
var source = target.constructor['name'] + '.addEventListener:' + eventName;
var data: ListenerTaskMeta = {
target: target,
eventName: eventName,
name: eventName,
useCapturing: useCapturing,
handler: handler
};
zone.scheduleEventTask(source, delegate, data, scheduleEventListener, cancelEventListener);
}
function zoneAwareRemoveEventListener(self: any, args: any[]) {
var eventName: string = args[0];
var handler: EventListenerOrEventListenerObject = args[1];
var useCapturing: boolean = args[2] || false;
// - Inside a Web Worker, `this` is undefined, the context is `global`
// - When `addEventListener` is called on the global context in strict mode, `this` is undefined
// see https://github.com/angular/zone.js/issues/190
var target = self || _global;
var eventTask = findExistingRegisteredTask(target, handler, eventName, useCapturing, false);
if (eventTask) {
eventTask.zone.cancelTask(eventTask);
} else {
target[SYMBOL_REMOVE_EVENT_LISTENER](eventName, handler, useCapturing);
}
}
export function patchEventTargetMethods(obj: any): boolean {
if (obj && obj.addEventListener) {
patchMethod(obj, ADD_EVENT_LISTENER, () => zoneAwareAddEventListener);
patchMethod(obj, REMOVE_EVENT_LISTENER, () => zoneAwareRemoveEventListener);
return true;
} else {
return false;
}
};
var originalInstanceKey = zoneSymbol('originalInstance');
// wrap some native API on `window`
export function patchClass(className) {
var OriginalClass = _global[className];
if (!OriginalClass) return;
_global[className] = function () {
var a = bindArguments(<any>arguments, className);
switch (a.length) {
case 0: this[originalInstanceKey] = new OriginalClass(); break;
case 1: this[originalInstanceKey] = new OriginalClass(a[0]); break;
case 2: this[originalInstanceKey] = new OriginalClass(a[0], a[1]); break;
case 3: this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2]); break;
case 4: this[originalInstanceKey] = new OriginalClass(a[0], a[1], a[2], a[3]); break;
default: throw new Error('Arg list too long.');
}
};
var instance = new OriginalClass(function () {});
var prop;
for (prop in instance) {
(function (prop) {
if (typeof instance[prop] === 'function') {
_global[className].prototype[prop] = function () {
return this[originalInstanceKey][prop].apply(this[originalInstanceKey], arguments);
};
} else {
Object.defineProperty(_global[className].prototype, prop, {
set: function (fn) {
if (typeof fn === 'function') {
this[originalInstanceKey][prop] = Zone.current.wrap(fn, className + '.' + prop);
} else {
this[originalInstanceKey][prop] = fn;
}
},
get: function () {
return this[originalInstanceKey][prop];
}
});
}
}(prop));
}
for (prop in OriginalClass) {
if (prop !== 'prototype' && OriginalClass.hasOwnProperty(prop)) {
_global[className][prop] = OriginalClass[prop];
}
}
};
export function createNamedFn(
name: string,
delegate: (self: any, args: any[]) => any): Function
{
try {
return (Function(
'f',
`return function ${name}(){return f(this, arguments)}`)
)(delegate);
} catch (e) {
// if we fail, we must be CSP, just return delegate.
return function() {
return delegate(this, <any>arguments);
};
}
}
export function patchMethod(target: any, name: string,
patchFn: (delegate: Function,
delegateName: string,
name: string) => (self: any, args: any[]) => any): Function
{
var proto = target;
while (proto && !proto.hasOwnProperty(name)) {
proto = Object.getPrototypeOf(proto);
}
if (!proto && target[name]) {
// somehow we did not find it, but we can see it. This happens on IE for Window properties.
proto = target;
}
var delegateName = zoneSymbol(name);
var delegate: Function;
if (proto && ! (delegate = proto[delegateName])) {
delegate = proto[delegateName] = proto[name];
proto[name] = createNamedFn(name, patchFn(delegate, delegateName, name));
}
return delegate;
}