forked from SuperMap/iClient-JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEvents.js
More file actions
529 lines (479 loc) · 19.4 KB
/
Copy pathEvents.js
File metadata and controls
529 lines (479 loc) · 19.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
/* Copyright© 2000 - 2021 SuperMap Software Co.Ltd. All rights reserved.
* This program are made available under the terms of the Apache License, Version 2.0
* which accompanies this distribution and is available at http://www.apache.org/licenses/LICENSE-2.0.html.*/
import {SuperMap} from '../SuperMap';
import {Pixel} from './Pixel';
import {Event} from './Event';
import {FunctionExt} from './BaseTypes';
import {Util} from './Util';
/**
* @class SuperMap.Events
* @classdesc 事件类。
* @param {Object} object - 当前事件对象被添加到的 JS 对象。
* @param {HTMLElement} element - 响应浏览器事件的 DOM 元素。
* @param {Array.<string>} eventTypes - 自定义应用事件的数组。
* @param {boolean} [fallThrough=false] - 是否允许事件处理之后向上传递(冒泡),为 false 的时候阻止事件冒泡。
* @param {Object} options - 事件对象选项。
*/
export class Events {
constructor(object, element, eventTypes, fallThrough, options) {
/**
* @member {Array.<string>} SuperMap.Events.prototype.BROWSER_EVENTS
* @description 支持的事件。
* @constant
* @default [
"mouseover", "mouseout","mousedown", "mouseup", "mousemove",
"click", "dblclick", "rightclick", "dblrightclick","resize",
"focus", "blur","touchstart", "touchmove", "touchend","keydown",
"MSPointerDown", "MSPointerUp", "pointerdown", "pointerup",
"MSGestureStart", "MSGestureChange", "MSGestureEnd","contextmenu"
]
*/
this.BROWSER_EVENTS = [
"mouseover", "mouseout",
"mousedown", "mouseup", "mousemove",
"click", "dblclick", "rightclick", "dblrightclick",
"resize", "focus", "blur",
"touchstart", "touchmove", "touchend",
"keydown", "MSPointerDown", "MSPointerUp", "pointerdown", "pointerup",
"MSGestureStart", "MSGestureChange", "MSGestureEnd",
"contextmenu"
];
/**
* @member {Object} SuperMap.Events.prototype.listeners
* @description Hashtable of Array(function): events listener functions
*/
this.listeners = {};
/**
* @member {Object} SuperMap.Events.prototype.object
* @description 发布应用程序事件的对象。
*/
this.object = object;
/**
* @member {HTMLElement} SuperMap.Events.prototype.element
* @description 接受浏览器事件的 DOM 节点。
*/
this.element = null;
/**
* @member {Array.<string>} SuperMap.Events.prototype.eventTypes
* @description 支持的事件类型列表。
*/
this.eventTypes = [];
/**
* @member {function} SuperMap.Events.prototype.eventHandler
* @description 绑定在元素上的事件处理器对象。
*/
this.eventHandler = null;
/**
* @member {boolean} [SuperMap.Events.prototype.fallThrough=false]
* @description 是否允许事件处理之后向上传递(冒泡),为 false 的时候阻止事件冒泡。
*/
this.fallThrough = fallThrough;
/**
* @member {boolean} [SuperMap.Events.prototype.includeXY=false]
* @description 判断是否让 xy 属性自动创建到浏览器上的鼠标事件,一般设置为 false,如果设置为 true,鼠标事件将会在事件传递过程中自动产生 xy 属性。
* 可根据事件对象的 'evt.object' 属性在相关的事件句柄上调用 getMousePosition 函数。这个选项习惯默认为 false 的原因在于,当创建一个
* 事件对象,其主要目的是管理。在一个 div 的相对定位的鼠标事件,将其设为 true 也是有意义的。这个选项也可以用来控制是否抵消缓存。如果
* 设为 false 不抵消,如果设为 true,用 this.clearMouseCache() 清除缓存偏移(边界元素偏移,元素在页面的位置偏移)。
* @example
* function named(evt) {
* this.xy = this.object.events.getMousePosition(evt);
* }
*/
this.includeXY = false;
/**
* @member {Object} SuperMap.Events.prototype.extensions
* @description 事件扩展。Keys 代表事件类型,values 代表事件对象。
* @example
* 以扩展 "foostart" 和 "fooend" 事件为例。展示替换 css 属性为 foo 的元素的 click 事件。
*
* SuperMap.Events.foostart = SuperMap.Class({
* initialize: function(target) {
* this.target = target;
* this.target.register("click", this, this.doStuff, {extension: true});
* // only required if extension provides more than one event type
* this.target.extensions["foostart"] = true;
* this.target.extensions["fooend"] = true;
* },
* destroy: function() {
* var target = this.target;
* target.unregister("click", this, this.doStuff);
* delete this.target;
* // only required if extension provides more than one event type
* delete target.extensions["foostart"];
* delete target.extensions["fooend"];
* },
* doStuff: function(evt) {
* var propagate = true;
* if (SuperMap.Event.element(evt).className === "foo") {
* propagate = false;
* var target = this.target;
* target.triggerEvent("foostart");
* window.setTimeout(function() {
* target.triggerEvent("fooend");
* }, 1000);
* }
* return propagate;
* }
* });
* // only required if extension provides more than one event type
* SuperMap.Events.fooend = SuperMap.Events.foostart;
*/
this.extensions = {};
/**
* @member {Object} SuperMap.Events.prototype.extensionCount
*/
this.extensionCount = {};
/**
* @member {Object} SuperMap.Events.prototype.clearMouseListener
*/
this.clearMouseListener = null;
Util.extend(this, options);
if (eventTypes != null) {
for (var i = 0, len = eventTypes.length; i < len; i++) {
this.addEventType(eventTypes[i]);
}
}
if (element != null) {
this.attachToElement(element);
}
this.CLASS_NAME = "SuperMap.Events";
}
/**
* @function SuperMap.Events.prototype.destroy
* @description 移除当前要素 element 上的所有事件监听和处理。
*/
destroy() {
for (var e in this.extensions) {
if (typeof this.extensions[e] !== "boolean") {
this.extensions[e].destroy();
}
}
this.extensions = null;
if (this.element) {
Event.stopObservingElement(this.element);
if (this.element.hasScrollEvent) {
Event.stopObserving(
window, "scroll", this.clearMouseListener
);
}
}
this.element = null;
this.listeners = null;
this.object = null;
this.eventTypes = null;
this.fallThrough = null;
this.eventHandler = null;
}
/**
* @function SuperMap.Events.prototype.addEventType
* @description 在此事件对象中添加新的事件类型,如果这个事件类型已经添加过了,则不做任何事情。
* @param {string} eventName - 事件名。
*/
addEventType(eventName) {
if (!this.listeners[eventName]) {
this.eventTypes.push(eventName);
this.listeners[eventName] = [];
}
}
/**
* @function SuperMap.Events.prototype.attachToElement
* @description 给 DOM 元素绑定浏览器事件。
* @param {HTMLDOMElement} element - 绑定浏览器事件的 DOM 元素。
*/
attachToElement(element) {
if (this.element) {
Event.stopObservingElement(this.element);
} else {
// keep a bound copy of handleBrowserEvent() so that we can
// pass the same function to both Event.observe() and .stopObserving()
this.eventHandler = FunctionExt.bindAsEventListener(
this.handleBrowserEvent, this
);
// to be used with observe and stopObserving
this.clearMouseListener = FunctionExt.bind(
this.clearMouseCache, this
);
}
this.element = element;
for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
var eventType = this.BROWSER_EVENTS[i];
// every browser event has a corresponding application event
// (whether it's listened for or not).
this.addEventType(eventType);
// use Prototype to register the event cross-browser
Event.observe(element, eventType, this.eventHandler);
}
// disable dragstart in IE so that mousedown/move/up works normally
Event.observe(element, "dragstart", Event.stop);
}
/**
* @function SuperMap.Events.prototype.on
* @description 在一个相同的范围内注册监听器的方法,此方法调用 register 函数。
* @example
* // 注册一个 "loadstart" 监听事件
* events.on({"loadstart": loadStartListener});
*
* // 同样注册一个 "loadstart" 监听事件
* events.register("loadstart", undefined, loadStartListener);
*
* // 同时为对象注册多个监听事件
* events.on({
* "loadstart": loadStartListener,
* "loadend": loadEndListener,
* scope: object
* });
*
* // 同时为对象注册多个监听事件,多次调用 register 方法
* events.register("loadstart", object, loadStartListener);
* events.register("loadend", object, loadEndListener);
*
*
* @param {Object} object - 添加监听的对象。
*/
on(object) {
for (var type in object) {
if (type !== "scope" && object.hasOwnProperty(type)) {
this.register(type, object.scope, object[type]);
}
}
}
/**
* @function SuperMap.Events.prototype.register
* @description 在事件对象上注册一个事件。当事件被触发时,'func' 函数被调用,假设我们触发一个事件,
* 指定 SuperMap.Bounds 作为 "obj",当事件被触发时,回调函数的上下文作为 Bounds 对象。
* @param {string} type - 事件注册者的名字。
* @param {Object} [obj=this.object] - 对象绑定的回调。
* @param {function} [func] - 回调函数,如果没有特定的回调,则这个函数不做任何事情。
* @param {(boolean|Object)} [priority] - 当为 true 时将新的监听加在事件队列的前面。
*/
register(type, obj, func, priority) {
if (type in Events && !this.extensions[type]) {
this.extensions[type] = new Events[type](this);
}
if ((func != null) &&
(Util.indexOf(this.eventTypes, type) !== -1)) {
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
if (!listeners) {
listeners = [];
this.listeners[type] = listeners;
this.extensionCount[type] = 0;
}
var listener = {obj: obj, func: func};
if (priority) {
listeners.splice(this.extensionCount[type], 0, listener);
if (typeof priority === "object" && priority.extension) {
this.extensionCount[type]++;
}
} else {
listeners.push(listener);
}
}
}
/**
* @function SuperMap.Events.prototype.registerPriority
* @description 相同的注册方法,但是在前面增加新的监听者事件查询而代替到方法的结束。
* @param {string} type - 事件注册者的名字。
* @param {Object} [obj=this.object] - 对象绑定方面的回调。
* @param {function} [func] - 回调函数,如果没有特定的回调,则这个函数不做任何事情。
*/
registerPriority(type, obj, func) {
this.register(type, obj, func, true);
}
/**
* @function SuperMap.Events.prototype.un
* @description 在一个相同的范围内取消注册监听器的方法,此方法调用 unregister 函数。
* @example
* // 移除 "loadstart" 事件监听
* events.un({"loadstart": loadStartListener});
*
* // 使用 "unregister" 方法移除 "loadstart" 事件监听
* events.unregister("loadstart", undefined, loadStartListener);
*
* // 取消对象多个事件监听
* events.un({
* "loadstart": loadStartListener,
* "loadend": loadEndListener,
* scope: object
* });
*
* // 取消对象多个事件监听,多次调用unregister方法。
* events.unregister("loadstart", object, loadStartListener);
* events.unregister("loadend", object, loadEndListener);
*
* @param {Object} object - 移除监听的对象。
*/
un(object) {
for (var type in object) {
if (type !== "scope" && object.hasOwnProperty(type)) {
this.unregister(type, object.scope, object[type]);
}
}
}
/**
* @function SuperMap.Events.prototype.unregister
* @description 取消注册。
* @param {string} type - 事件类型。
* @param {Object} [obj=this.object] - 对象绑定方面的回调。
* @param {function} [func] - 回调函数,如果没有特定的回调,则这个函数不做任何事情。
*/
unregister(type, obj, func) {
if (obj == null) {
obj = this.object;
}
var listeners = this.listeners[type];
if (listeners != null) {
for (var i = 0, len = listeners.length; i < len; i++) {
if (listeners[i].obj === obj && listeners[i].func === func) {
listeners.splice(i, 1);
break;
}
}
}
}
/**
* @function SuperMap.Events.prototype.remove
* @description 删除某个事件类型的所有监听,如果该事件类型没有注册,则不做任何操作。
* @param {string} type - 事件类型。
*/
remove(type) {
if (this.listeners[type] != null) {
this.listeners[type] = [];
}
}
/**
* @function SuperMap.Events.prototype.triggerEvent
* @description 触发一个特定的注册事件。
* @param {string} type - 触发事件类型。
* @param {Event} evt - 事件对象。
* @returns {boolean} 返回监听对象,如果返回是 false,则停止监听。
*/
triggerEvent(type, evt) {
var listeners = this.listeners[type];
// fast path
if (!listeners || listeners.length == 0) {
return undefined;
}
// prep evt object with object & div references
if (evt == null) {
evt = {};
}
evt.object = this.object;
evt.element = this.element;
if (!evt.type) {
evt.type = type;
}
// execute all callbacks registered for specified type
// get a clone of the listeners array to
// allow for splicing during callbacks
listeners = listeners.slice();
var continueChain;
for (var i = 0, len = listeners.length; i < len; i++) {
var callback = listeners[i];
// bind the context to callback.obj
continueChain = callback.func.apply(callback.obj, [evt]);
if ((continueChain != undefined) && (continueChain === false)) {
// if callback returns false, execute no more callbacks.
break;
}
}
// don't fall through to other DOM elements
if (!this.fallThrough) {
Event.stop(evt, true);
}
return continueChain;
}
/**
* @function SuperMap.Events.prototype.handleBrowserEvent
* @description 对 triggerEvent 函数的包装,给事件对象设置了 xy 属性(即当前鼠标点的 xy 坐标)。
* @param {Event} evt - 事件对象。
*/
handleBrowserEvent(evt) {
var type = evt.type, listeners = this.listeners[type];
if (!listeners || listeners.length == 0) {
// noone's listening, bail out
return;
}
// add clientX & clientY to all events - corresponds to average x, y
var touches = evt.touches;
if (touches && touches[0]) {
var x = 0;
var y = 0;
var num = touches.length;
var touch;
for (var i = 0; i < num; ++i) {
touch = touches[i];
x += touch.clientX;
y += touch.clientY;
}
evt.clientX = x / num;
evt.clientY = y / num;
}
if (this.includeXY) {
evt.xy = this.getMousePosition(evt);
}
this.triggerEvent(type, evt);
}
/**
* @function SuperMap.Events.prototype.clearMouseCache
* @description 清除鼠标缓存。
*/
clearMouseCache() {
this.element.scrolls = null;
this.element.lefttop = null;
var body = document.body;
if (body && !((body.scrollTop != 0 || body.scrollLeft != 0) &&
navigator.userAgent.match(/iPhone/i))) {
this.element.offsets = null;
}
}
/**
* @function SuperMap.Events.prototype.getMousePosition
* @param {Event} evt - 事件对象。
* @returns {SuperMap.Pixel} 当前的鼠标的 xy 坐标点。
*/
getMousePosition(evt) {
if (!this.includeXY) {
this.clearMouseCache();
} else if (!this.element.hasScrollEvent) {
Event.observe(window, "scroll", this.clearMouseListener);
this.element.hasScrollEvent = true;
}
if (!this.element.scrolls) {
var viewportElement = Util.getViewportElement();
this.element.scrolls = [
viewportElement.scrollLeft,
viewportElement.scrollTop
];
}
if (!this.element.lefttop) {
this.element.lefttop = [
(document.documentElement.clientLeft || 0),
(document.documentElement.clientTop || 0)
];
}
if (!this.element.offsets) {
this.element.offsets = Util.pagePosition(this.element);
}
return new Pixel(
(evt.clientX + this.element.scrolls[0]) - this.element.offsets[0]
- this.element.lefttop[0],
(evt.clientY + this.element.scrolls[1]) - this.element.offsets[1]
- this.element.lefttop[1]
);
}
}
SuperMap.Events = Events;
SuperMap.Events.prototype.BROWSER_EVENTS = [
"mouseover", "mouseout",
"mousedown", "mouseup", "mousemove",
"click", "dblclick", "rightclick", "dblrightclick",
"resize", "focus", "blur",
"touchstart", "touchmove", "touchend",
"keydown", "MSPointerDown", "MSPointerUp", "pointerdown", "pointerup",
"MSGestureStart", "MSGestureChange", "MSGestureEnd",
"contextmenu"
];