Нема описа

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