');
58 |
59 | let newParent = singleAttr['0'].parentNode;
60 |
61 | expect(firstParent.matches('#wrap')).to.be.equal(true);
62 | expect(newParent.matches('#wrap-id')).to.be.equal(true);
63 | expect(newParent.classList.contains('new-wrap')).to.be.equal(true);
64 | });
65 | });
66 |
67 | describe('Multiple Elements Wrap', () => {
68 | it('should wrap siblings with same parent', () => {
69 | let list = _("#wrap > li");
70 | let parent1 = list['0'].parentNode;
71 | let parent2 = list['1'].parentNode;
72 |
73 | list.wrapAll('ul');
74 |
75 | let oldList = _("#wrap > li");
76 | let newParent1 = list['0'].parentNode;
77 | let newParent2 = list['1'].parentNode;
78 |
79 | expect(list).not.to.be.equal(oldList);
80 | expect(parent1.nodeName.toUpperCase()).to.be.equal('DIV');
81 | expect(parent2.nodeName.toUpperCase()).to.be.equal('DIV');
82 | expect(parent1).to.be.equal(parent2);
83 | expect(newParent1.nodeName.toUpperCase()).to.be.equal('UL');
84 | expect(newParent2.nodeName.toUpperCase()).to.be.equal('UL');
85 | expect(newParent1).to.be.equal(newParent2);
86 | });
87 |
88 | it('should wrap the element even if it is the only child', () => {
89 | let list = _("#wrap-all > span");
90 | let parent1 = list['0'].parentNode;
91 |
92 | list.wrapAll('p');
93 |
94 | let oldList = _("#wrap-all > span");
95 | let newParent1 = list['0'].parentNode;
96 |
97 | expect(list).not.to.be.equal(oldList);
98 | expect(parent1.nodeName.toUpperCase()).to.be.equal('DIV');
99 | expect(newParent1.nodeName.toUpperCase()).to.be.equal('P');
100 | });
101 | });
102 |
103 | describe('Single Element UnWrap', () => {
104 | let single = _('#wrap-single');
105 | let singleAttr = _('#wrap-single-attr');
106 |
107 | it('should unwraps the element', () => {
108 | let parent = single['0'].parentNode;
109 |
110 | single.unwrap();
111 |
112 | let newParent = single['0'].parentNode;
113 |
114 | expect(parent).not.to.be.equal(newParent);
115 | });
116 |
117 | it('will not unwrap the element if parent selector is not matched', () => {
118 | let parent = singleAttr['0'].parentNode;
119 |
120 | singleAttr.unwrap('.not-matching-selector');
121 |
122 | let newParent = singleAttr['0'].parentNode;
123 |
124 | expect(parent).to.be.equal(newParent);
125 | });
126 |
127 | it('should remove parent, if after unwrapping it become an empty element', () => {
128 | let parent = singleAttr.parent();
129 |
130 | singleAttr.unwrap();
131 |
132 | expect(document.body.contains(parent[0])).to.be.equal(false);
133 | });
134 | });
135 | });
--------------------------------------------------------------------------------
/src/event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * event
3 | **/
4 |
5 | /* global hashCode */
6 | /* global maybeMultiple */
7 | /* global isObject */
8 |
9 | /*const events = {
10 | animation: ['animationend', 'animationiteration', 'animationstart'],
11 | drag: ['drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop'],
12 | frame: ['abort', 'beforeunload', 'error', 'hashchange', 'load', 'pageshow', 'pagehide', 'resize', 'scroll', 'unload'],
13 | form: ['blur', 'change', 'focus', 'focusin', 'focusout', 'input', 'invalid', 'reset', 'search', 'select', 'submit'],
14 | keyboard: ['keydown', 'keypress', 'keyup'],
15 | media: ['abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting'],
16 | misc: ['message', 'mousewheel', 'online', 'offline', 'popstate', 'show', 'storage', 'toggle', 'wheel'],
17 | mouse: ['click', 'contextmenu', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseover', 'mouseout', 'mouseup'],
18 | print: ['afterprint', 'beforeprint'],
19 | server: ['error', 'message', 'open'],
20 | transition: ['transitionend'],
21 | touch: ['touchcancel', 'touchend', 'touchmove', 'touchstart']
22 | };
23 |
24 | const allEvents = Object.keys(events).reduce((acc, cur) => acc.concat(events[cur]), []);*/
25 |
26 | const removeEventFromItem = (item, event, fn) => {
27 | item.removeEventListener(event, item.uxrAttachedEvents[event][fn]);
28 | delete item.uxrAttachedEvents[event][fn];
29 |
30 | return item;
31 | };
32 |
33 | _.extend.off = function (eventName, eventHandlerOrSelector, eventHandler) {
34 | let stack = this;
35 | let handler = eventHandlerOrSelector;
36 | let events = maybeMultiple(eventName);
37 |
38 | if (typeof eventHandlerOrSelector === 'string'
39 | || typeof eventHandler !== 'undefined') {
40 | handler = eventHandler;
41 | stack = this.find(eventHandlerOrSelector);
42 | }
43 |
44 | stack.el.forEach(item => {
45 | item.uxrAttachedEvents = item.uxrAttachedEvents || {};
46 |
47 | events.forEach(event => {
48 | // make sure not to give an error, if user tried to remove unattached event
49 | if (typeof item.uxrAttachedEvents[event] !== 'undefined') {
50 | if (typeof handler === 'undefined') {
51 | Object.keys(item.uxrAttachedEvents[event]).forEach(fn => {
52 | removeEventFromItem(item, event, fn);
53 | });
54 | }
55 |
56 | else {
57 | let hash = hashCode((handler).toString());
58 | removeEventFromItem(item, event, hash);
59 | }
60 | }
61 | });
62 | });
63 |
64 | return this;
65 | };
66 |
67 | _.extend.on = function (eventName, eventHandlerOrSelector, eventHandler) {
68 | let stack = this;
69 | let handler = eventHandlerOrSelector;
70 | let events = maybeMultiple(eventName);
71 |
72 | if (typeof eventHandler !== 'undefined') {
73 | handler = eventHandler;
74 | stack = this.find(eventHandlerOrSelector);
75 | }
76 |
77 | let hash = hashCode((handler).toString());
78 |
79 | stack.el.forEach(item => {
80 | item.uxrAttachedEvents = item.uxrAttachedEvents || {};
81 |
82 | events.forEach(event => {
83 | item.uxrAttachedEvents[event] = item.uxrAttachedEvents[event] || {};
84 | item.uxrAttachedEvents[event][hash] = handler;
85 |
86 | item.addEventListener(event, item.uxrAttachedEvents[event][hash]);
87 | });
88 | });
89 |
90 | return this;
91 | };
92 |
93 | _.extend.once = function (eventName, eventHandlerOrSelector, eventHandler) {
94 | let stack = this;
95 | let handler = eventHandlerOrSelector;
96 | let events = maybeMultiple(eventName);
97 |
98 | if (typeof eventHandler !== 'undefined') {
99 | handler = eventHandler;
100 | stack = this.find(eventHandlerOrSelector);
101 | }
102 |
103 | stack.el.forEach(item => {
104 | events.forEach(event => {
105 | /* istanbul ignore next */
106 | let oneHandler = e => {
107 | e.preventDefault();
108 | _(item).off(event, handler);
109 | _(item).off(event, oneHandler);
110 | };
111 |
112 | _(item).on(event, handler);
113 | _(item).on(event, oneHandler);
114 | });
115 | });
116 |
117 | return this;
118 | };
119 |
120 | _.extend.trigger = function (eventName, eventParamsOrSelector, eventParams) {
121 | let stack = this;
122 | let event;
123 |
124 | if (eventParamsOrSelector !== 'undefined' && typeof eventParamsOrSelector === 'string') {
125 | stack = this.find(eventParamsOrSelector);
126 | }
127 |
128 | if (!_eventHasParams(eventParamsOrSelector, eventParams)) {
129 | event = getNativeEvent(eventName);
130 | }
131 |
132 | else {
133 | event = getCustomEvent(eventName, eventParamsOrSelector, eventParams);
134 | }
135 |
136 | stack.el.forEach(item => item.dispatchEvent(event));
137 |
138 | return this;
139 | };
140 |
141 | const _eventHasParams = (eventParamsOrSelector, eventParams) => {
142 | return (typeof eventParamsOrSelector !== 'undefined' && isObject(eventParamsOrSelector)) || typeof eventParams !== 'undefined';
143 | };
144 |
145 | const getNativeEvent = (eventName) => {
146 | return new Event(eventName);
147 | };
148 |
149 | const getCustomEvent = (eventName, eventParamsOrSelector, eventParams) => {
150 | let params = eventParams;
151 |
152 | if (typeof eventParamsOrSelector !== 'undefined'
153 | && isObject(eventParamsOrSelector)) {
154 | params = eventParamsOrSelector;
155 | }
156 |
157 | return new CustomEvent(eventName, {detail: params});
158 | };
--------------------------------------------------------------------------------
/test/event-test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * event-test
3 | **/
4 |
5 | describe('Event Manager', () => {
6 | // event test
7 | (() => {
8 | let div = createElement('div', {id: 'event'});
9 | let input = createElement('input', {id: 'event-input', type: 'text'});
10 | let p = createElement('p', {
11 | id: 'event-paragraph',
12 | innerHTML: '
test' +
13 | '
test'
14 | });
15 |
16 | appendTo(div, [input, p]);
17 | appendToBody(div);
18 | })();
19 |
20 | let inputElem = _('#event-input');
21 | let paragraphElem = _('#event-paragraph');
22 |
23 | let value = '';
24 | let triggered = [];
25 | let handler = e => {
26 | e.preventDefault();
27 | triggered.push(e.currentTarget.dataset.trigger);
28 | };
29 |
30 | describe('Add Event', () => {
31 | it('should add an event', () => {
32 | inputElem.on('change', e => value = e.currentTarget.value);
33 |
34 | inputElem.attr('value', controls.value);
35 | inputElem.trigger('change');
36 |
37 | expect(value).to.be.equal(controls.value);
38 | });
39 |
40 | it('should add multiple events at once', () => {
41 | inputElem.on('focus blur', e => triggered.push(e.type));
42 |
43 | inputElem.trigger('focus');
44 | inputElem.trigger('blur');
45 |
46 | expect(triggered.includes('focus')).to.be.true;
47 | expect(triggered.includes('blur')).to.be.true;
48 | });
49 |
50 | it('should add an event to child element', () => {
51 | paragraphElem.on('click', '.event-link', handler);
52 |
53 | paragraphElem.find('.event-link').trigger('click');
54 |
55 | expect(triggered.includes('child-element')).to.be.true;
56 | });
57 |
58 | it('should stores the attached events in `uxrAttachedEvents` on item', () => {
59 | expect(inputElem[0].uxrAttachedEvents.change).to.not.be.undefined;
60 | });
61 | });
62 |
63 | describe('Remove Event', () => {
64 | it('should remove an event', () => {
65 | value = '';
66 | inputElem.off('change');
67 | inputElem.attr('value', controls.value);
68 | inputElem.trigger('change');
69 |
70 | inputElem.off('this-should-be-skipped');
71 |
72 | expect(inputElem[0].uxrAttachedEvents['change']).to.be.empty;
73 | expect(inputElem.attr('value')).to.not.be.equal(value);
74 | });
75 |
76 | it('should remove event with defined handler', () => {
77 | let eventResult = {};
78 | let handler = e => {
79 | eventResult['withHandler'] = e.type
80 | };
81 |
82 | inputElem.on('change', handler);
83 | inputElem.on('change', e => {
84 | eventResult['withAnonFunc'] = e.type
85 | });
86 |
87 | inputElem.off('change', handler);
88 | inputElem.trigger('change');
89 |
90 | expect(eventResult['withHandler']).to.be.undefined;
91 | expect(eventResult['withAnonFunc']).to.be.equal('change');
92 | });
93 |
94 | it('should remove multiple events at once', () => {
95 | inputElem.off('focus blur');
96 | expect(inputElem[0].uxrAttachedEvents['focus']).to.be.empty;
97 | expect(inputElem[0].uxrAttachedEvents['blur']).to.be.empty;
98 | });
99 |
100 | it('should remove the attached event with a handler from child element', () => {
101 | // if you send handler, it only removes the handler but event type stays in element object
102 | paragraphElem.off('click', '.event-link', handler);
103 |
104 | expect(paragraphElem.find('.event-link')[0].uxrAttachedEvents['click']).to.be.empty;
105 | });
106 |
107 | it('should remove the attached event with anonymous func from child element', () => {
108 | paragraphElem.on('click', '.event-link2', e => {
109 | e.preventDefault();
110 | triggered.push(e.currentTarget.dataset.trigger);
111 | });
112 |
113 | _('.event-link2').trigger('click');
114 |
115 | paragraphElem.off('click', '.event-link2');
116 |
117 | expect(triggered.includes('child-element2')).to.be.true;
118 | expect(paragraphElem.find('.event-link2')[0].uxrAttachedEvents['click']).to.be.empty;
119 | });
120 | });
121 |
122 | describe('Single Run Event', () => {
123 | it('should runs the event one time and remove itself', () => {
124 | value = '';
125 | inputElem.once('focus', e => value = e.currentTarget.value);
126 | inputElem.trigger('focus');
127 |
128 | inputElem.attr('value', controls.newValue);
129 | inputElem.trigger('focus');
130 |
131 | expect(value).to.not.be.equal(controls.newValue);
132 | });
133 |
134 | it('should runs the event one time and remove itself from a child element', () => {
135 | let counter = 0;
136 |
137 | paragraphElem.once('click', '.event-link2', e => {
138 | e.preventDefault();
139 | counter++;
140 | });
141 |
142 | // first trigger => counter = 1;
143 | paragraphElem.find('.event-link2').trigger('click');
144 |
145 | // second trigger => counter = 1 no changed
146 | paragraphElem.find('.event-link2').trigger('click');
147 |
148 | expect(counter).to.not.be.equal(1);
149 | });
150 | });
151 |
152 | describe('Event Trigger', () => {
153 | it('should trigger an event', () => {
154 | let counter = 0;
155 | paragraphElem.on('click', () => counter++);
156 | paragraphElem.trigger('click');
157 | paragraphElem.off('click');
158 | expect(counter).to.be.equal(1);
159 | });
160 |
161 | it('should trigger an event on children elements', () => {
162 | let counter = 0;
163 | paragraphElem.on('click', '.event-link', () => counter++);
164 | paragraphElem.trigger('click'); // no effect
165 | paragraphElem.trigger('click', '.event-link');
166 | paragraphElem.off('click', '.event-link');
167 | paragraphElem.off('click');
168 | expect(counter).to.be.equal(1);
169 | });
170 |
171 | it('should trigger an event', () => {
172 | let counter = 0;
173 | let event = {};
174 |
175 | paragraphElem.on('customClick', (e) => {
176 | event = e;
177 | counter++
178 | });
179 | paragraphElem.trigger('customClick', {custom: 'params'});
180 | paragraphElem.off('customClick');
181 | expect(counter).to.be.equal(1);
182 | expect(event.detail.custom).to.be.equal('params');
183 | });
184 |
185 | it('should trigger an event with params in children elements', () => {
186 | let counter = 0;
187 | let event = {};
188 |
189 | paragraphElem.on('customClick', '.event-link', (e) => {
190 | event = e;
191 | counter++
192 | });
193 | paragraphElem.trigger('customClick', '.event-link', {custom: 'params'});
194 | paragraphElem.off('customClick', '.event-link');
195 | expect(counter).to.be.equal(1);
196 | expect(event.detail.custom).to.be.equal('params');
197 | });
198 | });
199 | });
--------------------------------------------------------------------------------
/dist/uxr.min.js:
--------------------------------------------------------------------------------
1 | function t(e){var g=0;return function(){return g
2 |
3 | # UXR
4 | [![npm][npm]][npm-url]
5 | [![tests][tests]][tests-url]
6 | [![coverage][cover]][cover-url]
7 | [![devDependencies Status][dm]][dm-url]
8 | [![Maintainability][cc]][cc-url]
9 | [![Open Source Love][os]][os-url]
10 | [![PRs Welcome][pr]][pr-url]
11 |
12 |
13 | A minimal in mind library for DOM related routines and element selections. UXR wraps some most used methods like CSS Classes, Event Management, Data Management, Attribute selections/updates. Supports chaining and plugins.
14 |
15 | UXR has the philosophy of fewer code and low file size. Because of this, widely supported ES6 codes are not transpiled to ES5 versions and not trying to cover all JavaScript methods which are normally written without much effort. UXR provides easy to wrappers for normally complex once.
16 |
17 | ## Browser Support
18 | | Browser |

|

|

|

|

|
19 | | ------- | ----------------- | ------------------- | --------------- | ----------------- | ------------- |
20 | | Version | 49+ | 36+ | 37+ | 10+ | 12+ |
21 |
22 | ## How To Use
23 | You can install UXR via Node package managers
24 | ``` js
25 | $ npm install uxr
26 | // or
27 | $ yarn add uxr
28 | ```
29 |
30 | Or you can directly include you `dist` files to your project after downloading the desired version.
31 |
32 | After adding the `dist/uxr.min.js` to your page, you can select an element set from DOM and start to manipulate/modify the selection.
33 |
34 | ### Loading UXR methods
35 | You can define UXR methods to run when page loads and content ready. Or run when needed.
36 |
37 | ``` js
38 | uxr.ready(function(){
39 | // inner functions automatically runs when document is ready
40 | });
41 |
42 | uxr.load(function(){
43 | // inner functions automatically runs when document is fully loaded
44 | });
45 | ```
46 |
47 | ### Element Selection
48 | Every `uxr` methods starts with element selections. Basically selection uses `querySelectorAll` getting element from DOM or Arrays.
49 |
50 | ``` js
51 | // selecting an element with querySelectorAll supported selector strings
52 | uxr(selector);
53 |
54 | // getting an array as selector
55 | uxr([0, 1, 2, 3])
56 | ```
57 |
58 | ### Chaining
59 | `uxr` methods supports chaining. So you can call `uxr` methods one after another.
60 |
61 | ``` js
62 | uxr(selector)
63 | .addClass('hello')
64 | .find('a')
65 | .on('click', e => {
66 | e.preventDefault();
67 | console.log('Hello World!')
68 | })
69 | .end()
70 | .attr('id', 'my-id');
71 | ```
72 |
73 | ### Attribute Manipulation
74 | With `uxr(selector).attr()` methods you can get an attribute's value or set a value in HTML tags
75 |
76 | ``` js
77 | let el = uxr(selector);
78 |
79 | // get the ID
80 | let id = el.attr('id');
81 | // getAttr method is an alias to attr(name)
82 | let id = el.getAttr('id');
83 |
84 | // set the ID
85 | el.attr('id', 'new-id');
86 | // setAttr method is an alias to attr(name, value);
87 | el.setAttr('id', 'new-id');
88 |
89 | // get a data-attr value
90 | // the following both samples gets the same attribute
91 | el.attr('data-attr');
92 | el.attr('dataAttr');
93 |
94 | // Remove an attribute from HTML
95 | el.removeAttr('id');
96 | el.removeAttribute('id');
97 | ```
98 |
99 | There are some, easy to use - easy to remember attribute methods
100 |
101 |
102 | * `uxr(selector).src(value)` : if you send the `value` it sets the `src` of the element. Otherwise returns the `src` value.
103 | * `uxr(selector).href(value)` : if you send the `value` it sets the `href` value of the anchor with the new one. Otherwise returns the `href` value.
104 |
105 | ### Props
106 | With `uxr(selector).prop()` methods you can get an DOM node element's properties. This is different than attr methods where it get native elements properties rather than HTML attributes.
107 |
108 | ``` js
109 | let el = uxr(selector);
110 |
111 | // get a property
112 | let innerText = el.prop('innerText');
113 |
114 | // set a property
115 | el.prop('innerText', 'New Text');
116 | ```
117 |
118 | There are some, easy to use - easy to remember property methods
119 | * `uxr(selector).text(value)` : if you send the `value` it sets the `innerText` value with the new one. Otherwise returns the `innerText` value.
120 | * `uxr(selector).html(value)` : if you send the `value` it sets the `innerHTML` value with the new one. Otherwise returns the `innerHTML` value.
121 | * `uxr(selector).value(value)` : if you send the `value` it sets the value of form elements with the new one. Otherwise returns the value of the form element.
122 |
123 | ### Class Manipulation
124 | With `uxr` it is easier to add/remove or check classes. All for class manipulation methods supports multiple class names separated with space and setting class starting with dot (`.`)
125 |
126 | ``` js
127 | let el = uxr(selector);
128 |
129 | // add a new css class
130 | el.addClass('new-class');
131 | el.addClass('.new-class');
132 |
133 | // add multiple classes at once
134 | el.addClass('.a set .of classes');
135 | el.addClass(['array', 'of', 'classes']);
136 |
137 | // remove a css class
138 | el.removeClass('old-class');
139 | el.removeClass('.old-class');
140 |
141 | // toggles a class
142 | el.toggleClass('class-to-toggle');
143 | el.toggleClass('.class-to-toggle');
144 |
145 | // checks if has the class or not
146 | el.hasClass('class-to-check');
147 | el.hasClass('.class-to-check');
148 | ```
149 |
150 | ### Data Attributes
151 | Data method gets or sets a data attribute. If `dataset` supported, gets or sets values via `dataset` otherwise, uses the `getAttribute` method to get value. Both supports _camelCase_ and _dashed_ attribute names.
152 |
153 | ``` js
154 | let el = uxr(selector);
155 |
156 | // get data value
157 | el.data('uxr-demo');
158 | el.data('uxrDemo');
159 |
160 | // set data value
161 | el.data('uxr-demo', true);
162 | el.data('uxrDemo', true);
163 | ```
164 |
165 | ### Events
166 | DOM events can easily attached to elements. Named events and anonymous events supported while attaching the event. Also _triggering_ an event is possible.
167 |
168 | #### Add Events
169 |
170 | ``` js
171 | let myFunc = (e) => { console.log(e.currentTarget);}
172 |
173 | // attach an event
174 | uxr(selector).on('click', myFunc);
175 |
176 | // attach an event to child
177 | uxr(selector).on('click', '.clickable', myFunc);
178 |
179 | // attach multiple event
180 | uxr(selector).on('input focus', e => { console.log(e.currentTarget.value); }
181 | ```
182 |
183 | #### Remove Events
184 |
185 | ``` js
186 | // remove all click events
187 | uxr(selector).off('click');
188 |
189 | // remove only click event attached with myFunc
190 | uxr(selector).off('click', myFunc);
191 |
192 | // remove events attached with an anonymous function
193 | uxr(selector).off('input focus', e => { console.log(e.currentTarget.value); }
194 | ```
195 |
196 | #### Single Run Events
197 | Single Run events are only run once then remove itself from the element.
198 |
199 | ``` js
200 | // run once
201 | uxr(selector).once('touchend', e => { console.log('touch ended'); })
202 | ```
203 |
204 | #### Trigger Events
205 | Native and custom events can be triggered by using trigger method. Similar to event bindings, event trigger can be triggered on children elements. Despite event binding, where you can bind multiple events at once, you can trigger one event at a time.
206 |
207 | ``` js
208 | // trigger a click event
209 | uxr(selector).trigger('click');
210 |
211 | // trigger a custom event
212 | uxr(selector).trigger('custom');
213 |
214 | // trigger a focus event in children
215 | uxr(selector).trigger('focus', 'input[type=text]');
216 |
217 | // trigger event with params
218 | uxr(selector).trigger('click', {custom: 'params', another: {custom: 'paramater'}});
219 |
220 | // trigger event with params in children
221 | uxr(selector).trigger('blur', 'textarea', {custom: 'params', another: {custom: 'paramater'}});
222 | ```
223 |
224 | ### Wrapper Methods
225 | With wrapper methods, you can wrap element or elements to a new parent or unwrap them.
226 |
227 | ``` js
228 | let single = uxr('.single-element');
229 | let list = uxr('.list-element');
230 |
231 | // wrap element
232 | single.wrap('div');
233 |
234 | // wrap all elements in a single parent
235 | list.wrapAll('
');
236 |
237 | // Unwrap the parent
238 | single.unwrap();
239 | ```
240 |
241 | Unwrap removes the immediate parent. If a selector also defined for the unwrap as `el.unwrap(selector)`, it check if the immediate parent matches the selector.
242 |
243 | For wrapper definitions, you can define wrapper string without brackets, with brackets, with attributes etc.
244 | All of the following strings are valid wrapper definitions
245 |
246 | * `div` _only name of the tag_
247 | * `
` _tag name with brackets_
248 | * `
`
249 | * `
`
250 | * `
` _tag name with attributes_
251 | * `
`
252 |
253 | ### Element Insertions
254 | By using `before`, `after`, `prepend` and `append` you can control where to insert newly created elements. Also with `replaceWith` you can swap the element with a new one.
255 |
256 | ``` js
257 | let el = uxr('.container');
258 |
259 | // adds an element before selection
260 | el.before('
This will be before of "el"
');
261 | el.before(uxr('#new'));
262 |
263 | // adds an element after selection
264 | el.after('
This will be after of "el"
');
265 | el.after(uxr('#new'));
266 |
267 | // appends an element add the end of selection's content
268 | el.append('
This will be at the end of "el"
');
269 | el.append(uxr('#new'));
270 |
271 | // appends an element add the beginning of selection's content
272 | el.prepend('
This will be at the beginning of "el"
');
273 | el.prepend(uxr('#new'));
274 |
275 | // replaces the element with new one
276 | el.replaceWith('
Previous element replaced
');
277 | ```
278 |
279 | ### Filtering and Finding
280 | Filtering methods help to find or filter elements in a UXR object.
281 |
282 | ``` js
283 | // create a subset of elements in a UXR object
284 | uxr(selector).filter(anotherSelector);
285 |
286 | // create a subset of elements that a not matched the selector in a UXR object
287 | uxr(selector).not(anotherSelector);
288 |
289 | // find / select children elements in a UXR object
290 | // has method is an alias to find
291 | uxr(selector).find(childrenSelector);
292 | uxr(selector).has(childrenSelecotr);
293 | ```
294 |
295 | ### Traversing
296 | With traversal methods, you can find adjacent or parent elements accordingly. Almost all traversal methods returns a `uxr` object. You can return the previous `uxr` by chaining `end()`
297 |
298 | ``` js
299 |
300 | let el = uxr('li');
301 |
302 | // get the immediate parent
303 | el.closest();
304 |
305 | // get the grandparent
306 | el.closest().closest();
307 |
308 | // filter the parents and get the first matched
309 | el.closest(selector);
310 |
311 | // get the next sibling
312 | el.next();
313 |
314 | // get the next sibling if matched
315 | el.next(selector);
316 |
317 | // get the previous sibling
318 | el.prev();
319 |
320 | // get the previous sibling if matched
321 | el.prev(selector);
322 |
323 | // get the first element in uxr object - selection
324 | el.first();
325 |
326 | // get the last element in uxr object - selection
327 | el.last();
328 |
329 | // get the immediate parent
330 | el.parent();
331 |
332 | // get the immediate parent if matched to selector
333 | el.parent(selector);
334 |
335 | // get the all children element
336 | el.children();
337 |
338 | // get the all matched children
339 | el.children(selector);
340 |
341 | // get the all siblings
342 | el.siblings();
343 |
344 | // get the all matched siblings
345 | el.siblings(selector);
346 | ```
347 |
348 | ### CSS
349 | `css` method helps to set or get style attributes of the elements.
350 |
351 | ``` js
352 | let el = uxr(selector);
353 |
354 | // get a style property
355 | el.css('display'); // returns the display property value
356 |
357 | // get a list of style properties
358 | // returns an object with listed values.
359 | // note that, you can ask for properties both kebap-case and camelCase
360 | el.css(['display', 'margin', 'padding-top', 'borderLeft']);
361 | // returns {display: value, margin: value, paddingTop: value, borderLeft: value}
362 |
363 | // sets or updates a single property
364 | el.css('padding', '10px');
365 | el.css('background-color', '#ccc');
366 | el.css('backgroundSize', '100% auto');
367 |
368 | // sets or updates a list of properties
369 | // note that, you can send a object contains property:value pairs
370 | el.css({width: '100px', height: '50px', 'margin-bottom': '5px'});
371 | ```
372 |
373 | ### Dimensions
374 | Dimension related methods returns or sets content width or height according to dimension method. Except setting `width` and `height` methods, all other usages break the chaining.
375 |
376 | ``` js
377 | let el = uxr(selector);
378 |
379 | // returns the first elements content width
380 | // note that: this return only the content width, no-border, no-padding, no-margin
381 | el.width();
382 | el.contentWidth(); // alias method
383 |
384 | // sets the width of elements in the uxr object.
385 | // similar method to el.css('width', value);
386 | el.width('100px');
387 | el.contentWidth('100%');
388 |
389 | // returns the clientWidth of the first element
390 | // note that: this is only differs from width method with addition of padding
391 | el.innerWidth();
392 | el.clientWidth(); // alias method
393 |
394 |
395 | // returns the offsetWidth of the first element
396 | // note that: this calculates width with border, padding and content-width altogether
397 | el.outerWidth();
398 | el.offsetWidth(); // alias method
399 |
400 | // returns the offsetWidth of the first element including margins
401 | // note that: this calculates width with margin, border, padding and content-width altogether
402 | el.outerWidth(true);
403 | el.offsetWidth(true); // alias method
404 |
405 | // returns the first elements content height
406 | // note that: this return only the content height, no-border, no-padding, no-margin
407 | el.height();
408 | el.contentHeight(); // alias method
409 |
410 | // sets the height of elements in the uxr object.
411 | // similar method to el.css('height', value);
412 | el.height('100px');
413 | el.contentHeight('100%');
414 |
415 | // returns the clientHeight of the first element
416 | // note that: this is only differs from width method with addition of padding
417 | el.innerHeight();
418 | el.clientHeight(); // alias method
419 |
420 |
421 | // returns the offsetHeight of the first element
422 | // note that: this calculates height with border, padding and content-height altogether
423 | el.outerHeight();
424 | el.offsetHeight(); // alias method
425 |
426 | // returns the offsetHeight of the first element including margins
427 | // note that: this calculates height with margin, border, padding and content-height altogether
428 | el.outerHeight(true);
429 | el.offsetHeight(true); // alias method
430 | ```
431 |
432 | ### Cloning
433 | Clone methods, clones the nodes in a UXR object.
434 |
435 | ``` js
436 | let el = uxr(selector);
437 |
438 | // clones the all elements in uxr object
439 | let clone = el.clone();
440 |
441 | // deep clones (child elements and inner contents) the all elements in uxr object
442 | let cloneDeep = el.clone(true);
443 | ```
444 |
445 | [npm]: https://img.shields.io/npm/v/uxr.svg
446 | [npm-url]: https://npmjs.com/package/uxr
447 |
448 | [tests]: http://img.shields.io/travis/bcinarli/uxr.svg
449 | [tests-url]: https://travis-ci.org/bcinarli/uxr
450 |
451 | [cover]: https://codecov.io/gh/bcinarli/uxr/branch/master/graph/badge.svg
452 | [cover-url]: https://codecov.io/gh/bcinarli/uxr
453 |
454 | [os]:https://badges.frapsoft.com/os/v2/open-source.svg?v=103
455 | [os-url]:(https://github.com/ellerbrock/open-source-badges/)
456 |
457 | [pr]:https://img.shields.io/badge/PRs-welcome-brightgreen.svg
458 | [pr-url]:http://makeapullrequest.com
459 |
460 | [cc]:https://api.codeclimate.com/v1/badges/2da503653af06036b031/maintainability
461 | [cc-url]: https://codeclimate.com/github/bcinarli/uxr/maintainability
462 |
463 | [dm]:https://david-dm.org/bcinarli/uxr/dev-status.svg
464 | [dm-url]:https://david-dm.org/bcinarli/uxr?type=dev
465 |
--------------------------------------------------------------------------------
/dist/uxr.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | /**
3 | * uxr
4 | **/
5 |
6 | const _ = window['uxr'] = function (selector) {
7 | return new uxr(selector);
8 | };
9 |
10 | const uxr = function (selector) {
11 | this.selector = selector;
12 | this.init();
13 | };
14 |
15 | _.extend = uxr.prototype = {
16 | constructor: uxr,
17 |
18 | init: function () {
19 | const _this = this;
20 |
21 | if (typeof this.selector === 'string') {
22 | this.el = document.querySelectorAll(this.selector);
23 | }
24 |
25 | else if (this.selector.length) {
26 | this.el = this.selector;
27 | }
28 |
29 | else {
30 | this.el = [];
31 | }
32 |
33 | this.el = [...this.el];
34 |
35 | for (let i = 0; i < this.el.length; i++) {
36 | _this[i] = this.el[i];
37 | }
38 |
39 | this.length = this.el.length;
40 | }
41 | };
42 |
43 | _.extend.init.prototype = _.extend;
44 | /**
45 | * attr
46 | **/
47 |
48 | /* global toDomString */
49 |
50 | _.extend.attr = function (attr, value) {
51 | if (value) {
52 | return this.setAttribute(attr, value);
53 | }
54 |
55 | return this.getAttr(attr);
56 | };
57 |
58 | _.extend.setAttr = _.extend.setAttribute = function (attr, value) {
59 | this.el.forEach(item => item.setAttribute(toDomString(attr), value));
60 |
61 | return this;
62 | };
63 |
64 | _.extend.getAttr = _.extend.getAttribute = function (attr) {
65 | return this.el[0].getAttribute(toDomString(attr));
66 | };
67 |
68 | _.extend.removeAttr = _.extend.removeAttribute = function (attr) {
69 | this.el.forEach(item => item.removeAttribute(toDomString(attr)));
70 |
71 | return this;
72 | };
73 |
74 | _.extend.src = function (url) {
75 | return this.attr('src', url);
76 | };
77 |
78 | _.extend.href = function (url) {
79 | return this.attr('href', url);
80 | };
81 | /**
82 | * clone
83 | **/
84 |
85 | /* global mutated */
86 |
87 | _.extend.clone = function (deep = false) {
88 | return mutated(this, this.el.map(item => item.cloneNode(deep)));
89 | };
90 | /**
91 | * css-classes
92 | **/
93 |
94 | /* global normalizeClassName */
95 | /* global maybeMultiple */
96 |
97 | const _class = type => {
98 | return function (className) {
99 | this.el.forEach(item => {
100 | if (item.nodeType === 1) {
101 | maybeMultiple(className).map(className => item.classList[type](normalizeClassName(className)));
102 | }
103 | });
104 |
105 | return this;
106 | };
107 | };
108 |
109 | _.extend.addClass = _class('add');
110 |
111 | _.extend.removeClass = _class('remove');
112 |
113 | _.extend.hasClass = function (className) {
114 | return this.el[0].nodeType === 1 && this.filter('.' + normalizeClassName(className)).length > 0;
115 | };
116 |
117 | _.extend.toggleClass = function (className) {
118 | this.el.forEach(item => {
119 | let classNames = maybeMultiple(className);
120 |
121 | if (item.nodeType === 1) {
122 | classNames.forEach(className => item.classList.toggle(normalizeClassName(className)));
123 | }
124 | });
125 |
126 | return this;
127 | };
128 |
129 | /**
130 | * css
131 | **/
132 |
133 | /* global toDomString */
134 | /* global isObject */
135 |
136 | const maybePropIsObject = prop => {
137 | let properties = [];
138 | let values = [];
139 |
140 | if (isObject(prop)) {
141 | Object.keys(prop).forEach(p => {
142 | properties.push(toDomString(p));
143 | values.push(prop[p]);
144 | });
145 | return {properties, values};
146 | }
147 |
148 | return false;
149 | };
150 |
151 | const setProps = (props) => {
152 | if (typeof props === 'string') {
153 | return [toDomString(props)];
154 | }
155 |
156 | if (Array.isArray(props)) {
157 | return props.map(p => toDomString(p));
158 | }
159 | };
160 |
161 | const getStyles = (el, props) => {
162 | let list = {};
163 |
164 | props.forEach(prop => {
165 | list[prop] = el.style[prop];
166 | });
167 |
168 | return props.length === 1 ? list[props[0]] : list;
169 | };
170 |
171 |
172 | _.extend.css = function (prop, value) {
173 | let options = {
174 | properties: [],
175 | values: value ? [value] : []
176 | };
177 |
178 | options.properties = setProps(prop);
179 |
180 | // if the prop is object for set of prop/value pair
181 | options = maybePropIsObject(prop) || options;
182 |
183 | if (options.values.length > 0) {
184 | this.el.map(item => options.properties.forEach((p, i) => item.style[p] = options.values[i]));
185 |
186 | return this;
187 | }
188 |
189 | // if no value set then we are asking for getting the values of properties
190 | // this breaks the chaining
191 | return getStyles(this.el[0], options.properties);
192 | };
193 | /**
194 | * data
195 | **/
196 |
197 | /* global toDomString */
198 |
199 | _.extend.data = function (name, value) {
200 | let domName = toDomString(name);
201 |
202 | if (typeof value !== 'undefined') {
203 | this.el.forEach(item => item.dataset[domName] = value);
204 | return this;
205 | }
206 |
207 | return this.el[0].dataset[domName];
208 | };
209 | /**
210 | * dimensions
211 | **/
212 |
213 | /* global removeUnit */
214 |
215 | const _getContentSize = (el, type) => {
216 | let client = type === 'width' ? 'clientWidth' : 'clientHeight';
217 | let styleFirst = type === 'width' ? 'paddingLeft' : 'paddingTop';
218 | let styleLast = type === 'width' ? 'paddingRight' : 'paddingBottom';
219 |
220 | return el[client] - removeUnit(el.style[styleFirst]) - removeUnit(el.style[styleLast]);
221 | };
222 |
223 | const _contentSize = type => {
224 | return function (newSize) {
225 | if (this.length === 0) {
226 | return false;
227 | }
228 |
229 | if (newSize) {
230 | this.el.forEach(item => item.style[type] = newSize);
231 |
232 | return this;
233 | }
234 |
235 | return _getContentSize(this.el[0], type);
236 | };
237 | };
238 |
239 | const _clientSize = type => {
240 | return function () {
241 | return this.length > 0 ? this.el[0][type] : false;
242 | };
243 | };
244 |
245 | const _getMarginSize = (el, type) => {
246 | let styleFirst = type === 'offsetWidth' ? 'marginLeft' : 'marginTop';
247 | let styleLast = type === 'offsetHeight' ? 'marginRight' : 'marginBottom';
248 |
249 | return removeUnit(el.style[styleFirst]) + removeUnit(el.style[styleLast]);
250 | };
251 |
252 | const _offsetSize = type => {
253 | return function (includeMargins = false) {
254 |
255 | if (this.length === 0) {
256 | return false;
257 | }
258 |
259 | let el = this.el[0];
260 | let sizeType = el[type];
261 |
262 | if (includeMargins) {
263 | sizeType += _getMarginSize(el, type);
264 | }
265 |
266 | return sizeType;
267 | };
268 | };
269 |
270 | _.extend.contentWidth = _.extend.width = _contentSize('width');
271 | _.extend.clientWidth = _.extend.innerWidth = _clientSize('clientWidth');
272 | _.extend.offsetWidth = _.extend.outerWidth = _offsetSize('offsetWidth');
273 |
274 | _.extend.contentHeight = _.extend.height = _contentSize('height');
275 | _.extend.clientHeight = _.extend.innerHeight = _clientSize('clientHeight');
276 | _.extend.offsetHeight = _.extend.outerHeight = _offsetSize('offsetHeight');
277 | /**
278 | * end
279 | **/
280 |
281 | _.extend.end = function () {
282 | return this.prevObj || this;
283 | };
284 | /**
285 | * event
286 | **/
287 |
288 | /* global hashCode */
289 | /* global maybeMultiple */
290 | /* global isObject */
291 |
292 | /*const events = {
293 | animation: ['animationend', 'animationiteration', 'animationstart'],
294 | drag: ['drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop'],
295 | frame: ['abort', 'beforeunload', 'error', 'hashchange', 'load', 'pageshow', 'pagehide', 'resize', 'scroll', 'unload'],
296 | form: ['blur', 'change', 'focus', 'focusin', 'focusout', 'input', 'invalid', 'reset', 'search', 'select', 'submit'],
297 | keyboard: ['keydown', 'keypress', 'keyup'],
298 | media: ['abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'progress', 'ratechange', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting'],
299 | misc: ['message', 'mousewheel', 'online', 'offline', 'popstate', 'show', 'storage', 'toggle', 'wheel'],
300 | mouse: ['click', 'contextmenu', 'dblclick', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseover', 'mouseout', 'mouseup'],
301 | print: ['afterprint', 'beforeprint'],
302 | server: ['error', 'message', 'open'],
303 | transition: ['transitionend'],
304 | touch: ['touchcancel', 'touchend', 'touchmove', 'touchstart']
305 | };
306 |
307 | const allEvents = Object.keys(events).reduce((acc, cur) => acc.concat(events[cur]), []);*/
308 |
309 | const removeEventFromItem = (item, event, fn) => {
310 | item.removeEventListener(event, item.uxrAttachedEvents[event][fn]);
311 | delete item.uxrAttachedEvents[event][fn];
312 |
313 | return item;
314 | };
315 |
316 | _.extend.off = function (eventName, eventHandlerOrSelector, eventHandler) {
317 | let stack = this;
318 | let handler = eventHandlerOrSelector;
319 | let events = maybeMultiple(eventName);
320 |
321 | if (typeof eventHandlerOrSelector === 'string'
322 | || typeof eventHandler !== 'undefined') {
323 | handler = eventHandler;
324 | stack = this.find(eventHandlerOrSelector);
325 | }
326 |
327 | stack.el.forEach(item => {
328 | item.uxrAttachedEvents = item.uxrAttachedEvents || {};
329 |
330 | events.forEach(event => {
331 | // make sure not to give an error, if user tried to remove unattached event
332 | if (typeof item.uxrAttachedEvents[event] !== 'undefined') {
333 | if (typeof handler === 'undefined') {
334 | Object.keys(item.uxrAttachedEvents[event]).forEach(fn => {
335 | removeEventFromItem(item, event, fn);
336 | });
337 | }
338 |
339 | else {
340 | let hash = hashCode((handler).toString());
341 | removeEventFromItem(item, event, hash);
342 | }
343 | }
344 | });
345 | });
346 |
347 | return this;
348 | };
349 |
350 | _.extend.on = function (eventName, eventHandlerOrSelector, eventHandler) {
351 | let stack = this;
352 | let handler = eventHandlerOrSelector;
353 | let events = maybeMultiple(eventName);
354 |
355 | if (typeof eventHandler !== 'undefined') {
356 | handler = eventHandler;
357 | stack = this.find(eventHandlerOrSelector);
358 | }
359 |
360 | let hash = hashCode((handler).toString());
361 |
362 | stack.el.forEach(item => {
363 | item.uxrAttachedEvents = item.uxrAttachedEvents || {};
364 |
365 | events.forEach(event => {
366 | item.uxrAttachedEvents[event] = item.uxrAttachedEvents[event] || {};
367 | item.uxrAttachedEvents[event][hash] = handler;
368 |
369 | item.addEventListener(event, item.uxrAttachedEvents[event][hash]);
370 | });
371 | });
372 |
373 | return this;
374 | };
375 |
376 | _.extend.once = function (eventName, eventHandlerOrSelector, eventHandler) {
377 | let stack = this;
378 | let handler = eventHandlerOrSelector;
379 | let events = maybeMultiple(eventName);
380 |
381 | if (typeof eventHandler !== 'undefined') {
382 | handler = eventHandler;
383 | stack = this.find(eventHandlerOrSelector);
384 | }
385 |
386 | stack.el.forEach(item => {
387 | events.forEach(event => {
388 | /* istanbul ignore next */
389 | let oneHandler = e => {
390 | e.preventDefault();
391 | _(item).off(event, handler);
392 | _(item).off(event, oneHandler);
393 | };
394 |
395 | _(item).on(event, handler);
396 | _(item).on(event, oneHandler);
397 | });
398 | });
399 |
400 | return this;
401 | };
402 |
403 | _.extend.trigger = function (eventName, eventParamsOrSelector, eventParams) {
404 | let stack = this;
405 | let event;
406 |
407 | if (eventParamsOrSelector !== 'undefined' && typeof eventParamsOrSelector === 'string') {
408 | stack = this.find(eventParamsOrSelector);
409 | }
410 |
411 | if (!_eventHasParams(eventParamsOrSelector, eventParams)) {
412 | event = getNativeEvent(eventName);
413 | }
414 |
415 | else {
416 | event = getCustomEvent(eventName, eventParamsOrSelector, eventParams);
417 | }
418 |
419 | stack.el.forEach(item => item.dispatchEvent(event));
420 |
421 | return this;
422 | };
423 |
424 | const _eventHasParams = (eventParamsOrSelector, eventParams) => {
425 | return (typeof eventParamsOrSelector !== 'undefined' && isObject(eventParamsOrSelector)) || typeof eventParams !== 'undefined';
426 | };
427 |
428 | const getNativeEvent = (eventName) => {
429 | return new Event(eventName);
430 | };
431 |
432 | const getCustomEvent = (eventName, eventParamsOrSelector, eventParams) => {
433 | let params = eventParams;
434 |
435 | if (typeof eventParamsOrSelector !== 'undefined'
436 | && isObject(eventParamsOrSelector)) {
437 | params = eventParamsOrSelector;
438 | }
439 |
440 | return new CustomEvent(eventName, {detail: params});
441 | };
442 | /**
443 | * filter
444 | **/
445 |
446 | /* global mutated */
447 |
448 | _.extend.filter = function (selector) {
449 | return mutated(this, this.el.filter(item => item.matches(selector)));
450 | };
451 |
452 | _.extend.find = _.extend.has = function (selector) {
453 | return mutated(this, this.el.map(item => [...item.querySelectorAll(selector)]).reduce((acc, cur) => acc.concat(cur), []));
454 | };
455 |
456 | _.extend.not = function (selector) {
457 | return mutated(this, this.el.filter(item => !item.matches(selector)));
458 | };
459 | /**
460 | * manipulation
461 | **/
462 |
463 | /* global getInsertableElement */
464 | /* global insertBefore */
465 |
466 | const _insert = ({where, parent}) => {
467 | return function(stringOrObject) {
468 | this.el.forEach(
469 | item => insertBefore(stringOrObject, item, where, parent));
470 |
471 | return this;
472 | };
473 | };
474 |
475 | _.extend.empty = function () {
476 | this.el.forEach(item => item.innerHTML = '');
477 |
478 | return this;
479 | };
480 |
481 | _.extend.remove = function () {
482 | this.el.forEach(item => item.parentNode.removeChild(item));
483 |
484 | return this;
485 | };
486 |
487 | _.extend.append = function (stringOrObject) {
488 | this.el.forEach(item => item.appendChild(getInsertableElement(stringOrObject)));
489 |
490 | return this;
491 | };
492 |
493 | _.extend.prepend = _insert({where: 'firstChild', parent: false});
494 |
495 | _.extend.after = _insert({where: 'nextSibling', parent: true});
496 |
497 | _.extend.before = _insert({where: 'self', parent: true});
498 |
499 | _.extend.replaceWith = function (stringOrObject) {
500 | this.el.map(
501 | item => item.parentNode.replaceChild(getInsertableElement(stringOrObject), item)
502 | );
503 |
504 | return this;
505 | };
506 | /**
507 | * prop
508 | **/
509 |
510 | _.extend.prop = function (prop, value) {
511 | if (value) {
512 | return this.setProp(prop, value);
513 | }
514 |
515 | return this.getProp(prop);
516 | };
517 |
518 | _.extend.setProp = function (prop, value) {
519 | this.el.forEach(item => item[prop] = value);
520 |
521 | return this;
522 | };
523 |
524 | _.extend.getProp = function (prop) {
525 | return this[0][prop];
526 | };
527 |
528 | _.extend.text = function (txt) {
529 | return this.prop('innerText', txt);
530 | };
531 |
532 | _.extend.html = function (html) {
533 | return this.prop('innerHTML', html);
534 | };
535 |
536 | _.extend.value = function (value) {
537 | return this.prop('value', value);
538 | };
539 | /**
540 | * ready
541 | **/
542 |
543 | /* istanbul ignore next */
544 | const _stateChange = (condition) => {
545 | return fn => {
546 | return document.addEventListener('readystatechange', e => {
547 | if (e.target.readyState === condition) {
548 | fn();
549 | }
550 | });
551 | };
552 | };
553 |
554 | _.ready = _stateChange('interactive');
555 |
556 | _.load = _stateChange('complete');
557 | /**
558 | * traversing
559 | **/
560 |
561 | /* global mutated */
562 |
563 | const _mapFilterAndMutate = map => {
564 | return function (selector) {
565 | return mutated(
566 | this,
567 | this.el.map(item => item[map]).filter(item => selector ? item.matches(selector) : item));
568 | };
569 | };
570 |
571 | _.extend.closest = function (selector) {
572 | let el = this.el[0];
573 |
574 | if (!selector) {
575 | return mutated(this, [el.parentNode]);
576 | }
577 |
578 | while (el !== null && el.nodeType === 1) {
579 | if (el.matches(selector)) {
580 | return mutated(this, [el]);
581 | }
582 |
583 | el = el.parentNode;
584 | }
585 |
586 | return mutated(this, []);
587 | };
588 |
589 | _.extend.parent = _mapFilterAndMutate('parentNode');
590 |
591 | _.extend.children = function (selector) {
592 | return mutated(
593 | this,
594 | this.el.map(item => Array.from(item.children))
595 | .reduce((acc, cur) => acc.concat(cur), [])
596 | .filter(item => selector ? item.matches(selector) : item));
597 | };
598 |
599 | _.extend.siblings = function (selector) {
600 | return mutated(
601 | this,
602 | this.el.map(item =>
603 | Array.from(item.parentNode.children)
604 | .filter(child => !child.isEqualNode(item)))
605 | .reduce((acc, cur) => acc.concat(cur), [])
606 | .filter(item => selector ? item.matches(selector) : item));
607 | };
608 |
609 | _.extend.next = _mapFilterAndMutate('nextElementSibling');
610 |
611 | _.extend.prev = _mapFilterAndMutate('previousElementSibling');
612 |
613 | _.extend.first = function () {
614 | return mutated(this, this.el.filter((item, index) => index === 0));
615 | };
616 |
617 | _.extend.last = function () {
618 | let last = this.length - 1;
619 | return mutated(this, this.el.filter((item, index) => index === last));
620 | };
621 |
622 | /**
623 | * utils
624 | **/
625 |
626 | Element.prototype.matches = Element.prototype.matches ? Element.prototype.matches : Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
627 |
628 | // eslint-disable-next-line
629 | const normalizeClassName = className => className.charAt(0) === '.' ? className.substr(1) : className;
630 |
631 | // generate hashes for internal usage
632 | // eslint-disable-next-line
633 | const hashCode = s => s.split('').reduce((a, b) => {
634 | a = ((a << 5) - a) + b.charCodeAt(0);
635 | return a & a;
636 | }, 0);
637 |
638 | // trims the string and replaces to multiple spaces in the string with single space
639 | // eslint-disable-next-line
640 | const justifyString = s => s.replace(/\s\s+/g, ' ').trim();
641 |
642 | // split selector
643 | // eslint-disable-next-line
644 | const maybeMultiple = s => typeof s === 'string' ? justifyString(s).split(' ') : s;
645 |
646 | // Dom String Format
647 | // eslint-disable-next-line
648 | const toDomString = s => s.substr(0, 1).toLowerCase() + s.split('-').map(chunk => chunk.charAt(0).toUpperCase() + chunk.slice(1)).join('').substring(1);
649 |
650 | // Element from string
651 | // eslint-disable-next-line
652 | const elementFromString = s => {
653 | if (typeof s === 'string') {
654 | let template = document.createElement('template');
655 | template.innerHTML = s.trim();
656 |
657 | return template.content.firstChild;
658 | }
659 |
660 | return s;
661 | };
662 |
663 | // Insertable Element
664 | // eslint-disable-next-line
665 | const getInsertableElement = s => {
666 | let insertableElement = elementFromString(s);
667 |
668 | if (insertableElement instanceof uxr) {
669 | let template = document.createElement('template');
670 | insertableElement.el.forEach(item => template.content.appendChild(item));
671 | insertableElement = template.content;
672 | }
673 |
674 | return insertableElement;
675 | };
676 |
677 | // InserBefore
678 | // eslint-disable-next-line
679 | const insertBefore = (insert, target, ref, parent) => {
680 | let to = parent === true ? target.parentNode : target;
681 | let where = ref === 'self' ? target : target[ref];
682 |
683 | to.insertBefore(getInsertableElement(insert), where);
684 | };
685 |
686 | // mutatedObj
687 | // eslint-disable-next-line
688 | const mutated = (orgObj, newSet) => {
689 | let obj = _(newSet);
690 |
691 | obj.prevObj = orgObj;
692 |
693 | return obj;
694 | };
695 |
696 | // Is Object => {key: value}
697 | // eslint-disable-next-line
698 | const isObject = objLike => ({}.toString.call(objLike) === '[object Object]');
699 |
700 | // Remove Unit
701 | // eslint-disable-next-line
702 | const removeUnit = number => parseInt(number, 10);
703 | /**
704 | * wrap
705 | **/
706 |
707 | /* global elementFromString */
708 |
709 | const getWrapper = wrapperStr => {
710 | let wrapperString = wrapperStr.toString();
711 |
712 | return wrapperString.charAt(0) !== '<' ?
713 | document.createElement(wrapperString) :
714 | elementFromString(wrapperString);
715 | };
716 |
717 | _.extend.wrap = function (wrapper) {
718 | let newWrap = getWrapper(wrapper);
719 |
720 | let parent = this.el[0].parentNode;
721 | let siblings = this.el[0].nextSibling;
722 |
723 | newWrap.appendChild(this.el[0]);
724 |
725 | if (siblings) {
726 | parent.insertBefore(newWrap, siblings);
727 | }
728 | else {
729 | parent.appendChild(newWrap);
730 | }
731 |
732 | return this;
733 | };
734 |
735 | _.extend.wrapAll = function (wrapper) {
736 | let firstSibling = true;
737 | let newWrap = getWrapper(wrapper);
738 |
739 | this.el.forEach(item => {
740 | if (firstSibling) {
741 | let parent = item.parentNode;
742 | let siblings = item.nextSibling;
743 |
744 | newWrap.appendChild(item);
745 |
746 | if (siblings) {
747 | parent.insertBefore(newWrap, siblings);
748 | }
749 | else {
750 | parent.appendChild(newWrap);
751 | }
752 |
753 | firstSibling = false;
754 | }
755 |
756 | else {
757 | newWrap.appendChild(item);
758 | }
759 | });
760 |
761 | return this;
762 | };
763 |
764 | _.extend.unwrap = function (selector) {
765 | let parent = this.el[0].parentNode;
766 |
767 | // if the parent is not the desired one, skip unwrapping
768 | if (selector && !parent.matches(selector.toString())) {
769 | return this;
770 | }
771 |
772 | parent.parentNode.appendChild(this.el[0]);
773 |
774 | if (parent.children.length === 0) {
775 | parent.parentNode.removeChild(parent);
776 | }
777 |
778 | return this;
779 | };
780 | _.uxr = { version: '0.7.0' };
781 | })();
--------------------------------------------------------------------------------