91cdfe8cbf9819c75605e7af7a8dfe732666c954311f486cce013e8d5f349d6290b6faecce9d51e4e290f76745fbba5aa22e917ed97573924940325033b3dd 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. 'use strict';
  2. exports.__esModule = true;
  3. exports.HTML_CHARACTERS = undefined;
  4. exports.getParent = getParent;
  5. exports.closest = closest;
  6. exports.closestDown = closestDown;
  7. exports.isChildOf = isChildOf;
  8. exports.isChildOfWebComponentTable = isChildOfWebComponentTable;
  9. exports.polymerWrap = polymerWrap;
  10. exports.polymerUnwrap = polymerUnwrap;
  11. exports.index = index;
  12. exports.overlayContainsElement = overlayContainsElement;
  13. exports.hasClass = hasClass;
  14. exports.addClass = addClass;
  15. exports.removeClass = removeClass;
  16. exports.removeTextNodes = removeTextNodes;
  17. exports.empty = empty;
  18. exports.fastInnerHTML = fastInnerHTML;
  19. exports.fastInnerText = fastInnerText;
  20. exports.isVisible = isVisible;
  21. exports.offset = offset;
  22. exports.getWindowScrollTop = getWindowScrollTop;
  23. exports.getWindowScrollLeft = getWindowScrollLeft;
  24. exports.getScrollTop = getScrollTop;
  25. exports.getScrollLeft = getScrollLeft;
  26. exports.getScrollableElement = getScrollableElement;
  27. exports.getTrimmingContainer = getTrimmingContainer;
  28. exports.getStyle = getStyle;
  29. exports.getComputedStyle = getComputedStyle;
  30. exports.outerWidth = outerWidth;
  31. exports.outerHeight = outerHeight;
  32. exports.innerHeight = innerHeight;
  33. exports.innerWidth = innerWidth;
  34. exports.addEvent = addEvent;
  35. exports.removeEvent = removeEvent;
  36. exports.getCaretPosition = getCaretPosition;
  37. exports.getSelectionEndPosition = getSelectionEndPosition;
  38. exports.getSelectionText = getSelectionText;
  39. exports.setCaretPosition = setCaretPosition;
  40. exports.getScrollbarWidth = getScrollbarWidth;
  41. exports.hasVerticalScrollbar = hasVerticalScrollbar;
  42. exports.hasHorizontalScrollbar = hasHorizontalScrollbar;
  43. exports.setOverlayPosition = setOverlayPosition;
  44. exports.getCssTransform = getCssTransform;
  45. exports.resetCssTransform = resetCssTransform;
  46. exports.isInput = isInput;
  47. exports.isOutsideInput = isOutsideInput;
  48. var _browser = require('../browser');
  49. var _feature = require('../feature');
  50. /**
  51. * Get the parent of the specified node in the DOM tree.
  52. *
  53. * @param {HTMLElement} element Element from which traversing is started.
  54. * @param {Number} [level=0] Traversing deep level.
  55. * @return {HTMLElement|null}
  56. */
  57. function getParent(element) {
  58. var level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  59. var iteration = -1;
  60. var parent = null;
  61. while (element != null) {
  62. if (iteration === level) {
  63. parent = element;
  64. break;
  65. }
  66. if (element.host && element.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  67. element = element.host;
  68. } else {
  69. iteration++;
  70. element = element.parentNode;
  71. }
  72. }
  73. return parent;
  74. }
  75. /**
  76. * Goes up the DOM tree (including given element) until it finds an element that matches the nodes or nodes name.
  77. * This method goes up through web components.
  78. *
  79. * @param {HTMLElement} element Element from which traversing is started
  80. * @param {Array} nodes Array of elements or Array of elements name
  81. * @param {HTMLElement} [until]
  82. * @returns {HTMLElement|null}
  83. */
  84. function closest(element, nodes, until) {
  85. while (element != null && element !== until) {
  86. if (element.nodeType === Node.ELEMENT_NODE && (nodes.indexOf(element.nodeName) > -1 || nodes.indexOf(element) > -1)) {
  87. return element;
  88. }
  89. if (element.host && element.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  90. element = element.host;
  91. } else {
  92. element = element.parentNode;
  93. }
  94. }
  95. return null;
  96. }
  97. /**
  98. * Goes "down" the DOM tree (including given element) until it finds an element that matches the nodes or nodes name.
  99. *
  100. * @param {HTMLElement} element Element from which traversing is started
  101. * @param {Array} nodes Array of elements or Array of elements name
  102. * @param {HTMLElement} [until]
  103. * @returns {HTMLElement|null}
  104. */
  105. function closestDown(element, nodes, until) {
  106. var matched = [];
  107. while (element) {
  108. element = closest(element, nodes, until);
  109. if (!element || until && !until.contains(element)) {
  110. break;
  111. }
  112. matched.push(element);
  113. if (element.host && element.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  114. element = element.host;
  115. } else {
  116. element = element.parentNode;
  117. }
  118. }
  119. var length = matched.length;
  120. return length ? matched[length - 1] : null;
  121. }
  122. /**
  123. * Goes up the DOM tree and checks if element is child of another element.
  124. *
  125. * @param child Child element
  126. * @param {Object|String} parent Parent element OR selector of the parent element.
  127. * If string provided, function returns `true` for the first occurrence of element with that class.
  128. * @returns {Boolean}
  129. */
  130. function isChildOf(child, parent) {
  131. var node = child.parentNode;
  132. var queriedParents = [];
  133. if (typeof parent === 'string') {
  134. queriedParents = Array.prototype.slice.call(document.querySelectorAll(parent), 0);
  135. } else {
  136. queriedParents.push(parent);
  137. }
  138. while (node != null) {
  139. if (queriedParents.indexOf(node) > -1) {
  140. return true;
  141. }
  142. node = node.parentNode;
  143. }
  144. return false;
  145. }
  146. /**
  147. * Check if an element is part of `hot-table` web component.
  148. *
  149. * @param {Element} element
  150. * @returns {Boolean}
  151. */
  152. function isChildOfWebComponentTable(element) {
  153. var hotTableName = 'hot-table',
  154. result = false,
  155. parentNode;
  156. parentNode = polymerWrap(element);
  157. function isHotTable(element) {
  158. return element.nodeType === Node.ELEMENT_NODE && element.nodeName === hotTableName.toUpperCase();
  159. }
  160. while (parentNode != null) {
  161. if (isHotTable(parentNode)) {
  162. result = true;
  163. break;
  164. } else if (parentNode.host && parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  165. result = isHotTable(parentNode.host);
  166. if (result) {
  167. break;
  168. }
  169. parentNode = parentNode.host;
  170. }
  171. parentNode = parentNode.parentNode;
  172. }
  173. return result;
  174. }
  175. /**
  176. * Wrap element into polymer/webcomponent container if exists
  177. *
  178. * @param element
  179. * @returns {*}
  180. */
  181. function polymerWrap(element) {
  182. /* global Polymer */
  183. return typeof Polymer !== 'undefined' && typeof wrap === 'function' ? wrap(element) : element;
  184. }
  185. /**
  186. * Unwrap element from polymer/webcomponent container if exists
  187. *
  188. * @param element
  189. * @returns {*}
  190. */
  191. function polymerUnwrap(element) {
  192. /* global Polymer */
  193. return typeof Polymer !== 'undefined' && typeof unwrap === 'function' ? unwrap(element) : element;
  194. }
  195. /**
  196. * Counts index of element within its parent
  197. * WARNING: for performance reasons, assumes there are only element nodes (no text nodes). This is true for Walkotnable
  198. * Otherwise would need to check for nodeType or use previousElementSibling
  199. *
  200. * @see http://jsperf.com/sibling-index/10
  201. * @param {Element} element
  202. * @return {Number}
  203. */
  204. function index(element) {
  205. var i = 0;
  206. if (element.previousSibling) {
  207. /* eslint-disable no-cond-assign */
  208. while (element = element.previousSibling) {
  209. ++i;
  210. }
  211. }
  212. return i;
  213. }
  214. /**
  215. * Check if the provided overlay contains the provided element
  216. *
  217. * @param {String} overlay
  218. * @param {HTMLElement} element
  219. * @returns {boolean}
  220. */
  221. function overlayContainsElement(overlayType, element) {
  222. var overlayElement = document.querySelector('.ht_clone_' + overlayType);
  223. return overlayElement ? overlayElement.contains(element) : null;
  224. }
  225. var classListSupport = !!document.documentElement.classList;
  226. var _hasClass, _addClass, _removeClass;
  227. function filterEmptyClassNames(classNames) {
  228. var len = 0,
  229. result = [];
  230. if (!classNames || !classNames.length) {
  231. return result;
  232. }
  233. while (classNames[len]) {
  234. result.push(classNames[len]);
  235. len++;
  236. }
  237. return result;
  238. }
  239. if (classListSupport) {
  240. var isSupportMultipleClassesArg = function () {
  241. var element = document.createElement('div');
  242. element.classList.add('test', 'test2');
  243. return element.classList.contains('test2');
  244. }();
  245. _hasClass = function _hasClass(element, className) {
  246. if (className === '') {
  247. return false;
  248. }
  249. return element.classList.contains(className);
  250. };
  251. _addClass = function _addClass(element, className) {
  252. var len = 0;
  253. if (typeof className === 'string') {
  254. className = className.split(' ');
  255. }
  256. className = filterEmptyClassNames(className);
  257. if (isSupportMultipleClassesArg) {
  258. element.classList.add.apply(element.classList, className);
  259. } else {
  260. while (className && className[len]) {
  261. element.classList.add(className[len]);
  262. len++;
  263. }
  264. }
  265. };
  266. _removeClass = function _removeClass(element, className) {
  267. var len = 0;
  268. if (typeof className === 'string') {
  269. className = className.split(' ');
  270. }
  271. className = filterEmptyClassNames(className);
  272. if (isSupportMultipleClassesArg) {
  273. element.classList.remove.apply(element.classList, className);
  274. } else {
  275. while (className && className[len]) {
  276. element.classList.remove(className[len]);
  277. len++;
  278. }
  279. }
  280. };
  281. } else {
  282. var createClassNameRegExp = function createClassNameRegExp(className) {
  283. return new RegExp('(\\s|^)' + className + '(\\s|$)');
  284. };
  285. _hasClass = function _hasClass(element, className) {
  286. // http://snipplr.com/view/3561/addclass-removeclass-hasclass/
  287. return !!element.className.match(createClassNameRegExp(className));
  288. };
  289. _addClass = function _addClass(element, className) {
  290. var len = 0,
  291. _className = element.className;
  292. if (typeof className === 'string') {
  293. className = className.split(' ');
  294. }
  295. if (_className === '') {
  296. _className = className.join(' ');
  297. } else {
  298. while (className && className[len]) {
  299. if (!createClassNameRegExp(className[len]).test(_className)) {
  300. _className += ' ' + className[len];
  301. }
  302. len++;
  303. }
  304. }
  305. element.className = _className;
  306. };
  307. _removeClass = function _removeClass(element, className) {
  308. var len = 0,
  309. _className = element.className;
  310. if (typeof className === 'string') {
  311. className = className.split(' ');
  312. }
  313. while (className && className[len]) {
  314. // String.prototype.trim is defined in polyfill.js
  315. _className = _className.replace(createClassNameRegExp(className[len]), ' ').trim();
  316. len++;
  317. }
  318. if (element.className !== _className) {
  319. element.className = _className;
  320. }
  321. };
  322. }
  323. /**
  324. * Checks if element has class name
  325. *
  326. * @param {HTMLElement} element
  327. * @param {String} className Class name to check
  328. * @returns {Boolean}
  329. */
  330. function hasClass(element, className) {
  331. return _hasClass(element, className);
  332. }
  333. /**
  334. * Add class name to an element
  335. *
  336. * @param {HTMLElement} element
  337. * @param {String|Array} className Class name as string or array of strings
  338. */
  339. function addClass(element, className) {
  340. return _addClass(element, className);
  341. }
  342. /**
  343. * Remove class name from an element
  344. *
  345. * @param {HTMLElement} element
  346. * @param {String|Array} className Class name as string or array of strings
  347. */
  348. function removeClass(element, className) {
  349. return _removeClass(element, className);
  350. }
  351. function removeTextNodes(element, parent) {
  352. if (element.nodeType === 3) {
  353. parent.removeChild(element); // bye text nodes!
  354. } else if (['TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR'].indexOf(element.nodeName) > -1) {
  355. var childs = element.childNodes;
  356. for (var i = childs.length - 1; i >= 0; i--) {
  357. removeTextNodes(childs[i], element);
  358. }
  359. }
  360. }
  361. /**
  362. * Remove childs function
  363. * WARNING - this doesn't unload events and data attached by jQuery
  364. * http://jsperf.com/jquery-html-vs-empty-vs-innerhtml/9
  365. * http://jsperf.com/jquery-html-vs-empty-vs-innerhtml/11 - no siginificant improvement with Chrome remove() method
  366. *
  367. * @param element
  368. * @returns {void}
  369. */
  370. //
  371. function empty(element) {
  372. var child;
  373. /* eslint-disable no-cond-assign */
  374. while (child = element.lastChild) {
  375. element.removeChild(child);
  376. }
  377. }
  378. var HTML_CHARACTERS = exports.HTML_CHARACTERS = /(<(.*)>|&(.*);)/;
  379. /**
  380. * Insert content into element trying avoid innerHTML method.
  381. * @return {void}
  382. */
  383. function fastInnerHTML(element, content) {
  384. if (HTML_CHARACTERS.test(content)) {
  385. element.innerHTML = content;
  386. } else {
  387. fastInnerText(element, content);
  388. }
  389. }
  390. /**
  391. * Insert text content into element
  392. * @return {void}
  393. */
  394. var textContextSupport = !!document.createTextNode('test').textContent;
  395. function fastInnerText(element, content) {
  396. var child = element.firstChild;
  397. if (child && child.nodeType === 3 && child.nextSibling === null) {
  398. // fast lane - replace existing text node
  399. if (textContextSupport) {
  400. // http://jsperf.com/replace-text-vs-reuse
  401. child.textContent = content;
  402. } else {
  403. // http://jsperf.com/replace-text-vs-reuse
  404. child.data = content;
  405. }
  406. } else {
  407. // slow lane - empty element and insert a text node
  408. empty(element);
  409. element.appendChild(document.createTextNode(content));
  410. }
  411. }
  412. /**
  413. * Returns true if element is attached to the DOM and visible, false otherwise
  414. * @param elem
  415. * @returns {boolean}
  416. */
  417. function isVisible(elem) {
  418. var next = elem;
  419. while (polymerUnwrap(next) !== document.documentElement) {
  420. // until <html> reached
  421. if (next === null) {
  422. // parent detached from DOM
  423. return false;
  424. } else if (next.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  425. if (next.host) {
  426. // this is Web Components Shadow DOM
  427. // see: http://w3c.github.io/webcomponents/spec/shadow/#encapsulation
  428. // according to spec, should be if (next.ownerDocument !== window.document), but that doesn't work yet
  429. if (next.host.impl) {
  430. // Chrome 33.0.1723.0 canary (2013-11-29) Web Platform features disabled
  431. return isVisible(next.host.impl);
  432. } else if (next.host) {
  433. // Chrome 33.0.1723.0 canary (2013-11-29) Web Platform features enabled
  434. return isVisible(next.host);
  435. }
  436. throw new Error('Lost in Web Components world');
  437. } else {
  438. return false; // this is a node detached from document in IE8
  439. }
  440. } else if (next.style.display === 'none') {
  441. return false;
  442. }
  443. next = next.parentNode;
  444. }
  445. return true;
  446. }
  447. /**
  448. * Returns elements top and left offset relative to the document. Function is not compatible with jQuery offset.
  449. *
  450. * @param {HTMLElement} elem
  451. * @return {Object} Returns object with `top` and `left` props
  452. */
  453. function offset(elem) {
  454. var offsetLeft, offsetTop, lastElem, docElem, box;
  455. docElem = document.documentElement;
  456. if ((0, _feature.hasCaptionProblem)() && elem.firstChild && elem.firstChild.nodeName === 'CAPTION') {
  457. // fixes problem with Firefox ignoring <caption> in TABLE offset (see also export outerHeight)
  458. // http://jsperf.com/offset-vs-getboundingclientrect/8
  459. box = elem.getBoundingClientRect();
  460. return {
  461. top: box.top + (window.pageYOffset || docElem.scrollTop) - (docElem.clientTop || 0),
  462. left: box.left + (window.pageXOffset || docElem.scrollLeft) - (docElem.clientLeft || 0)
  463. };
  464. }
  465. offsetLeft = elem.offsetLeft;
  466. offsetTop = elem.offsetTop;
  467. lastElem = elem;
  468. /* eslint-disable no-cond-assign */
  469. while (elem = elem.offsetParent) {
  470. // from my observation, document.body always has scrollLeft/scrollTop == 0
  471. if (elem === document.body) {
  472. break;
  473. }
  474. offsetLeft += elem.offsetLeft;
  475. offsetTop += elem.offsetTop;
  476. lastElem = elem;
  477. }
  478. // slow - http://jsperf.com/offset-vs-getboundingclientrect/6
  479. if (lastElem && lastElem.style.position === 'fixed') {
  480. // if(lastElem !== document.body) { //faster but does gives false positive in Firefox
  481. offsetLeft += window.pageXOffset || docElem.scrollLeft;
  482. offsetTop += window.pageYOffset || docElem.scrollTop;
  483. }
  484. return {
  485. left: offsetLeft,
  486. top: offsetTop
  487. };
  488. }
  489. /**
  490. * Returns the document's scrollTop property.
  491. *
  492. * @returns {Number}
  493. */
  494. function getWindowScrollTop() {
  495. var res = window.scrollY;
  496. if (res === void 0) {
  497. // IE8-11
  498. res = document.documentElement.scrollTop;
  499. }
  500. return res;
  501. }
  502. /**
  503. * Returns the document's scrollLeft property.
  504. *
  505. * @returns {Number}
  506. */
  507. function getWindowScrollLeft() {
  508. var res = window.scrollX;
  509. if (res === void 0) {
  510. // IE8-11
  511. res = document.documentElement.scrollLeft;
  512. }
  513. return res;
  514. }
  515. /**
  516. * Returns the provided element's scrollTop property.
  517. *
  518. * @param element
  519. * @returns {Number}
  520. */
  521. function getScrollTop(element) {
  522. if (element === window) {
  523. return getWindowScrollTop();
  524. }
  525. return element.scrollTop;
  526. }
  527. /**
  528. * Returns the provided element's scrollLeft property.
  529. *
  530. * @param element
  531. * @returns {Number}
  532. */
  533. function getScrollLeft(element) {
  534. if (element === window) {
  535. return getWindowScrollLeft();
  536. }
  537. return element.scrollLeft;
  538. }
  539. /**
  540. * Returns a DOM element responsible for scrolling of the provided element.
  541. *
  542. * @param {HTMLElement} element
  543. * @returns {HTMLElement} Element's scrollable parent
  544. */
  545. function getScrollableElement(element) {
  546. var el = element.parentNode,
  547. props = ['auto', 'scroll'],
  548. overflow,
  549. overflowX,
  550. overflowY,
  551. computedStyle = '',
  552. computedOverflow = '',
  553. computedOverflowY = '',
  554. computedOverflowX = '';
  555. while (el && el.style && document.body !== el) {
  556. overflow = el.style.overflow;
  557. overflowX = el.style.overflowX;
  558. overflowY = el.style.overflowY;
  559. if (overflow == 'scroll' || overflowX == 'scroll' || overflowY == 'scroll') {
  560. return el;
  561. } else if (window.getComputedStyle) {
  562. computedStyle = window.getComputedStyle(el);
  563. computedOverflow = computedStyle.getPropertyValue('overflow');
  564. computedOverflowY = computedStyle.getPropertyValue('overflow-y');
  565. computedOverflowX = computedStyle.getPropertyValue('overflow-x');
  566. if (computedOverflow === 'scroll' || computedOverflowX === 'scroll' || computedOverflowY === 'scroll') {
  567. return el;
  568. }
  569. }
  570. if (el.clientHeight <= el.scrollHeight && (props.indexOf(overflowY) !== -1 || props.indexOf(overflow) !== -1 || props.indexOf(computedOverflow) !== -1 || props.indexOf(computedOverflowY) !== -1)) {
  571. return el;
  572. }
  573. if (el.clientWidth <= el.scrollWidth && (props.indexOf(overflowX) !== -1 || props.indexOf(overflow) !== -1 || props.indexOf(computedOverflow) !== -1 || props.indexOf(computedOverflowX) !== -1)) {
  574. return el;
  575. }
  576. el = el.parentNode;
  577. }
  578. return window;
  579. }
  580. /**
  581. * Returns a DOM element responsible for trimming the provided element.
  582. *
  583. * @param {HTMLElement} base Base element
  584. * @returns {HTMLElement} Base element's trimming parent
  585. */
  586. function getTrimmingContainer(base) {
  587. var el = base.parentNode;
  588. while (el && el.style && document.body !== el) {
  589. if (el.style.overflow !== 'visible' && el.style.overflow !== '') {
  590. return el;
  591. } else if (window.getComputedStyle) {
  592. var computedStyle = window.getComputedStyle(el);
  593. if (computedStyle.getPropertyValue('overflow') !== 'visible' && computedStyle.getPropertyValue('overflow') !== '') {
  594. return el;
  595. }
  596. }
  597. el = el.parentNode;
  598. }
  599. return window;
  600. }
  601. /**
  602. * Returns a style property for the provided element. (Be it an inline or external style).
  603. *
  604. * @param {HTMLElement} element
  605. * @param {String} prop Wanted property
  606. * @returns {String|undefined} Element's style property
  607. */
  608. function getStyle(element, prop) {
  609. /* eslint-disable */
  610. if (!element) {
  611. return;
  612. } else if (element === window) {
  613. if (prop === 'width') {
  614. return window.innerWidth + 'px';
  615. } else if (prop === 'height') {
  616. return window.innerHeight + 'px';
  617. }
  618. return;
  619. }
  620. var styleProp = element.style[prop],
  621. computedStyle;
  622. if (styleProp !== '' && styleProp !== void 0) {
  623. return styleProp;
  624. } else {
  625. computedStyle = getComputedStyle(element);
  626. if (computedStyle[prop] !== '' && computedStyle[prop] !== void 0) {
  627. return computedStyle[prop];
  628. }
  629. }
  630. }
  631. /**
  632. * Returns a computed style object for the provided element. (Needed if style is declared in external stylesheet).
  633. *
  634. * @param element
  635. * @returns {IEElementStyle|CssStyle} Elements computed style object
  636. */
  637. function getComputedStyle(element) {
  638. return element.currentStyle || document.defaultView.getComputedStyle(element);
  639. }
  640. /**
  641. * Returns the element's outer width.
  642. *
  643. * @param element
  644. * @returns {number} Element's outer width
  645. */
  646. function outerWidth(element) {
  647. return element.offsetWidth;
  648. }
  649. /**
  650. * Returns the element's outer height
  651. *
  652. * @param elem
  653. * @returns {number} Element's outer height
  654. */
  655. function outerHeight(elem) {
  656. if ((0, _feature.hasCaptionProblem)() && elem.firstChild && elem.firstChild.nodeName === 'CAPTION') {
  657. // fixes problem with Firefox ignoring <caption> in TABLE.offsetHeight
  658. // jQuery (1.10.1) still has this unsolved
  659. // may be better to just switch to getBoundingClientRect
  660. // http://bililite.com/blog/2009/03/27/finding-the-size-of-a-table/
  661. // http://lists.w3.org/Archives/Public/www-style/2009Oct/0089.html
  662. // http://bugs.jquery.com/ticket/2196
  663. // http://lists.w3.org/Archives/Public/www-style/2009Oct/0140.html#start140
  664. return elem.offsetHeight + elem.firstChild.offsetHeight;
  665. }
  666. return elem.offsetHeight;
  667. }
  668. /**
  669. * Returns the element's inner height.
  670. *
  671. * @param element
  672. * @returns {number} Element's inner height
  673. */
  674. function innerHeight(element) {
  675. return element.clientHeight || element.innerHeight;
  676. }
  677. /**
  678. * Returns the element's inner width.
  679. *
  680. * @param element
  681. * @returns {number} Element's inner width
  682. */
  683. function innerWidth(element) {
  684. return element.clientWidth || element.innerWidth;
  685. }
  686. function addEvent(element, event, callback) {
  687. if (window.addEventListener) {
  688. element.addEventListener(event, callback, false);
  689. } else {
  690. element.attachEvent('on' + event, callback);
  691. }
  692. }
  693. function removeEvent(element, event, callback) {
  694. if (window.removeEventListener) {
  695. element.removeEventListener(event, callback, false);
  696. } else {
  697. element.detachEvent('on' + event, callback);
  698. }
  699. }
  700. /**
  701. * Returns caret position in text input
  702. *
  703. * @author http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea
  704. * @return {Number}
  705. */
  706. function getCaretPosition(el) {
  707. if (el.selectionStart) {
  708. return el.selectionStart;
  709. } else if (document.selection) {
  710. // IE8
  711. el.focus();
  712. var r = document.selection.createRange();
  713. if (r == null) {
  714. return 0;
  715. }
  716. var re = el.createTextRange();
  717. var rc = re.duplicate();
  718. re.moveToBookmark(r.getBookmark());
  719. rc.setEndPoint('EndToStart', re);
  720. return rc.text.length;
  721. }
  722. return 0;
  723. }
  724. /**
  725. * Returns end of the selection in text input
  726. *
  727. * @return {Number}
  728. */
  729. function getSelectionEndPosition(el) {
  730. if (el.selectionEnd) {
  731. return el.selectionEnd;
  732. } else if (document.selection) {
  733. // IE8
  734. var r = document.selection.createRange();
  735. if (r == null) {
  736. return 0;
  737. }
  738. var re = el.createTextRange();
  739. return re.text.indexOf(r.text) + r.text.length;
  740. }
  741. return 0;
  742. }
  743. /**
  744. * Returns text under selection.
  745. *
  746. * @returns {String}
  747. */
  748. function getSelectionText() {
  749. var text = '';
  750. if (window.getSelection) {
  751. text = window.getSelection().toString();
  752. } else if (document.selection && document.selection.type !== 'Control') {
  753. text = document.selection.createRange().text;
  754. }
  755. return text;
  756. }
  757. /**
  758. * Sets caret position in text input.
  759. *
  760. * @author http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/
  761. * @param {Element} element
  762. * @param {Number} pos
  763. * @param {Number} endPos
  764. */
  765. function setCaretPosition(element, pos, endPos) {
  766. if (endPos === void 0) {
  767. endPos = pos;
  768. }
  769. if (element.setSelectionRange) {
  770. element.focus();
  771. try {
  772. element.setSelectionRange(pos, endPos);
  773. } catch (err) {
  774. var elementParent = element.parentNode;
  775. var parentDisplayValue = elementParent.style.display;
  776. elementParent.style.display = 'block';
  777. element.setSelectionRange(pos, endPos);
  778. elementParent.style.display = parentDisplayValue;
  779. }
  780. } else if (element.createTextRange) {
  781. // IE8
  782. var range = element.createTextRange();
  783. range.collapse(true);
  784. range.moveEnd('character', endPos);
  785. range.moveStart('character', pos);
  786. range.select();
  787. }
  788. }
  789. var cachedScrollbarWidth;
  790. // http://stackoverflow.com/questions/986937/how-can-i-get-the-browsers-scrollbar-sizes
  791. function walkontableCalculateScrollbarWidth() {
  792. var inner = document.createElement('div');
  793. inner.style.height = '200px';
  794. inner.style.width = '100%';
  795. var outer = document.createElement('div');
  796. outer.style.boxSizing = 'content-box';
  797. outer.style.height = '150px';
  798. outer.style.left = '0px';
  799. outer.style.overflow = 'hidden';
  800. outer.style.position = 'absolute';
  801. outer.style.top = '0px';
  802. outer.style.width = '200px';
  803. outer.style.visibility = 'hidden';
  804. outer.appendChild(inner);
  805. (document.body || document.documentElement).appendChild(outer);
  806. var w1 = inner.offsetWidth;
  807. outer.style.overflow = 'scroll';
  808. var w2 = inner.offsetWidth;
  809. if (w1 == w2) {
  810. w2 = outer.clientWidth;
  811. }
  812. (document.body || document.documentElement).removeChild(outer);
  813. return w1 - w2;
  814. }
  815. /**
  816. * Returns the computed width of the native browser scroll bar.
  817. *
  818. * @return {Number} width
  819. */
  820. function getScrollbarWidth() {
  821. if (cachedScrollbarWidth === void 0) {
  822. cachedScrollbarWidth = walkontableCalculateScrollbarWidth();
  823. }
  824. return cachedScrollbarWidth;
  825. }
  826. /**
  827. * Checks if the provided element has a vertical scrollbar.
  828. *
  829. * @param {HTMLElement} element
  830. * @returns {Boolean}
  831. */
  832. function hasVerticalScrollbar(element) {
  833. return element.offsetWidth !== element.clientWidth;
  834. }
  835. /**
  836. * Checks if the provided element has a vertical scrollbar.
  837. *
  838. * @param {HTMLElement} element
  839. * @returns {Boolean}
  840. */
  841. function hasHorizontalScrollbar(element) {
  842. return element.offsetHeight !== element.clientHeight;
  843. }
  844. /**
  845. * Sets overlay position depending on it's type and used browser
  846. */
  847. function setOverlayPosition(overlayElem, left, top) {
  848. if ((0, _browser.isIE8)() || (0, _browser.isIE9)()) {
  849. overlayElem.style.top = top;
  850. overlayElem.style.left = left;
  851. } else if ((0, _browser.isSafari)()) {
  852. overlayElem.style['-webkit-transform'] = 'translate3d(' + left + ',' + top + ',0)';
  853. } else {
  854. overlayElem.style.transform = 'translate3d(' + left + ',' + top + ',0)';
  855. }
  856. }
  857. function getCssTransform(element) {
  858. var transform;
  859. if (element.style.transform && (transform = element.style.transform) !== '') {
  860. return ['transform', transform];
  861. } else if (element.style['-webkit-transform'] && (transform = element.style['-webkit-transform']) !== '') {
  862. return ['-webkit-transform', transform];
  863. }
  864. return -1;
  865. }
  866. function resetCssTransform(element) {
  867. if (element.style.transform && element.style.transform !== '') {
  868. element.style.transform = '';
  869. } else if (element.style['-webkit-transform'] && element.style['-webkit-transform'] !== '') {
  870. element.style['-webkit-transform'] = '';
  871. }
  872. }
  873. /**
  874. * Determines if the given DOM element is an input field.
  875. * Notice: By 'input' we mean input, textarea and select nodes
  876. *
  877. * @param {HTMLElement} element - DOM element
  878. * @returns {Boolean}
  879. */
  880. function isInput(element) {
  881. var inputs = ['INPUT', 'SELECT', 'TEXTAREA'];
  882. return element && (inputs.indexOf(element.nodeName) > -1 || element.contentEditable === 'true');
  883. }
  884. /**
  885. * Determines if the given DOM element is an input field placed OUTSIDE of HOT.
  886. * Notice: By 'input' we mean input, textarea and select nodes
  887. *
  888. * @param {HTMLElement} element - DOM element
  889. * @returns {Boolean}
  890. */
  891. function isOutsideInput(element) {
  892. return isInput(element) && element.className.indexOf('handsontableInput') == -1 && element.className.indexOf('copyPaste') == -1;
  893. }