vgapp 0.0.1
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/.gitattributes +1 -0
- package/CHANGELOG.md +0 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/app/modules/base-module.js +97 -0
- package/app/modules/module-fn.js +119 -0
- package/app/modules/vgcollapse/js/vgcollapse.js +219 -0
- package/app/modules/vgdropdown/js/vgdropdown.js +279 -0
- package/app/modules/vgdropdown/scss/_variables.scss +9 -0
- package/app/modules/vgdropdown/scss/vgdropdown.scss +41 -0
- package/app/modules/vgformsender/js/vgformsender.js +400 -0
- package/app/modules/vgformsender/scss/vgformsender.scss +19 -0
- package/app/modules/vgmodal/js/vgmodal.js +346 -0
- package/app/modules/vgmodal/scss/_variables.scss +25 -0
- package/app/modules/vgmodal/scss/vgmodal.scss +111 -0
- package/app/modules/vgnav/js/vgnav.js +498 -0
- package/app/modules/vgnav/scss/_breakpoints.scss +127 -0
- package/app/modules/vgnav/scss/_hamburger.scss +62 -0
- package/app/modules/vgnav/scss/_placement.scss +70 -0
- package/app/modules/vgnav/scss/_toggle.scss +20 -0
- package/app/modules/vgnav/scss/_variables.scss +68 -0
- package/app/modules/vgnav/scss/vgnav.scss +150 -0
- package/app/modules/vgsidebar/js/vgsidebar.js +165 -0
- package/app/modules/vgsidebar/scss/_variables.scss +19 -0
- package/app/modules/vgsidebar/scss/vgsidebar.scss +90 -0
- package/app/utils/js/components/backdrop.js +54 -0
- package/app/utils/js/components/overflow.js +28 -0
- package/app/utils/js/components/params.js +44 -0
- package/app/utils/js/components/placement.js +59 -0
- package/app/utils/js/components/responsive.js +83 -0
- package/app/utils/js/components/scrollbar.js +114 -0
- package/app/utils/js/dom/data.js +51 -0
- package/app/utils/js/dom/event.js +331 -0
- package/app/utils/js/dom/manipulator.js +62 -0
- package/app/utils/js/dom/selectors.js +65 -0
- package/app/utils/js/functions.js +272 -0
- package/app/utils/scss/animate.scss +4074 -0
- package/app/utils/scss/default.scss +277 -0
- package/app/utils/scss/functions.scss +3 -0
- package/app/utils/scss/mixin.scss +11 -0
- package/app/utils/scss/variables.scss +80 -0
- package/build/vgapp.css +4538 -0
- package/build/vgapp.css.map +1 -0
- package/build/vgapp.js +3230 -0
- package/build/vgapp.js.map +1 -0
- package/index.js +29 -0
- package/package.json +43 -0
- package/webpack.config.js +63 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {mergeDeepObject, normalizeData} from "../functions";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Класс Placement, определяет и устанавливает местоположение элемента на странице.
|
|
5
|
+
* TODO класс не дописан
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class Placement {
|
|
9
|
+
constructor(arg = {}) {
|
|
10
|
+
this.params = mergeDeepObject({
|
|
11
|
+
element: null,
|
|
12
|
+
drop: null
|
|
13
|
+
}, arg);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_getPlacement() {
|
|
17
|
+
const _this = this;
|
|
18
|
+
const _parent = (self) => {
|
|
19
|
+
let parent = self.parentNode,
|
|
20
|
+
overflow = getComputedStyle(parent).overflow;
|
|
21
|
+
|
|
22
|
+
if (parent.tagName !== 'BODY') {
|
|
23
|
+
if (overflow === 'visible') {
|
|
24
|
+
_parent(parent)
|
|
25
|
+
} else {
|
|
26
|
+
return parent;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let isFixed = false, top, left,
|
|
34
|
+
bounds = _this.params.drop.getBoundingClientRect(),
|
|
35
|
+
parent = _this.params.element.getBoundingClientRect();
|
|
36
|
+
|
|
37
|
+
if (_parent(_this.params.element)) {
|
|
38
|
+
isFixed = true;
|
|
39
|
+
top = bounds.top;
|
|
40
|
+
left = bounds.left;
|
|
41
|
+
} else {
|
|
42
|
+
let styles = getComputedStyle(_this.params.drop);
|
|
43
|
+
top = normalizeData(styles.top.slice(0, -2));
|
|
44
|
+
left = normalizeData(styles.left.slice(0, -2));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ((bounds.left + bounds.width) > window.innerWidth) {
|
|
48
|
+
left = parent.width - bounds.width;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
isFixed: isFixed,
|
|
53
|
+
top: top,
|
|
54
|
+
left: left
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default Placement;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Класс Responsive, работает по таким же медиа точкам, что и bootstrap
|
|
3
|
+
* и определяет на тач устройства.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class Responsive {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.breakpoints = {
|
|
9
|
+
xs: 0,
|
|
10
|
+
sm: 576,
|
|
11
|
+
md: 768,
|
|
12
|
+
lg: 992,
|
|
13
|
+
xl: 1200,
|
|
14
|
+
xxl: 1400,
|
|
15
|
+
xxxl: 1600,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Если наша ширина экрана совпадает с диапазоном который указан в модуле выдаем true, иначе false
|
|
21
|
+
* @param module
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
static check(module) {
|
|
25
|
+
let instance = new this ;
|
|
26
|
+
return instance.define(module);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Проверяет на тач устройства. TODO не совсем правильно, надо сделать по-другому
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
static checkMobileOrTablet() {
|
|
34
|
+
let check = false;
|
|
35
|
+
(function(a) {
|
|
36
|
+
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.slice(0,4))){
|
|
37
|
+
check = true;
|
|
38
|
+
}
|
|
39
|
+
})(navigator.userAgent||navigator.vendor||window.opera);
|
|
40
|
+
|
|
41
|
+
return check;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
define(module) {
|
|
45
|
+
let windowWidth = window.innerWidth,
|
|
46
|
+
responsive_size = this._checkResponsiveClass(module),
|
|
47
|
+
breakpoints = this.breakpoints,
|
|
48
|
+
point = Object.keys(breakpoints).find(key => breakpoints[key] === responsive_size);
|
|
49
|
+
|
|
50
|
+
let keys = Object.keys(breakpoints),
|
|
51
|
+
loc = keys.indexOf(point);
|
|
52
|
+
|
|
53
|
+
return windowWidth >= breakpoints[keys[loc + 1]];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_checkResponsiveClass(module) {
|
|
57
|
+
let element = module._element,
|
|
58
|
+
params = module._params,
|
|
59
|
+
current_responsive_size = 0;
|
|
60
|
+
|
|
61
|
+
if (element.classList.contains(params.classes.XXXL)) {
|
|
62
|
+
current_responsive_size = this.breakpoints.xxxl;
|
|
63
|
+
} else if (element.classList.contains(params.classes.XXL)) {
|
|
64
|
+
current_responsive_size = this.breakpoints.xxl;
|
|
65
|
+
} else if (element.classList.contains(params.classes.XL)) {
|
|
66
|
+
current_responsive_size = this.breakpoints.xl;
|
|
67
|
+
} else if (element.classList.contains(params.classes.LG)) {
|
|
68
|
+
current_responsive_size = this.breakpoints.lg;
|
|
69
|
+
} else if (element.classList.contains(params.classes.MD)) {
|
|
70
|
+
current_responsive_size = this.breakpoints.md;
|
|
71
|
+
} else if (element.classList.contains(params.classes.SM)) {
|
|
72
|
+
current_responsive_size = this.breakpoints.sm;
|
|
73
|
+
} else if (element.classList.contains(params.classes.XS)) {
|
|
74
|
+
current_responsive_size = this.breakpoints.xs;
|
|
75
|
+
} else {
|
|
76
|
+
current_responsive_size = this.breakpoints.xs;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return current_responsive_size
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export default Responsive;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------------------------------------
|
|
3
|
+
* Bootstrap util/scrollBar.js
|
|
4
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
|
+
* --------------------------------------------------------------------------
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {Manipulator} from "../dom/manipulator";
|
|
9
|
+
import {isElement} from "../functions";
|
|
10
|
+
import Selectors from "../dom/selectors";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constants
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
|
|
17
|
+
const SELECTOR_STICKY_CONTENT = '.sticky-top'
|
|
18
|
+
const PROPERTY_PADDING = 'padding-right'
|
|
19
|
+
const PROPERTY_MARGIN = 'margin-right'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Class definition
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
class ScrollBarHelper {
|
|
26
|
+
constructor() {
|
|
27
|
+
this._element = document.body
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Public
|
|
31
|
+
getWidth() {
|
|
32
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
|
|
33
|
+
const documentWidth = document.documentElement.clientWidth
|
|
34
|
+
return Math.abs(window.innerWidth - documentWidth)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
hide() {
|
|
38
|
+
const width = this.getWidth()
|
|
39
|
+
this._disableOverFlow()
|
|
40
|
+
// give padding to element to balance the hidden scrollbar width
|
|
41
|
+
this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
|
|
42
|
+
// trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
|
|
43
|
+
this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
|
|
44
|
+
this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
reset() {
|
|
48
|
+
this._resetElementAttributes(this._element, 'overflow')
|
|
49
|
+
this._resetElementAttributes(this._element, PROPERTY_PADDING)
|
|
50
|
+
this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)
|
|
51
|
+
this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
isOverflowing() {
|
|
55
|
+
return this.getWidth() > 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Private
|
|
59
|
+
_disableOverFlow() {
|
|
60
|
+
this._saveInitialAttribute(this._element, 'overflow')
|
|
61
|
+
this._element.style.overflow = 'hidden'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
_setElementAttributes(selector, styleProperty, callback) {
|
|
65
|
+
const scrollbarWidth = this.getWidth()
|
|
66
|
+
const manipulationCallBack = element => {
|
|
67
|
+
if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this._saveInitialAttribute(element, styleProperty)
|
|
72
|
+
const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)
|
|
73
|
+
element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._applyManipulationCallback(selector, manipulationCallBack)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
_saveInitialAttribute(element, styleProperty) {
|
|
80
|
+
const actualValue = element.style.getPropertyValue(styleProperty)
|
|
81
|
+
if (actualValue) {
|
|
82
|
+
Manipulator.get(element, styleProperty, actualValue)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_resetElementAttributes(selector, styleProperty) {
|
|
87
|
+
const manipulationCallBack = element => {
|
|
88
|
+
const value = Manipulator.get(element, styleProperty)
|
|
89
|
+
// We only want to remove the property if the value is `null`; the value can also be zero
|
|
90
|
+
if (value === null) {
|
|
91
|
+
element.style.removeProperty(styleProperty)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Manipulator.remove(element, styleProperty)
|
|
96
|
+
element.style.setProperty(styleProperty, value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this._applyManipulationCallback(selector, manipulationCallBack)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_applyManipulationCallback(selector, callBack) {
|
|
103
|
+
if (isElement(selector)) {
|
|
104
|
+
callBack(selector)
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const sel of Selectors.findAll(selector, this._element)) {
|
|
109
|
+
callBack(sel)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export default ScrollBarHelper
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------------------------------------
|
|
3
|
+
* Bootstrap data.js
|
|
4
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
|
+
* --------------------------------------------------------------------------
|
|
6
|
+
* Скрипт работает с коллекцией модулей. Подробнее тут https://learn.javascript.ru/map-set
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Константы
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const elementMap = new Map()
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
set(element, key, instance) {
|
|
17
|
+
if (!elementMap.has(element)) {
|
|
18
|
+
elementMap.set(element, new Map())
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const instanceMap = elementMap.get(element)
|
|
22
|
+
if (!instanceMap.has(key) && instanceMap.size !== 0) {
|
|
23
|
+
console.error(`VGApp не допускает более одного экземпляра для каждого элемента. Связанный экземпляр: ${Array.from(instanceMap.keys())[0]}.`)
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
instanceMap.set(key, instance)
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
get(element, key) {
|
|
31
|
+
if (elementMap.has(element)) {
|
|
32
|
+
return elementMap.get(element).get(key) || null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
remove(element, key) {
|
|
39
|
+
if (!elementMap.has(element)) {
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const instanceMap = elementMap.get(element)
|
|
44
|
+
|
|
45
|
+
instanceMap.delete(key);
|
|
46
|
+
|
|
47
|
+
if (instanceMap.size === 0) {
|
|
48
|
+
elementMap.delete(element)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------------------------------------
|
|
3
|
+
* Bootstrap event.js
|
|
4
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
|
+
* --------------------------------------------------------------------------
|
|
6
|
+
* Скрипт для прослушивания события
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Константы
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
|
|
14
|
+
const stripNameRegex = /\..*/
|
|
15
|
+
const stripUidRegex = /::\d+$/
|
|
16
|
+
const eventRegistry = {} // Events storage
|
|
17
|
+
let uidEvent = 1
|
|
18
|
+
const customEvents = {
|
|
19
|
+
mouseenter: 'mouseover',
|
|
20
|
+
mouseleave: 'mouseout'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const nativeEvents = new Set([
|
|
24
|
+
'click',
|
|
25
|
+
'dblclick',
|
|
26
|
+
'mouseup',
|
|
27
|
+
'mousedown',
|
|
28
|
+
'contextmenu',
|
|
29
|
+
'mousewheel',
|
|
30
|
+
'DOMMouseScroll',
|
|
31
|
+
'mouseover',
|
|
32
|
+
'mouseout',
|
|
33
|
+
'mousemove',
|
|
34
|
+
'selectstart',
|
|
35
|
+
'selectend',
|
|
36
|
+
'submit',
|
|
37
|
+
'keydown',
|
|
38
|
+
'keypress',
|
|
39
|
+
'keyup',
|
|
40
|
+
'orientationchange',
|
|
41
|
+
'touchstart',
|
|
42
|
+
'touchmove',
|
|
43
|
+
'touchend',
|
|
44
|
+
'touchcancel',
|
|
45
|
+
'pointerdown',
|
|
46
|
+
'pointermove',
|
|
47
|
+
'pointerup',
|
|
48
|
+
'pointerleave',
|
|
49
|
+
'pointercancel',
|
|
50
|
+
'gesturestart',
|
|
51
|
+
'gesturechange',
|
|
52
|
+
'gestureend',
|
|
53
|
+
'focus',
|
|
54
|
+
'blur',
|
|
55
|
+
'change',
|
|
56
|
+
'reset',
|
|
57
|
+
'select',
|
|
58
|
+
'submit',
|
|
59
|
+
'focusin',
|
|
60
|
+
'focusout',
|
|
61
|
+
'load',
|
|
62
|
+
'unload',
|
|
63
|
+
'beforeunload',
|
|
64
|
+
'resize',
|
|
65
|
+
'move',
|
|
66
|
+
'DOMContentLoaded',
|
|
67
|
+
'readystatechange',
|
|
68
|
+
'error',
|
|
69
|
+
'abort',
|
|
70
|
+
'scroll'
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Приватные методы
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
function makeEventUid(element, uid) {
|
|
78
|
+
return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getElementEvents(element) {
|
|
82
|
+
const uid = makeEventUid(element)
|
|
83
|
+
|
|
84
|
+
element.uidEvent = uid
|
|
85
|
+
eventRegistry[uid] = eventRegistry[uid] || {}
|
|
86
|
+
|
|
87
|
+
return eventRegistry[uid]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function bootstrapHandler(element, fn) {
|
|
91
|
+
return function handler(event) {
|
|
92
|
+
hydrateObj(event, { delegateTarget: element })
|
|
93
|
+
|
|
94
|
+
if (handler.oneOff) {
|
|
95
|
+
EventHandler.off(element, event.type, fn)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return fn.apply(element, [event])
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function bootstrapDelegationHandler(element, selector, fn) {
|
|
103
|
+
return function handler(event) {
|
|
104
|
+
const domElements = element.querySelectorAll(selector)
|
|
105
|
+
|
|
106
|
+
for (let { target } = event; target && target !== this; target = target.parentNode) {
|
|
107
|
+
for (const domElement of domElements) {
|
|
108
|
+
if (domElement !== target) {
|
|
109
|
+
continue
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
hydrateObj(event, { delegateTarget: target })
|
|
113
|
+
|
|
114
|
+
if (handler.oneOff) {
|
|
115
|
+
EventHandler.off(element, event.type, selector, fn)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return fn.apply(target, [event])
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function findHandler(events, callable, delegationSelector = null) {
|
|
125
|
+
return Object.values(events)
|
|
126
|
+
.find(event => event.callable === callable && event.delegationSelector === delegationSelector)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
|
|
130
|
+
const isDelegated = typeof handler === 'string'
|
|
131
|
+
// TODO: выдает "false" вместо селектора, поэтому нужно проверить. boot
|
|
132
|
+
const callable = isDelegated ? delegationFunction : (handler || delegationFunction)
|
|
133
|
+
let typeEvent = getTypeEvent(originalTypeEvent)
|
|
134
|
+
|
|
135
|
+
if (!nativeEvents.has(typeEvent)) {
|
|
136
|
+
typeEvent = originalTypeEvent
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return [isDelegated, callable, typeEvent]
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
|
|
143
|
+
if (typeof originalTypeEvent !== 'string' || !element) {
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
|
|
148
|
+
|
|
149
|
+
// in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
|
|
150
|
+
// this prevents the handler from being dispatched the same way as mouseover or mouseout does
|
|
151
|
+
if (originalTypeEvent in customEvents) {
|
|
152
|
+
const wrapFunction = fn => {
|
|
153
|
+
return function (event) {
|
|
154
|
+
if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
|
|
155
|
+
return fn.call(this, event)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
callable = wrapFunction(callable)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const events = getElementEvents(element)
|
|
164
|
+
const handlers = events[typeEvent] || (events[typeEvent] = {})
|
|
165
|
+
const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)
|
|
166
|
+
|
|
167
|
+
if (previousFunction) {
|
|
168
|
+
previousFunction.oneOff = previousFunction.oneOff && oneOff
|
|
169
|
+
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))
|
|
174
|
+
const fn = isDelegated ?
|
|
175
|
+
bootstrapDelegationHandler(element, handler, callable) :
|
|
176
|
+
bootstrapHandler(element, callable)
|
|
177
|
+
|
|
178
|
+
fn.delegationSelector = isDelegated ? handler : null
|
|
179
|
+
fn.callable = callable
|
|
180
|
+
fn.oneOff = oneOff
|
|
181
|
+
fn.uidEvent = uid
|
|
182
|
+
handlers[uid] = fn
|
|
183
|
+
|
|
184
|
+
element.addEventListener(typeEvent, fn, isDelegated)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
|
|
188
|
+
const fn = findHandler(events[typeEvent], handler, delegationSelector)
|
|
189
|
+
|
|
190
|
+
if (!fn) {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
|
|
195
|
+
delete events[typeEvent][fn.uidEvent]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
|
|
199
|
+
const storeElementEvent = events[typeEvent] || {}
|
|
200
|
+
|
|
201
|
+
for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
|
|
202
|
+
if (handlerKey.includes(namespace)) {
|
|
203
|
+
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function getTypeEvent(event) {
|
|
209
|
+
// allow to get the native events from namespaced events ('click.bs.button' --> 'click')
|
|
210
|
+
event = event.replace(stripNameRegex, '')
|
|
211
|
+
return customEvents[event] || event
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function hydrateObj(obj, meta = {}) {
|
|
215
|
+
for (const [key, value] of Object.entries(meta)) {
|
|
216
|
+
try {
|
|
217
|
+
obj[key] = value
|
|
218
|
+
} catch {
|
|
219
|
+
Object.defineProperty(obj, key, {
|
|
220
|
+
configurable: true,
|
|
221
|
+
get() {
|
|
222
|
+
return value
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return obj
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* События
|
|
233
|
+
* @type {{one(*, *, *, *): void, trigger(*, *, *): (null|*), off(*, *, *, *): void, on(*, *, *, *): void}}
|
|
234
|
+
*/
|
|
235
|
+
const EventHandler = {
|
|
236
|
+
/**
|
|
237
|
+
* Прослушиватель событий (элемент, событие (полный список смотри в константе nativeEvents, источник события или хендлер, функция обратного вызова))
|
|
238
|
+
* @param element
|
|
239
|
+
* @param event
|
|
240
|
+
* @param handler
|
|
241
|
+
* @param delegationFunction
|
|
242
|
+
*/
|
|
243
|
+
on(element, event, handler, delegationFunction) {
|
|
244
|
+
addHandler(element, event, handler, delegationFunction, false)
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Прослушиватель событий, но замыкается и больше не повторяется на элементе
|
|
249
|
+
* @param element
|
|
250
|
+
* @param event
|
|
251
|
+
* @param handler
|
|
252
|
+
* @param delegationFunction
|
|
253
|
+
*/
|
|
254
|
+
one(element, event, handler, delegationFunction) {
|
|
255
|
+
addHandler(element, event, handler, delegationFunction, true)
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Удаление обработчика
|
|
260
|
+
* @param element
|
|
261
|
+
* @param originalTypeEvent
|
|
262
|
+
* @param handler
|
|
263
|
+
* @param delegationFunction
|
|
264
|
+
*/
|
|
265
|
+
off(element, originalTypeEvent, handler, delegationFunction) {
|
|
266
|
+
if (typeof originalTypeEvent !== 'string' || !element) {
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
|
|
271
|
+
const inNamespace = typeEvent !== originalTypeEvent
|
|
272
|
+
const events = getElementEvents(element)
|
|
273
|
+
const storeElementEvent = events[typeEvent] || {}
|
|
274
|
+
const isNamespace = originalTypeEvent.startsWith('.')
|
|
275
|
+
|
|
276
|
+
if (typeof callable !== 'undefined') {
|
|
277
|
+
// Simplest case: handler is passed, remove that listener ONLY.
|
|
278
|
+
if (!Object.keys(storeElementEvent).length) {
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)
|
|
283
|
+
return
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (isNamespace) {
|
|
287
|
+
for (const elementEvent of Object.keys(events)) {
|
|
288
|
+
removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
|
|
293
|
+
const handlerKey = keyHandlers.replace(stripUidRegex, '')
|
|
294
|
+
|
|
295
|
+
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
|
|
296
|
+
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Пользовательские события. Подробнее тут https://learn.javascript.ru/dispatch-events
|
|
303
|
+
* @param element
|
|
304
|
+
* @param event
|
|
305
|
+
* @param args
|
|
306
|
+
* @returns {*|null}
|
|
307
|
+
*/
|
|
308
|
+
trigger(element, event, args) {
|
|
309
|
+
if (typeof event !== 'string' || !element) {
|
|
310
|
+
return null
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
let bubbles = true;
|
|
314
|
+
let nativeDispatch = true;
|
|
315
|
+
let defaultPrevented = false;
|
|
316
|
+
|
|
317
|
+
const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)
|
|
318
|
+
|
|
319
|
+
if (defaultPrevented) {
|
|
320
|
+
evt.preventDefault()
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (nativeDispatch) {
|
|
324
|
+
element.dispatchEvent(evt)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return evt
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
export default EventHandler
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {isElement, normalizeData} from "../functions";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Манипуляции с атрибутами у элемента:
|
|
5
|
+
* get (элемент, имя, флаг - вырезать data-) - метод выбирает значение атрибута по его имени, если в поле имени передать 'data' -> будут выбраны только дата атрибуты, если 'all' -> метод вернет значение всех атрибутов
|
|
6
|
+
* has (элемент, имя) - есть ли атрибут у элемента
|
|
7
|
+
* set (элемент, имя, значение) - установка у элемента атрибута или его изменение
|
|
8
|
+
* remove (элемент, имя) - удаляет атрибут у элемента
|
|
9
|
+
*/
|
|
10
|
+
const Manipulator = {
|
|
11
|
+
get(element, nameAttribute = 'data', isRemoveDataName = true) {
|
|
12
|
+
if (!element) {
|
|
13
|
+
return {}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (nameAttribute === 'data') {
|
|
17
|
+
let elmBase = ['data-vg-toggle', 'data-vg-target', 'data-vg-dismiss'],
|
|
18
|
+
attributes = {};
|
|
19
|
+
|
|
20
|
+
let arr = [].filter.call(element.attributes, function (at) {
|
|
21
|
+
return /^data-/.test(at.name);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (arr.length) {
|
|
25
|
+
arr.forEach(function (v) {
|
|
26
|
+
let name = v.name;
|
|
27
|
+
|
|
28
|
+
if (!elmBase.includes(name)) {
|
|
29
|
+
if (isRemoveDataName) name = name.slice(5);
|
|
30
|
+
attributes[name] = normalizeData(v.value)
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return attributes;
|
|
36
|
+
} else if (nameAttribute === 'all') {
|
|
37
|
+
return element.getAttributeNames().reduce((acc, name) => {
|
|
38
|
+
return {...acc, [name]: element.getAttribute(name)};
|
|
39
|
+
}, {});
|
|
40
|
+
} else {
|
|
41
|
+
return element.getAttribute(nameAttribute);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
has(element, nameAttribute) {
|
|
46
|
+
return element.hasAttribute(nameAttribute);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
set(element, name, value) {
|
|
50
|
+
if (isElement(element) && name) {
|
|
51
|
+
element.setAttribute(name, value);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
remove(element, nameAttribute) {
|
|
56
|
+
if (isElement(element) && nameAttribute) {
|
|
57
|
+
element.removeAttribute(nameAttribute);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {Manipulator}
|