pseudo-dom 0.2.0 → 0.3.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/README.md +134 -48
- package/browser/pseudo-dom.js +233 -126
- package/browser/pseudo-dom.min.js +1 -1
- package/dist/classes/PseudoEventListener.d.ts +25 -25
- package/dist/classes/PseudoEventListener.js +37 -34
- package/dist/classes/PseudoEventListener.min.js +1 -1
- package/dist/interfaces/PseudoEventTarget.d.ts +2 -2
- package/dist/services/ElementService.d.ts +1 -0
- package/dist/services/ElementService.js +11 -4
- package/dist/services/ElementService.min.js +1 -1
- package/dist/services/EventService.d.ts +16 -4
- package/dist/services/EventService.js +59 -31
- package/dist/services/EventService.min.js +1 -1
- package/dist/services/EventTargetService.d.ts +41 -9
- package/dist/services/EventTargetService.js +121 -52
- package/dist/services/EventTargetService.min.js +1 -1
- package/package.json +1 -1
package/browser/pseudo-dom.js
CHANGED
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
* @property {boolean} isDefault
|
|
21
21
|
*/
|
|
22
22
|
class PseudoEventListener {
|
|
23
|
+
/**
|
|
24
|
+
* @param {string} eventType The type of event this listens for
|
|
25
|
+
* @param {Object} [options] The capture, once and passive options
|
|
26
|
+
* @param {Function} handleEvent The function which is called with the event, already bound to what it should run as
|
|
27
|
+
* @param {Function} [originalCallback=handleEvent] The function (or object) which was given when registering, used to find this listener again
|
|
28
|
+
* @constructor
|
|
29
|
+
*/
|
|
23
30
|
constructor (eventType, {
|
|
24
31
|
capture = false,
|
|
25
32
|
once = false,
|
|
@@ -32,6 +39,7 @@
|
|
|
32
39
|
}
|
|
33
40
|
this.eventType = ''
|
|
34
41
|
this.defaultListener = false
|
|
42
|
+
this.isRemoved = false
|
|
35
43
|
this.eventOptions = {
|
|
36
44
|
capture,
|
|
37
45
|
once,
|
|
@@ -49,6 +57,11 @@
|
|
|
49
57
|
return this.originalCallback
|
|
50
58
|
}
|
|
51
59
|
|
|
60
|
+
/** Whether this listener listens in the capture phase (and at the target) rather than in the bubble phase. */
|
|
61
|
+
get capture () {
|
|
62
|
+
return this.eventOptions.capture
|
|
63
|
+
}
|
|
64
|
+
|
|
52
65
|
get isDefault () {
|
|
53
66
|
return this.defaultListener
|
|
54
67
|
}
|
|
@@ -57,6 +70,20 @@
|
|
|
57
70
|
return this.eventOptions.once
|
|
58
71
|
}
|
|
59
72
|
|
|
73
|
+
/** Whether the listener promises not to prevent the default (preventDefault does nothing while it runs). */
|
|
74
|
+
get passive () {
|
|
75
|
+
return this.eventOptions.passive
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Whether this listener has been removed, a removed listener does not run even if the event already started. */
|
|
79
|
+
get removed () {
|
|
80
|
+
return this.isRemoved
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
set removed (removed) {
|
|
84
|
+
this.isRemoved = removed
|
|
85
|
+
}
|
|
86
|
+
|
|
60
87
|
/**
|
|
61
88
|
* @method
|
|
62
89
|
* @name PseudoEventListener#handleEvent
|
|
@@ -68,6 +95,7 @@
|
|
|
68
95
|
}
|
|
69
96
|
|
|
70
97
|
/**
|
|
98
|
+
* A capture listener runs while the event travels down to the target.
|
|
71
99
|
* @method
|
|
72
100
|
* @name PseudoEventListener#doCapturePhase
|
|
73
101
|
* @param {PseudoEvent} event
|
|
@@ -78,6 +106,7 @@
|
|
|
78
106
|
}
|
|
79
107
|
|
|
80
108
|
/**
|
|
109
|
+
* Every listener of the target itself runs, capture listeners first.
|
|
81
110
|
* @method
|
|
82
111
|
* @name PseudoEventListener#doTargetPhase
|
|
83
112
|
* @param {PseudoEvent} event
|
|
@@ -88,13 +117,14 @@
|
|
|
88
117
|
}
|
|
89
118
|
|
|
90
119
|
/**
|
|
120
|
+
* A listener which is not a capture listener runs while the event travels back up (when it bubbles).
|
|
91
121
|
* @method
|
|
92
122
|
* @name PseudoEventListener#doBubblePhase
|
|
93
123
|
* @param {PseudoEvent} event
|
|
94
|
-
* @returns {boolean
|
|
124
|
+
* @returns {boolean}
|
|
95
125
|
*/
|
|
96
126
|
doBubblePhase (event) {
|
|
97
|
-
return event.eventPhase === EventService_1.EventService.BUBBLING_PHASE &&
|
|
127
|
+
return event.eventPhase === EventService_1.EventService.BUBBLING_PHASE && !this.eventOptions.capture
|
|
98
128
|
}
|
|
99
129
|
|
|
100
130
|
/**
|
|
@@ -108,43 +138,16 @@
|
|
|
108
138
|
}
|
|
109
139
|
|
|
110
140
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
* @returns {boolean|*}
|
|
115
|
-
*/
|
|
116
|
-
skipDefault (event) {
|
|
117
|
-
return this.isDefault && event.defaultPrevented
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* @method
|
|
122
|
-
* @name PseudoEventListener#stopPropagation
|
|
123
|
-
* @param {PseudoEvent} event
|
|
124
|
-
* @returns {boolean}
|
|
125
|
-
*/
|
|
126
|
-
stopPropagation (event) {
|
|
127
|
-
return !this.doTargetPhase(event) && event.inner.propagationStopped
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @method
|
|
132
|
-
* @name PseudoEventListener#nonPassiveHalt
|
|
133
|
-
* @param {PseudoEvent} event
|
|
134
|
-
* @returns {boolean|*}
|
|
135
|
-
*/
|
|
136
|
-
nonPassiveHalt (event) {
|
|
137
|
-
return !this.eventOptions.passive && (this.skipDefault(event) || event.inner.immediatePropagationStopped || this.stopPropagation(event))
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
+
* Whether this listener should not run for the event as it is now (it was removed, or it is for another phase).
|
|
142
|
+
* Stopping propagation is handled by the dispatching, since it stops other targets and not the listeners of the
|
|
143
|
+
* current one.
|
|
141
144
|
* @method
|
|
142
145
|
* @name PseudoEventListener#rejectEvent
|
|
143
146
|
* @param {PseudoEvent} event
|
|
144
|
-
* @returns {
|
|
147
|
+
* @returns {boolean}
|
|
145
148
|
*/
|
|
146
149
|
rejectEvent (event) {
|
|
147
|
-
return this.
|
|
150
|
+
return this.isRemoved || this.skipPhase(event)
|
|
148
151
|
}
|
|
149
152
|
}
|
|
150
153
|
exports.default = PseudoEventListener
|
|
@@ -701,6 +704,7 @@
|
|
|
701
704
|
value: true
|
|
702
705
|
})
|
|
703
706
|
exports.ElementService = void 0
|
|
707
|
+
const EventService_1 = require('./EventService')
|
|
704
708
|
const NodeService_1 = require('./NodeService')
|
|
705
709
|
const AttrService_1 = require('./AttrService')
|
|
706
710
|
const DOMTokenListService_1 = require('./DOMTokenListService')
|
|
@@ -737,6 +741,7 @@
|
|
|
737
741
|
children = []
|
|
738
742
|
} = {}) {
|
|
739
743
|
super()
|
|
744
|
+
this.defaultEventApplied = false
|
|
740
745
|
this.tokenList = new DOMTokenListService_1.DOMTokenListService()
|
|
741
746
|
this.tag = tagName
|
|
742
747
|
this.attributeList = attributes.concat([{
|
|
@@ -802,20 +807,25 @@
|
|
|
802
807
|
*/
|
|
803
808
|
applyDefaultEvent () {
|
|
804
809
|
let callback = event => undefined
|
|
810
|
+
if (this.defaultEventApplied) {
|
|
811
|
+
return callback
|
|
812
|
+
}
|
|
805
813
|
switch (this.tagName) {
|
|
806
|
-
case 'form':
|
|
807
|
-
this.addEventListener('submit', callback)
|
|
808
|
-
break
|
|
809
814
|
case 'button':
|
|
810
815
|
case 'input':
|
|
811
816
|
if (/^(submit|image)$/i.test(this.type || '')) {
|
|
817
|
+
// Clicking a submit button submits the form it is in: the form gets a submit event, which can be cancelled
|
|
812
818
|
callback = event => {
|
|
813
819
|
const forms = (0, getParentNodesFromAttribute_1.default)('tagName', 'form', this)
|
|
814
820
|
if (forms.length) {
|
|
815
|
-
forms[
|
|
821
|
+
forms[forms.length - 1].dispatchEvent(new EventService_1.EventService('submit', {
|
|
822
|
+
bubbles: true,
|
|
823
|
+
cancelable: true
|
|
824
|
+
}))
|
|
816
825
|
}
|
|
817
826
|
}
|
|
818
827
|
super.setDefaultEvent('click', callback)
|
|
828
|
+
this.defaultEventApplied = true
|
|
819
829
|
}
|
|
820
830
|
}
|
|
821
831
|
return callback
|
|
@@ -892,7 +902,7 @@
|
|
|
892
902
|
}
|
|
893
903
|
}
|
|
894
904
|
exports.ElementService = ElementService
|
|
895
|
-
}, { '../functions/getParentNodesFromAttribute': 7, './AttrService': 9, './DOMTokenListService': 10, './NamedNodeMapService': 15, './NodeService': 16, 'core-js/modules/esnext.iterator.constructor.js': 130, 'core-js/modules/esnext.iterator.find.js': 132, 'core-js/modules/esnext.iterator.for-each.js': 133, 'core-js/modules/esnext.iterator.map.js': 134, 'core-js/modules/esnext.iterator.some.js': 136 }],
|
|
905
|
+
}, { '../functions/getParentNodesFromAttribute': 7, './AttrService': 9, './DOMTokenListService': 10, './EventService': 12, './NamedNodeMapService': 15, './NodeService': 16, 'core-js/modules/esnext.iterator.constructor.js': 130, 'core-js/modules/esnext.iterator.find.js': 132, 'core-js/modules/esnext.iterator.for-each.js': 133, 'core-js/modules/esnext.iterator.map.js': 134, 'core-js/modules/esnext.iterator.some.js': 136 }],
|
|
896
906
|
12: [function (require, module, exports) {
|
|
897
907
|
'use strict'
|
|
898
908
|
|
|
@@ -901,18 +911,10 @@
|
|
|
901
911
|
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
902
912
|
* @version 1.0.0
|
|
903
913
|
*/
|
|
904
|
-
const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
905
|
-
return mod && mod.__esModule
|
|
906
|
-
? mod
|
|
907
|
-
: {
|
|
908
|
-
default: mod
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
914
|
Object.defineProperty(exports, '__esModule', {
|
|
912
915
|
value: true
|
|
913
916
|
})
|
|
914
917
|
exports.EventService = void 0
|
|
915
|
-
const getParentNodes_1 = __importDefault(require('../functions/getParentNodes'))
|
|
916
918
|
/**
|
|
917
919
|
* Simulate the behaviour of the Event Class when there is no DOM available.
|
|
918
920
|
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
@@ -947,20 +949,20 @@
|
|
|
947
949
|
*
|
|
948
950
|
* @param {string} typeArg
|
|
949
951
|
* @param {Object} [eventOptions={}]
|
|
950
|
-
* @param {boolean} [eventOptions.bubbles=
|
|
951
|
-
* @param {boolean} [eventOptions.cancelable=
|
|
952
|
-
* @param {boolean} [eventOptions.composed=
|
|
952
|
+
* @param {boolean} [eventOptions.bubbles=false]
|
|
953
|
+
* @param {boolean} [eventOptions.cancelable=false]
|
|
954
|
+
* @param {boolean} [eventOptions.composed=false]
|
|
953
955
|
* @constructor
|
|
954
956
|
*/
|
|
955
957
|
constructor (typeArg = '', {
|
|
956
|
-
bubbles =
|
|
957
|
-
cancelable =
|
|
958
|
-
composed =
|
|
958
|
+
bubbles = false,
|
|
959
|
+
cancelable = false,
|
|
960
|
+
composed = false
|
|
959
961
|
} = {}) {
|
|
960
962
|
this.properties = {
|
|
961
|
-
bubbles:
|
|
962
|
-
cancelable:
|
|
963
|
-
composed:
|
|
963
|
+
bubbles: false,
|
|
964
|
+
cancelable: false,
|
|
965
|
+
composed: false,
|
|
964
966
|
currentTarget: null,
|
|
965
967
|
defaultPrevented: false,
|
|
966
968
|
immediatePropagationStopped: false,
|
|
@@ -969,7 +971,10 @@
|
|
|
969
971
|
target: null,
|
|
970
972
|
timeStamp: Math.floor(Date.now() / 1000),
|
|
971
973
|
type: '',
|
|
972
|
-
isTrusted: true
|
|
974
|
+
isTrusted: true,
|
|
975
|
+
dispatching: false,
|
|
976
|
+
inPassiveListener: false,
|
|
977
|
+
path: []
|
|
973
978
|
}
|
|
974
979
|
this.setReadOnlyProperties({
|
|
975
980
|
type: typeArg,
|
|
@@ -1026,12 +1031,21 @@
|
|
|
1026
1031
|
get inner () {
|
|
1027
1032
|
const self = this
|
|
1028
1033
|
return {
|
|
1034
|
+
get currentTarget () {
|
|
1035
|
+
return self.properties.currentTarget
|
|
1036
|
+
},
|
|
1029
1037
|
set currentTarget (target) {
|
|
1030
1038
|
self.properties.currentTarget = target
|
|
1031
1039
|
},
|
|
1040
|
+
get eventPhase () {
|
|
1041
|
+
return self.properties.eventPhase
|
|
1042
|
+
},
|
|
1032
1043
|
set eventPhase (phase) {
|
|
1033
1044
|
self.properties.eventPhase = phase
|
|
1034
1045
|
},
|
|
1046
|
+
get target () {
|
|
1047
|
+
return self.properties.target
|
|
1048
|
+
},
|
|
1035
1049
|
set target (target) {
|
|
1036
1050
|
self.properties.target = target
|
|
1037
1051
|
},
|
|
@@ -1040,6 +1054,35 @@
|
|
|
1040
1054
|
},
|
|
1041
1055
|
get propagationStopped () {
|
|
1042
1056
|
return self.properties.propagationStopped
|
|
1057
|
+
},
|
|
1058
|
+
get dispatching () {
|
|
1059
|
+
return self.properties.dispatching
|
|
1060
|
+
},
|
|
1061
|
+
set dispatching (dispatching) {
|
|
1062
|
+
self.properties.dispatching = dispatching
|
|
1063
|
+
},
|
|
1064
|
+
get inPassiveListener () {
|
|
1065
|
+
return self.properties.inPassiveListener
|
|
1066
|
+
},
|
|
1067
|
+
set inPassiveListener (passive) {
|
|
1068
|
+
self.properties.inPassiveListener = passive
|
|
1069
|
+
},
|
|
1070
|
+
get path () {
|
|
1071
|
+
return self.properties.path
|
|
1072
|
+
},
|
|
1073
|
+
set path (path) {
|
|
1074
|
+
self.properties.path = path
|
|
1075
|
+
},
|
|
1076
|
+
finishDispatch () {
|
|
1077
|
+
self.setReadOnlyProperties({
|
|
1078
|
+
currentTarget: null,
|
|
1079
|
+
eventPhase: EventService.NONE,
|
|
1080
|
+
path: [],
|
|
1081
|
+
dispatching: false,
|
|
1082
|
+
inPassiveListener: false,
|
|
1083
|
+
propagationStopped: false,
|
|
1084
|
+
immediatePropagationStopped: false
|
|
1085
|
+
})
|
|
1043
1086
|
}
|
|
1044
1087
|
}
|
|
1045
1088
|
}
|
|
@@ -1050,16 +1093,8 @@
|
|
|
1050
1093
|
* @returns {Array.<PseudoEventTarget>}
|
|
1051
1094
|
*/
|
|
1052
1095
|
composedPath () {
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
return (0, getParentNodes_1.default)(this.target)
|
|
1056
|
-
case EventService.BUBBLING_PHASE:
|
|
1057
|
-
return (0, getParentNodes_1.default)(this.target).slice().reverse()
|
|
1058
|
-
case EventService.AT_TARGET:
|
|
1059
|
-
return [this.target]
|
|
1060
|
-
default:
|
|
1061
|
-
return []
|
|
1062
|
-
}
|
|
1096
|
+
// While the event is being dispatched this is every target it travels through, the target first and the root last
|
|
1097
|
+
return this.properties.dispatching ? this.properties.path.slice() : []
|
|
1063
1098
|
}
|
|
1064
1099
|
|
|
1065
1100
|
/**
|
|
@@ -1068,9 +1103,12 @@
|
|
|
1068
1103
|
* @returns {null}
|
|
1069
1104
|
*/
|
|
1070
1105
|
preventDefault () {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1106
|
+
// Only an event which can be cancelled can be prevented, and a passive listener cannot prevent the default
|
|
1107
|
+
if (this.cancelable && !this.properties.inPassiveListener) {
|
|
1108
|
+
this.setReadOnlyProperties({
|
|
1109
|
+
defaultPrevented: true
|
|
1110
|
+
})
|
|
1111
|
+
}
|
|
1074
1112
|
return null
|
|
1075
1113
|
}
|
|
1076
1114
|
|
|
@@ -1110,7 +1148,7 @@
|
|
|
1110
1148
|
EventService.CAPTURING_PHASE = 1
|
|
1111
1149
|
EventService.AT_TARGET = 2
|
|
1112
1150
|
EventService.BUBBLING_PHASE = 3
|
|
1113
|
-
}, {
|
|
1151
|
+
}, {}],
|
|
1114
1152
|
13: [function (require, module, exports) {
|
|
1115
1153
|
'use strict'
|
|
1116
1154
|
|
|
@@ -1118,6 +1156,8 @@
|
|
|
1118
1156
|
require('core-js/modules/esnext.iterator.filter.js')
|
|
1119
1157
|
require('core-js/modules/esnext.iterator.find.js')
|
|
1120
1158
|
require('core-js/modules/esnext.iterator.for-each.js')
|
|
1159
|
+
require('core-js/modules/esnext.iterator.map.js')
|
|
1160
|
+
require('core-js/modules/esnext.iterator.some.js')
|
|
1121
1161
|
const __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
1122
1162
|
return mod && mod.__esModule
|
|
1123
1163
|
? mod
|
|
@@ -1135,9 +1175,13 @@
|
|
|
1135
1175
|
*/
|
|
1136
1176
|
const EventService_1 = require('./EventService')
|
|
1137
1177
|
const PseudoEventListener_1 = __importDefault(require('../classes/PseudoEventListener'))
|
|
1178
|
+
const getParentNodes_1 = __importDefault(require('../functions/getParentNodes'))
|
|
1138
1179
|
const LinkedList_1 = require('collect-your-stuff/dist/collections/linked-list/LinkedList')
|
|
1139
1180
|
/**
|
|
1140
1181
|
* Simulate the behaviour of the EventTarget Class when there is no DOM available.
|
|
1182
|
+
* Dispatching an event sends it through the tree the way the DOM does: down from the root to the target (capture
|
|
1183
|
+
* listeners), to the target itself, then back up to the root (the listeners which are not capture listeners, when the
|
|
1184
|
+
* event bubbles).
|
|
1141
1185
|
* @author Joshua Heagle <joshuaheagle@gmail.com>
|
|
1142
1186
|
* @class
|
|
1143
1187
|
* @property {Object.<string, Array.<PseudoEventListener>>} listeners
|
|
@@ -1167,33 +1211,51 @@
|
|
|
1167
1211
|
}
|
|
1168
1212
|
|
|
1169
1213
|
/**
|
|
1170
|
-
* Run
|
|
1171
|
-
*
|
|
1172
|
-
* and listeners
|
|
1173
|
-
*
|
|
1174
|
-
* @
|
|
1214
|
+
* Run the listeners registered on this target for the type of the event which apply to the phase the event is in
|
|
1215
|
+
* (at the target, the capture listeners run before the others). Listeners which are added while this runs do not run
|
|
1216
|
+
* for this event, and listeners which are removed while it runs no longer do. Running stops as soon as immediate
|
|
1217
|
+
* propagation is stopped. A listener which throws does not stop the others.
|
|
1218
|
+
* @param {EventService} event The event, which is at a phase and has a current target
|
|
1219
|
+
* @returns {Array<*>} The errors which the listeners threw
|
|
1175
1220
|
*/
|
|
1176
1221
|
runEvents (event) {
|
|
1222
|
+
const errors = []
|
|
1177
1223
|
if (!(event.type in this.listeners)) {
|
|
1178
|
-
return
|
|
1224
|
+
return errors
|
|
1179
1225
|
}
|
|
1180
|
-
const listeners = this.listeners[event.type]
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
for (const
|
|
1184
|
-
const listener = linker.data
|
|
1226
|
+
const listeners = Array.from(this.listeners[event.type]).map(linker => linker.data)
|
|
1227
|
+
// At the target the capture listeners come first, otherwise the order is the order they were added
|
|
1228
|
+
const ordered = event.eventPhase === EventService_1.EventService.AT_TARGET ? listeners.filter(listener => listener.capture).concat(listeners.filter(listener => !listener.capture)) : listeners
|
|
1229
|
+
for (const listener of ordered) {
|
|
1185
1230
|
if (event.inner.immediatePropagationStopped) {
|
|
1186
1231
|
break
|
|
1187
1232
|
}
|
|
1188
1233
|
if (listener.rejectEvent(event)) {
|
|
1189
1234
|
continue
|
|
1190
1235
|
}
|
|
1191
|
-
eventReturn = listener.handleEvent(event)
|
|
1192
1236
|
if (listener.once) {
|
|
1193
|
-
|
|
1237
|
+
this.removeListener(event.type, listener)
|
|
1194
1238
|
}
|
|
1239
|
+
event.inner.inPassiveListener = listener.passive
|
|
1240
|
+
try {
|
|
1241
|
+
listener.handleEvent(event)
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
errors.push(error)
|
|
1244
|
+
}
|
|
1245
|
+
event.inner.inPassiveListener = false
|
|
1195
1246
|
}
|
|
1196
|
-
return
|
|
1247
|
+
return errors
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
/**
|
|
1251
|
+
* Take a listener out of the registered listeners, so that it does not run again.
|
|
1252
|
+
* @param {string} type
|
|
1253
|
+
* @param {PseudoEventListener} listener
|
|
1254
|
+
*/
|
|
1255
|
+
removeListener (type, listener) {
|
|
1256
|
+
listener.removed = true
|
|
1257
|
+
const registered = this.listeners[type]
|
|
1258
|
+
Array.from(registered).filter(linker => linker.data === listener).forEach(linker => registered.remove(linker))
|
|
1197
1259
|
}
|
|
1198
1260
|
|
|
1199
1261
|
/**
|
|
@@ -1206,48 +1268,34 @@
|
|
|
1206
1268
|
this.defaultEvent[type] = callback
|
|
1207
1269
|
}
|
|
1208
1270
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
startEvents (eventType) {
|
|
1218
|
-
const event = new EventService_1.EventService(eventType)
|
|
1219
|
-
event.inner.target = this;
|
|
1220
|
-
[EventService_1.EventService.CAPTURING_PHASE, EventService_1.EventService.AT_TARGET, EventService_1.EventService.BUBBLING_PHASE].forEach(phase => {
|
|
1221
|
-
let continueEvents = null
|
|
1222
|
-
if (phase === EventService_1.EventService.AT_TARGET || !event.inner.propagationStopped) {
|
|
1223
|
-
event.inner.eventPhase = phase
|
|
1224
|
-
event.composedPath().forEach(target => {
|
|
1225
|
-
event.inner.currentTarget = target
|
|
1226
|
-
continueEvents = event.currentTarget.runEvents(event)
|
|
1227
|
-
})
|
|
1228
|
-
}
|
|
1229
|
-
if (event.eventPhase === EventService_1.EventService.AT_TARGET && typeof continueEvents !== 'boolean' && this.defaultEvent[eventType]) {
|
|
1230
|
-
this.runDefaultEvent(event)
|
|
1231
|
-
}
|
|
1232
|
-
})
|
|
1233
|
-
return true
|
|
1234
|
-
}
|
|
1235
|
-
|
|
1271
|
+
/**
|
|
1272
|
+
* Registers an event handler of a specific event type. Adding the same handler again for the same type and phase does
|
|
1273
|
+
* nothing, like the DOM.
|
|
1274
|
+
* @param {string} type The type of event to listen for
|
|
1275
|
+
* @param {Function|Object} callback The function to call (or an object with a handleEvent function)
|
|
1276
|
+
* @param {Object|boolean} [useCapture=false] Listen while the event travels down to the target (true), or an object with capture, once and passive
|
|
1277
|
+
*/
|
|
1236
1278
|
addEventListener (type, callback, useCapture = false) {
|
|
1237
1279
|
let options = {
|
|
1238
1280
|
capture: false,
|
|
1239
1281
|
once: false,
|
|
1240
1282
|
passive: false
|
|
1241
1283
|
}
|
|
1242
|
-
if (typeof useCapture === 'object') {
|
|
1284
|
+
if (typeof useCapture === 'object' && useCapture !== null) {
|
|
1243
1285
|
// Originally useCapture was a single boolean flag, later optional other flags can be used
|
|
1244
1286
|
// Here we take all the given flags from the object and assign them as the options
|
|
1245
1287
|
options = Object.assign(options, useCapture)
|
|
1246
1288
|
} else {
|
|
1247
|
-
options.capture = useCapture
|
|
1289
|
+
options.capture = !!useCapture
|
|
1248
1290
|
}
|
|
1249
|
-
const listener = new PseudoEventListener_1.default(type, options, (callback.handleEvent || callback).bind(this), callback)
|
|
1250
1291
|
const listeners = this.listenersFor(type)
|
|
1292
|
+
const alreadyAdded = Array.from(listeners).some(linker => linker.data.callback === callback && linker.data.capture === options.capture)
|
|
1293
|
+
if (alreadyAdded) {
|
|
1294
|
+
return
|
|
1295
|
+
}
|
|
1296
|
+
// A function runs with this target as this, an object runs its handleEvent as itself
|
|
1297
|
+
const handler = typeof callback === 'function' ? callback.bind(this) : callback.handleEvent.bind(callback)
|
|
1298
|
+
const listener = new PseudoEventListener_1.default(type, options, handler, callback)
|
|
1251
1299
|
// Listeners run in the order they were added, except that listeners which are not defaults always come before the defaults
|
|
1252
1300
|
const firstDefault = Array.from(listeners).find(linker => linker.data.isDefault)
|
|
1253
1301
|
if (firstDefault && !listener.isDefault) {
|
|
@@ -1257,25 +1305,84 @@
|
|
|
1257
1305
|
}
|
|
1258
1306
|
}
|
|
1259
1307
|
|
|
1260
|
-
|
|
1308
|
+
/**
|
|
1309
|
+
* Removes an event listener, the one which was added with the same type, handler and phase.
|
|
1310
|
+
* @param {string} type The type of event
|
|
1311
|
+
* @param {Function|Object} callback The handler which was added
|
|
1312
|
+
* @param {Object|boolean} [options=false] Whether the listener was a capture listener (true), or an object with capture
|
|
1313
|
+
*/
|
|
1314
|
+
removeEventListener (type, callback, options = false) {
|
|
1261
1315
|
if (!(type in this.listeners)) {
|
|
1262
1316
|
return
|
|
1263
1317
|
}
|
|
1264
|
-
const
|
|
1265
|
-
Array.from(listeners).
|
|
1266
|
-
}
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1318
|
+
const capture = typeof options === 'object' && options !== null ? !!options.capture : !!options
|
|
1319
|
+
Array.from(this.listeners[type]).map(linker => linker.data).filter(listener => !listener.isDefault && listener.callback === callback && listener.capture === capture).forEach(listener => this.removeListener(type, listener))
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
/**
|
|
1323
|
+
* Dispatches an event to this target and through the tree: capture listeners of the ancestors from the root down,
|
|
1324
|
+
* then the listeners of this target, then (when the event bubbles) the other listeners of the ancestors from the
|
|
1325
|
+
* parent up to the root. stopPropagation() stops it reaching further targets, stopImmediatePropagation() also stops
|
|
1326
|
+
* the remaining listeners of the current target. Afterwards, unless the default was prevented, the default action
|
|
1327
|
+
* of this target (see setDefaultEvent) runs. The event can be dispatched again afterwards.
|
|
1328
|
+
* @param {EventService} event The event to dispatch
|
|
1329
|
+
* @returns {boolean} False when the event was cancelable and a listener prevented the default, otherwise true
|
|
1330
|
+
* @throws {Error} When the event is already being dispatched, or (after the whole dispatch has finished) the error
|
|
1331
|
+
* which a listener threw (an error with all of them in its errors property when several did)
|
|
1332
|
+
*/
|
|
1333
|
+
dispatchEvent (event) {
|
|
1334
|
+
if (event.inner.dispatching) {
|
|
1335
|
+
throw new Error('The event is already being dispatched.')
|
|
1336
|
+
}
|
|
1337
|
+
event.inner.dispatching = true
|
|
1338
|
+
event.inner.target = this
|
|
1339
|
+
// The ancestors, the root first, which can have listeners
|
|
1340
|
+
const ancestors = (0, getParentNodes_1.default)(this).filter(node => node instanceof EventTargetService)
|
|
1341
|
+
event.inner.path = [this].concat(ancestors.slice().reverse())
|
|
1342
|
+
const errors = []
|
|
1343
|
+
const visit = (target, phase) => {
|
|
1344
|
+
event.inner.eventPhase = phase
|
|
1345
|
+
event.inner.currentTarget = target
|
|
1346
|
+
errors.push(...target.runEvents(event))
|
|
1347
|
+
}
|
|
1348
|
+
for (const ancestor of ancestors) {
|
|
1349
|
+
if (event.inner.propagationStopped) {
|
|
1350
|
+
break
|
|
1351
|
+
}
|
|
1352
|
+
visit(ancestor, EventService_1.EventService.CAPTURING_PHASE)
|
|
1353
|
+
}
|
|
1354
|
+
if (!event.inner.propagationStopped) {
|
|
1355
|
+
visit(this, EventService_1.EventService.AT_TARGET)
|
|
1356
|
+
}
|
|
1357
|
+
if (event.bubbles) {
|
|
1358
|
+
for (const ancestor of ancestors.slice().reverse()) {
|
|
1359
|
+
if (event.inner.propagationStopped) {
|
|
1360
|
+
break
|
|
1361
|
+
}
|
|
1362
|
+
visit(ancestor, EventService_1.EventService.BUBBLING_PHASE)
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
event.inner.finishDispatch()
|
|
1366
|
+
if (!event.defaultPrevented && typeof this.defaultEvent[event.type] === 'function') {
|
|
1367
|
+
try {
|
|
1368
|
+
this.defaultEvent[event.type](event)
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
errors.push(error)
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (errors.length === 1) {
|
|
1374
|
+
throw errors[0]
|
|
1375
|
+
}
|
|
1376
|
+
if (errors.length > 1) {
|
|
1377
|
+
throw Object.assign(new Error(`${errors.length} listeners threw an error while dispatching the ${event.type} event.`), {
|
|
1378
|
+
errors
|
|
1379
|
+
})
|
|
1272
1380
|
}
|
|
1273
|
-
this.runEvents(event)
|
|
1274
1381
|
return !event.defaultPrevented
|
|
1275
1382
|
}
|
|
1276
1383
|
}
|
|
1277
1384
|
exports.default = EventTargetService
|
|
1278
|
-
}, { '../classes/PseudoEventListener': 1, './EventService': 12, 'collect-your-stuff/dist/collections/linked-list/LinkedList': 21, 'core-js/modules/esnext.iterator.constructor.js': 130, 'core-js/modules/esnext.iterator.filter.js': 131, 'core-js/modules/esnext.iterator.find.js': 132, 'core-js/modules/esnext.iterator.for-each.js': 133 }],
|
|
1385
|
+
}, { '../classes/PseudoEventListener': 1, '../functions/getParentNodes': 6, './EventService': 12, 'collect-your-stuff/dist/collections/linked-list/LinkedList': 21, 'core-js/modules/esnext.iterator.constructor.js': 130, 'core-js/modules/esnext.iterator.filter.js': 131, 'core-js/modules/esnext.iterator.find.js': 132, 'core-js/modules/esnext.iterator.for-each.js': 133, 'core-js/modules/esnext.iterator.map.js': 134, 'core-js/modules/esnext.iterator.some.js': 136 }],
|
|
1279
1386
|
14: [function (require, module, exports) {
|
|
1280
1387
|
'use strict'
|
|
1281
1388
|
|