UNPKG

18.1 kBJavaScriptView Raw
1import _Promise from 'babel-runtime/core-js/promise';
2import _typeof from 'babel-runtime/helpers/typeof';
3import _Object$keys from 'babel-runtime/core-js/object/keys';
4import _Array$from from 'babel-runtime/core-js/array/from';
5import _toConsumableArray from 'babel-runtime/helpers/toConsumableArray';
6/*
7Copyright 2013-2015 ASIAL CORPORATION
8
9Licensed under the Apache License, Version 2.0 (the "License");
10you may not use this file except in compliance with the License.
11You may obtain a copy of the License at
12
13 http://www.apache.org/licenses/LICENSE-2.0
14
15Unless required by applicable law or agreed to in writing, software
16distributed under the License is distributed on an "AS IS" BASIS,
17WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18See the License for the specific language governing permissions and
19limitations under the License.
20
21*/
22
23import onsElements from './elements';
24import styler from './styler';
25import internal from './internal';
26import autoStyle from './autostyle';
27import ModifierUtil from './internal/modifier-util';
28import animationOptionsParse from './animation-options-parser';
29import platform from './platform';
30
31var util = {};
32var errorPrefix = '[Onsen UI]';
33
34util.globals = {
35 fabOffset: 0,
36 errorPrefix: errorPrefix,
37 supportsPassive: false
38};
39
40platform._runOnActualPlatform(function () {
41 util.globals.actualMobileOS = platform.getMobileOS();
42 util.globals.isUIWebView = platform.isUIWebView();
43 util.globals.isWKWebView = platform.isWKWebView();
44});
45
46try {
47 var opts = Object.defineProperty({}, 'passive', {
48 get: function get() {
49 util.globals.supportsPassive = true;
50 }
51 });
52 window.addEventListener('testPassive', null, opts);
53 window.removeEventListener('testPassive', null, opts);
54} catch (e) {
55 null;
56}
57
58/**
59 * @param {Element} el Target
60 * @param {String} name Event name
61 * @param {Function} handler Event handler
62 * @param {Object} [opt] Event options (passive, capture...)
63 * @param {Boolean} [isGD] If comes from GestureDetector. Just for testing.
64 */
65util.addEventListener = function (el, name, handler, opt, isGD) {
66 el.addEventListener(name, handler, util.globals.supportsPassive ? opt : (opt || {}).capture);
67};
68util.removeEventListener = function (el, name, handler, opt, isGD) {
69 el.removeEventListener(name, handler, util.globals.supportsPassive ? opt : (opt || {}).capture);
70};
71
72/**
73 * @param {String/Function} query dot class name or node name or matcher function.
74 * @return {Function}
75 */
76util.prepareQuery = function (query) {
77 return query instanceof Function ? query : function (element) {
78 return util.match(element, query);
79 };
80};
81
82/**
83 * @param {Element} e
84 * @param {String/Function} s CSS Selector.
85 * @return {Boolean}
86 */
87util.match = function (e, s) {
88 return (e.matches || e.webkitMatchesSelector || e.mozMatchesSelector || e.msMatchesSelector).call(e, s);
89};
90
91/**
92 * @param {Element} element
93 * @param {String/Function} query dot class name or node name or matcher function.
94 * @return {HTMLElement/null}
95 */
96util.findChild = function (element, query) {
97 var match = util.prepareQuery(query);
98
99 // Caution: `element.children` is `undefined` in some environments if `element` is `svg`
100 for (var i = 0; i < element.childNodes.length; i++) {
101 var node = element.childNodes[i];
102 if (node.nodeType !== Node.ELEMENT_NODE) {
103 // process only element nodes
104 continue;
105 }
106 if (match(node)) {
107 return node;
108 }
109 }
110 return null;
111};
112
113/**
114 * @param {Element} element
115 * @param {String/Function} query dot class name or node name or matcher function.
116 * @return {HTMLElement/null}
117 */
118util.findParent = function (element, query, until) {
119 var match = util.prepareQuery(query);
120
121 var parent = element.parentNode;
122 for (;;) {
123 if (!parent || parent === document || parent instanceof DocumentFragment || until && until(parent)) {
124 return null;
125 } else if (match(parent)) {
126 return parent;
127 }
128 parent = parent.parentNode;
129 }
130};
131
132/**
133 * @param {Element} element
134 * @return {boolean}
135 */
136util.isAttached = function (element) {
137 return document.body.contains(element);
138};
139
140/**
141 * @param {Element} element
142 * @return {boolean}
143 */
144util.hasAnyComponentAsParent = function (element) {
145 while (element && document.documentElement !== element) {
146 element = element.parentNode;
147 if (element && element.nodeName.toLowerCase().match(/(ons-navigator|ons-tabbar|ons-modal)/)) {
148 return true;
149 }
150 }
151 return false;
152};
153
154/**
155 * @param {Object} element
156 * @return {Array}
157 */
158util.getAllChildNodes = function (element) {
159 var _ref;
160
161 return (_ref = [element]).concat.apply(_ref, _toConsumableArray(_Array$from(element.children).map(function (childEl) {
162 return util.getAllChildNodes(childEl);
163 })));
164};
165
166/**
167 * @param {Element} element
168 * @return {boolean}
169 */
170util.isPageControl = function (element) {
171 return element.nodeName.match(/^ons-(navigator|splitter|tabbar|page)$/i);
172};
173
174/**
175 * @param {Element} element
176 * @param {String} action to propagate
177 */
178util.propagateAction = function (element, action) {
179 for (var i = 0; i < element.childNodes.length; i++) {
180 var child = element.childNodes[i];
181 if (child[action] instanceof Function) {
182 child[action]();
183 } else {
184 util.propagateAction(child, action);
185 }
186 }
187};
188
189/**
190 * @param {String} string - string to be camelized
191 * @return {String} Camelized string
192 */
193util.camelize = function (string) {
194 return string.toLowerCase().replace(/-([a-z])/g, function (m, l) {
195 return l.toUpperCase();
196 });
197};
198
199/**
200 * @param {String} string - string to be hyphenated
201 * @return {String} Hyphenated string
202 */
203util.hyphenate = function (string) {
204 return string.replace(/([a-zA-Z])([A-Z])/g, '$1-$2').toLowerCase();
205};
206
207/**
208 * @param {String} selector - tag and class only
209 * @param {Object} style
210 * @param {Element}
211 */
212util.create = function () {
213 var selector = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
214 var style = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
215
216 var classList = selector.split('.');
217 var element = document.createElement(classList.shift() || 'div');
218
219 if (classList.length) {
220 element.className = classList.join(' ');
221 }
222
223 styler(element, style);
224
225 return element;
226};
227
228/**
229 * @param {String} html
230 * @return {Element}
231 */
232util.createElement = function (html) {
233 var wrapper = document.createElement('div');
234
235 if (html instanceof DocumentFragment) {
236 wrapper.appendChild(document.importNode(html, true));
237 } else {
238 wrapper.innerHTML = html.trim();
239 }
240
241 if (wrapper.children.length > 1) {
242 util.throw('HTML template must contain a single root element');
243 }
244
245 var element = wrapper.children[0];
246 wrapper.children[0].remove();
247 return element;
248};
249
250/**
251 * @param {String} html
252 * @return {HTMLFragment}
253 */
254util.createFragment = function (html) {
255 var template = document.createElement('template');
256 template.innerHTML = html;
257 return document.importNode(template.content, true);
258};
259
260/*
261 * @param {Object} dst Destination object.
262 * @param {...Object} src Source object(s).
263 * @returns {Object} Reference to `dst`.
264 */
265util.extend = function (dst) {
266 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
267 args[_key - 1] = arguments[_key];
268 }
269
270 for (var i = 0; i < args.length; i++) {
271 if (args[i]) {
272 var keys = _Object$keys(args[i]);
273 for (var j = 0; j < keys.length; j++) {
274 var key = keys[j];
275 dst[key] = args[i][key];
276 }
277 }
278 }
279
280 return dst;
281};
282
283/**
284 * @param {Object} arrayLike
285 * @return {Array}
286 */
287util.arrayFrom = function (arrayLike) {
288 return Array.prototype.slice.apply(arrayLike);
289};
290
291/**
292 * @param {String} jsonString
293 * @param {Object} [failSafe]
294 * @return {Object}
295 */
296util.parseJSONObjectSafely = function (jsonString) {
297 var failSafe = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
298
299 try {
300 var result = JSON.parse('' + jsonString);
301 if ((typeof result === 'undefined' ? 'undefined' : _typeof(result)) === 'object' && result !== null) {
302 return result;
303 }
304 } catch (e) {
305 return failSafe;
306 }
307 return failSafe;
308};
309
310/**
311 * @param {String} path - path such as 'myApp.controllers.data.loadData'
312 * @return {Any} - whatever is located at that path
313 */
314util.findFromPath = function (path) {
315 path = path.split('.');
316 var el = window,
317 key;
318 while (key = path.shift()) {
319 // eslint-disable-line no-cond-assign
320 el = el[key];
321 }
322 return el;
323};
324
325/**
326 * @param {HTMLElement} container - Page or page-container that implements 'topPage'
327 * @return {HTMLElement|null} - Visible page element or null if not found.
328 */
329util.getTopPage = function (container) {
330 return container && (container.tagName.toLowerCase() === 'ons-page' ? container : container.topPage) || null;
331};
332
333/**
334 * @param {HTMLElement} container - Element where the search begins
335 * @return {HTMLElement|null} - Page element that contains the visible toolbar or null.
336 */
337util.findToolbarPage = function (container) {
338 var page = util.getTopPage(container);
339
340 if (page) {
341 if (page._canAnimateToolbar()) {
342 return page;
343 }
344
345 for (var i = 0; i < page._contentElement.children.length; i++) {
346 var nextPage = util.getTopPage(page._contentElement.children[i]);
347 if (nextPage && !/ons-tabbar/i.test(page._contentElement.children[i].tagName)) {
348 return util.findToolbarPage(nextPage);
349 }
350 }
351 }
352
353 return null;
354};
355
356/**
357 * @param {Element} element
358 * @param {String} eventName
359 * @param {Object} [detail]
360 * @return {CustomEvent}
361 */
362util.triggerElementEvent = function (target, eventName) {
363 var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
364
365
366 var event = new CustomEvent(eventName, {
367 bubbles: true,
368 cancelable: true,
369 detail: detail
370 });
371
372 _Object$keys(detail).forEach(function (key) {
373 event[key] = detail[key];
374 });
375
376 target.dispatchEvent(event);
377
378 return event;
379};
380
381/**
382 * @param {Element} target
383 * @param {String} modifierName
384 * @return {Boolean}
385 */
386util.hasModifier = function (target, modifierName) {
387 if (!target.hasAttribute('modifier')) {
388 return false;
389 }
390
391 return RegExp('(^|\\s+)' + modifierName + '($|\\s+)', 'i').test(target.getAttribute('modifier'));
392};
393
394/**
395 * @param {Element} target
396 * @param {String} modifierName
397 * @param {Object} options.autoStyle Maps the modifierName to the corresponding styled modifier.
398 * @param {Object} options.forceAutoStyle Ignores platform limitation.
399 * @return {Boolean} Whether it was added or not.
400 */
401util.addModifier = function (target, modifierName) {
402 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
403
404 if (options.autoStyle) {
405 modifierName = autoStyle.mapModifier(modifierName, target, options.forceAutoStyle);
406 }
407
408 if (util.hasModifier(target, modifierName)) {
409 return false;
410 }
411
412 target.setAttribute('modifier', ((target.getAttribute('modifier') || '') + ' ' + modifierName).trim());
413 return true;
414};
415
416/**
417 * @param {Element} target
418 * @param {String} modifierName
419 * @param {Object} options.autoStyle Maps the modifierName to the corresponding styled modifier.
420 * @param {Object} options.forceAutoStyle Ignores platform limitation.
421 * @return {Boolean} Whether it was found or not.
422 */
423util.removeModifier = function (target, modifierName) {
424 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
425
426 if (options.autoStyle) {
427 modifierName = autoStyle.mapModifier(modifierName, target, options.forceAutoStyle);
428 }
429
430 if (!target.getAttribute('modifier') || !util.hasModifier(target, modifierName)) {
431 return false;
432 }
433
434 var newModifiers = target.getAttribute('modifier').split(/\s+/).filter(function (m) {
435 return m && m !== modifierName;
436 });
437 newModifiers.length ? target.setAttribute('modifier', newModifiers.join(' ')) : target.removeAttribute('modifier');
438 return true;
439};
440
441/**
442 * @param {Element} target
443 * @param {String} modifierName
444 * @param {Boolean} options.force Forces modifier to be added or removed.
445 * @param {Object} options.autoStyle Maps the modifierName to the corresponding styled modifier.
446 * @param {Boolean} options.forceAutoStyle Ignores platform limitation.
447 * @return {Boolean} Whether it was found or not.
448 */
449util.toggleModifier = function () {
450 var options = arguments.length > 2 ? arguments.length <= 2 ? undefined : arguments[2] : {};
451 var force = typeof options === 'boolean' ? options : options.force;
452
453 var toggle = typeof force === 'boolean' ? force : !util.hasModifier.apply(util, arguments);
454 toggle ? util.addModifier.apply(util, arguments) : util.removeModifier.apply(util, arguments);
455};
456
457/**
458 * @param {Element} el
459 * @param {String} defaultClass
460 * @param {Object} scheme
461 */
462util.restoreClass = function (el, defaultClass, scheme) {
463 defaultClass.split(/\s+/).forEach(function (c) {
464 return c !== '' && !el.classList.contains(c) && el.classList.add(c);
465 });
466 el.hasAttribute('modifier') && ModifierUtil.refresh(el, scheme);
467};
468
469// TODO: FIX
470util.updateParentPosition = function (el) {
471 if (!el._parentUpdated && el.parentElement) {
472 if (window.getComputedStyle(el.parentElement).getPropertyValue('position') === 'static') {
473 el.parentElement.style.position = 'relative';
474 }
475 el._parentUpdated = true;
476 }
477};
478
479util.toggleAttribute = function (element, name, value) {
480 if (value) {
481 element.setAttribute(name, typeof value === 'boolean' ? '' : value);
482 } else {
483 element.removeAttribute(name);
484 }
485};
486
487util.bindListeners = function (element, listenerNames) {
488 listenerNames.forEach(function (name) {
489 var boundName = name.replace(/^_[a-z]/, '_bound' + name[1].toUpperCase());
490 element[boundName] = element[boundName] || element[name].bind(element);
491 });
492};
493
494util.each = function (obj, f) {
495 return _Object$keys(obj).forEach(function (key) {
496 return f(key, obj[key]);
497 });
498};
499
500/**
501 * @param {Element} target
502 * @param {boolean} hasRipple
503 * @param {Object} attrs
504 */
505util.updateRipple = function (target, hasRipple) {
506 var attrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
507
508 if (hasRipple === undefined) {
509 hasRipple = target.hasAttribute('ripple');
510 }
511
512 var rippleElement = util.findChild(target, 'ons-ripple');
513
514 if (hasRipple) {
515 if (!rippleElement) {
516 var element = document.createElement('ons-ripple');
517 _Object$keys(attrs).forEach(function (key) {
518 return element.setAttribute(key, attrs[key]);
519 });
520 target.insertBefore(element, target.firstChild);
521 }
522 } else if (rippleElement) {
523 rippleElement.remove();
524 }
525};
526
527/**
528 * @param {String}
529 * @return {Object}
530 */
531util.animationOptionsParse = animationOptionsParse;
532
533/**
534 * @param {*} value
535 */
536util.isInteger = function (value) {
537 return typeof value === 'number' && isFinite(value) && Math.floor(value) === value;
538};
539
540/**
541 * @return {Object} Deferred promise.
542 */
543util.defer = function () {
544 var deferred = {};
545 deferred.promise = new _Promise(function (resolve, reject) {
546 deferred.resolve = resolve;
547 deferred.reject = reject;
548 });
549 return deferred;
550};
551
552/**
553 * Show warnings when they are enabled.
554 *
555 * @param {*} arguments to console.warn
556 */
557util.warn = function () {
558 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
559 args[_key2] = arguments[_key2];
560 }
561
562 if (!internal.config.warningsDisabled) {
563 var _console;
564
565 (_console = console).warn.apply(_console, [errorPrefix].concat(args));
566 }
567};
568
569util.throw = function (message) {
570 throw new Error(errorPrefix + ' ' + message);
571};
572
573util.throwAbstract = function () {
574 return util.throw('Cannot instantiate abstract class');
575};
576util.throwMember = function () {
577 return util.throw('Class member must be implemented');
578};
579util.throwPageLoader = function () {
580 return util.throw('First parameter should be an instance of PageLoader');
581};
582util.throwAnimator = function (el) {
583 return util.throw('"Animator" param must inherit ' + el + 'Animator');
584};
585
586var prevent = function prevent(e) {
587 return e.cancelable && e.preventDefault();
588};
589
590/**
591 * Prevent scrolling while draging horizontally on iOS.
592 *
593 * @param {gd} GestureDetector instance
594 */
595util.iosPreventScroll = function (gd) {
596 if (util.globals.actualMobileOS === 'ios') {
597 var clean = function clean(e) {
598 gd.off('touchmove', prevent);
599 gd.off('dragend', clean);
600 };
601
602 gd.on('touchmove', prevent);
603 gd.on('dragend', clean);
604 }
605};
606
607/**
608 * Prevents scroll in underlying pages on iOS. See #2220 #2274 #1949
609 *
610 * @param {el} HTMLElement that prevents the events
611 * @param {add} Boolean Add or remove event listeners
612 */
613util.iosPageScrollFix = function (add) {
614 // Full fix - May cause issues with UIWebView's momentum scroll
615 if (util.globals.actualMobileOS === 'ios') {
616 document.body.classList.toggle('ons-ios-scroll', add); // Allows custom and localized fixes (#2274)
617 if (!util.globals.isUIWebView || internal.config.forceUIWebViewScrollFix) {
618 document.body.classList.toggle('ons-ios-scroll-fix', add);
619 }
620 }
621};
622util.iosMaskScrollFix = function (el, add) {
623 // Half fix - only prevents scroll on masks
624 if (util.globals.isUIWebView) {
625 var action = (add ? 'add' : 'remove') + 'EventListener';
626 el[action]('touchmove', prevent, false);
627 }
628};
629
630/**
631 * Distance and deltaTime filter some weird dragstart events that are not fired immediately.
632 *
633 * @param {event}
634 */
635util.isValidGesture = function (event) {
636 return event.gesture !== undefined && (event.gesture.distance <= 15 || event.gesture.deltaTime <= 100);
637};
638
639util.checkMissingImport = function () {
640 for (var _len3 = arguments.length, elementNames = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
641 elementNames[_key3] = arguments[_key3];
642 }
643
644 elementNames.forEach(function (name) {
645 if (!onsElements[name]) {
646 util.throw('Ons' + name + ' is required but was not imported (Custom Elements)');
647 }
648 });
649};
650
651export default util;
\No newline at end of file