ad5984f7c67f5be6d5ef787da53162f0763ce612d1f53594eef21c2805fb054f14c259f0b1993b97f4bb518eb4c457bf86b6c592b71bb413c74461772ab29f 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { getZoomFactor } from '../../browser.js';
  6. import * as dom from '../../dom.js';
  7. import { createFastDomNode } from '../../fastDomNode.js';
  8. import { StandardWheelEvent } from '../../mouseEvent.js';
  9. import { HorizontalScrollbar } from './horizontalScrollbar.js';
  10. import { VerticalScrollbar } from './verticalScrollbar.js';
  11. import { Widget } from '../widget.js';
  12. import { TimeoutTimer } from '../../../common/async.js';
  13. import { Emitter } from '../../../common/event.js';
  14. import { dispose } from '../../../common/lifecycle.js';
  15. import * as platform from '../../../common/platform.js';
  16. import { Scrollable } from '../../../common/scrollable.js';
  17. import './media/scrollbars.css';
  18. const HIDE_TIMEOUT = 500;
  19. const SCROLL_WHEEL_SENSITIVITY = 50;
  20. const SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED = true;
  21. class MouseWheelClassifierItem {
  22. constructor(timestamp, deltaX, deltaY) {
  23. this.timestamp = timestamp;
  24. this.deltaX = deltaX;
  25. this.deltaY = deltaY;
  26. this.score = 0;
  27. }
  28. }
  29. export class MouseWheelClassifier {
  30. constructor() {
  31. this._capacity = 5;
  32. this._memory = [];
  33. this._front = -1;
  34. this._rear = -1;
  35. }
  36. isPhysicalMouseWheel() {
  37. if (this._front === -1 && this._rear === -1) {
  38. // no elements
  39. return false;
  40. }
  41. // 0.5 * last + 0.25 * 2nd last + 0.125 * 3rd last + ...
  42. let remainingInfluence = 1;
  43. let score = 0;
  44. let iteration = 1;
  45. let index = this._rear;
  46. do {
  47. const influence = (index === this._front ? remainingInfluence : Math.pow(2, -iteration));
  48. remainingInfluence -= influence;
  49. score += this._memory[index].score * influence;
  50. if (index === this._front) {
  51. break;
  52. }
  53. index = (this._capacity + index - 1) % this._capacity;
  54. iteration++;
  55. } while (true);
  56. return (score <= 0.5);
  57. }
  58. accept(timestamp, deltaX, deltaY) {
  59. const item = new MouseWheelClassifierItem(timestamp, deltaX, deltaY);
  60. item.score = this._computeScore(item);
  61. if (this._front === -1 && this._rear === -1) {
  62. this._memory[0] = item;
  63. this._front = 0;
  64. this._rear = 0;
  65. }
  66. else {
  67. this._rear = (this._rear + 1) % this._capacity;
  68. if (this._rear === this._front) {
  69. // Drop oldest
  70. this._front = (this._front + 1) % this._capacity;
  71. }
  72. this._memory[this._rear] = item;
  73. }
  74. }
  75. /**
  76. * A score between 0 and 1 for `item`.
  77. * - a score towards 0 indicates that the source appears to be a physical mouse wheel
  78. * - a score towards 1 indicates that the source appears to be a touchpad or magic mouse, etc.
  79. */
  80. _computeScore(item) {
  81. if (Math.abs(item.deltaX) > 0 && Math.abs(item.deltaY) > 0) {
  82. // both axes exercised => definitely not a physical mouse wheel
  83. return 1;
  84. }
  85. let score = 0.5;
  86. const prev = (this._front === -1 && this._rear === -1 ? null : this._memory[this._rear]);
  87. if (prev) {
  88. // const deltaT = item.timestamp - prev.timestamp;
  89. // if (deltaT < 1000 / 30) {
  90. // // sooner than X times per second => indicator that this is not a physical mouse wheel
  91. // score += 0.25;
  92. // }
  93. // if (item.deltaX === prev.deltaX && item.deltaY === prev.deltaY) {
  94. // // equal amplitude => indicator that this is a physical mouse wheel
  95. // score -= 0.25;
  96. // }
  97. }
  98. if (!this._isAlmostInt(item.deltaX) || !this._isAlmostInt(item.deltaY)) {
  99. // non-integer deltas => indicator that this is not a physical mouse wheel
  100. score += 0.25;
  101. }
  102. return Math.min(Math.max(score, 0), 1);
  103. }
  104. _isAlmostInt(value) {
  105. const delta = Math.abs(Math.round(value) - value);
  106. return (delta < 0.01);
  107. }
  108. }
  109. MouseWheelClassifier.INSTANCE = new MouseWheelClassifier();
  110. export class AbstractScrollableElement extends Widget {
  111. constructor(element, options, scrollable) {
  112. super();
  113. this._onScroll = this._register(new Emitter());
  114. this.onScroll = this._onScroll.event;
  115. this._onWillScroll = this._register(new Emitter());
  116. element.style.overflow = 'hidden';
  117. this._options = resolveOptions(options);
  118. this._scrollable = scrollable;
  119. this._register(this._scrollable.onScroll((e) => {
  120. this._onWillScroll.fire(e);
  121. this._onDidScroll(e);
  122. this._onScroll.fire(e);
  123. }));
  124. const scrollbarHost = {
  125. onMouseWheel: (mouseWheelEvent) => this._onMouseWheel(mouseWheelEvent),
  126. onDragStart: () => this._onDragStart(),
  127. onDragEnd: () => this._onDragEnd(),
  128. };
  129. this._verticalScrollbar = this._register(new VerticalScrollbar(this._scrollable, this._options, scrollbarHost));
  130. this._horizontalScrollbar = this._register(new HorizontalScrollbar(this._scrollable, this._options, scrollbarHost));
  131. this._domNode = document.createElement('div');
  132. this._domNode.className = 'monaco-scrollable-element ' + this._options.className;
  133. this._domNode.setAttribute('role', 'presentation');
  134. this._domNode.style.position = 'relative';
  135. this._domNode.style.overflow = 'hidden';
  136. this._domNode.appendChild(element);
  137. this._domNode.appendChild(this._horizontalScrollbar.domNode.domNode);
  138. this._domNode.appendChild(this._verticalScrollbar.domNode.domNode);
  139. if (this._options.useShadows) {
  140. this._leftShadowDomNode = createFastDomNode(document.createElement('div'));
  141. this._leftShadowDomNode.setClassName('shadow');
  142. this._domNode.appendChild(this._leftShadowDomNode.domNode);
  143. this._topShadowDomNode = createFastDomNode(document.createElement('div'));
  144. this._topShadowDomNode.setClassName('shadow');
  145. this._domNode.appendChild(this._topShadowDomNode.domNode);
  146. this._topLeftShadowDomNode = createFastDomNode(document.createElement('div'));
  147. this._topLeftShadowDomNode.setClassName('shadow');
  148. this._domNode.appendChild(this._topLeftShadowDomNode.domNode);
  149. }
  150. else {
  151. this._leftShadowDomNode = null;
  152. this._topShadowDomNode = null;
  153. this._topLeftShadowDomNode = null;
  154. }
  155. this._listenOnDomNode = this._options.listenOnDomNode || this._domNode;
  156. this._mouseWheelToDispose = [];
  157. this._setListeningToMouseWheel(this._options.handleMouseWheel);
  158. this.onmouseover(this._listenOnDomNode, (e) => this._onMouseOver(e));
  159. this.onmouseleave(this._listenOnDomNode, (e) => this._onMouseLeave(e));
  160. this._hideTimeout = this._register(new TimeoutTimer());
  161. this._isDragging = false;
  162. this._mouseIsOver = false;
  163. this._shouldRender = true;
  164. this._revealOnScroll = true;
  165. }
  166. get options() {
  167. return this._options;
  168. }
  169. dispose() {
  170. this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);
  171. super.dispose();
  172. }
  173. /**
  174. * Get the generated 'scrollable' dom node
  175. */
  176. getDomNode() {
  177. return this._domNode;
  178. }
  179. getOverviewRulerLayoutInfo() {
  180. return {
  181. parent: this._domNode,
  182. insertBefore: this._verticalScrollbar.domNode.domNode,
  183. };
  184. }
  185. /**
  186. * Delegate a pointer down event to the vertical scrollbar.
  187. * This is to help with clicking somewhere else and having the scrollbar react.
  188. */
  189. delegateVerticalScrollbarPointerDown(browserEvent) {
  190. this._verticalScrollbar.delegatePointerDown(browserEvent);
  191. }
  192. getScrollDimensions() {
  193. return this._scrollable.getScrollDimensions();
  194. }
  195. setScrollDimensions(dimensions) {
  196. this._scrollable.setScrollDimensions(dimensions, false);
  197. }
  198. /**
  199. * Update the class name of the scrollable element.
  200. */
  201. updateClassName(newClassName) {
  202. this._options.className = newClassName;
  203. // Defaults are different on Macs
  204. if (platform.isMacintosh) {
  205. this._options.className += ' mac';
  206. }
  207. this._domNode.className = 'monaco-scrollable-element ' + this._options.className;
  208. }
  209. /**
  210. * Update configuration options for the scrollbar.
  211. */
  212. updateOptions(newOptions) {
  213. if (typeof newOptions.handleMouseWheel !== 'undefined') {
  214. this._options.handleMouseWheel = newOptions.handleMouseWheel;
  215. this._setListeningToMouseWheel(this._options.handleMouseWheel);
  216. }
  217. if (typeof newOptions.mouseWheelScrollSensitivity !== 'undefined') {
  218. this._options.mouseWheelScrollSensitivity = newOptions.mouseWheelScrollSensitivity;
  219. }
  220. if (typeof newOptions.fastScrollSensitivity !== 'undefined') {
  221. this._options.fastScrollSensitivity = newOptions.fastScrollSensitivity;
  222. }
  223. if (typeof newOptions.scrollPredominantAxis !== 'undefined') {
  224. this._options.scrollPredominantAxis = newOptions.scrollPredominantAxis;
  225. }
  226. if (typeof newOptions.horizontal !== 'undefined') {
  227. this._options.horizontal = newOptions.horizontal;
  228. }
  229. if (typeof newOptions.vertical !== 'undefined') {
  230. this._options.vertical = newOptions.vertical;
  231. }
  232. if (typeof newOptions.horizontalScrollbarSize !== 'undefined') {
  233. this._options.horizontalScrollbarSize = newOptions.horizontalScrollbarSize;
  234. }
  235. if (typeof newOptions.verticalScrollbarSize !== 'undefined') {
  236. this._options.verticalScrollbarSize = newOptions.verticalScrollbarSize;
  237. }
  238. if (typeof newOptions.scrollByPage !== 'undefined') {
  239. this._options.scrollByPage = newOptions.scrollByPage;
  240. }
  241. this._horizontalScrollbar.updateOptions(this._options);
  242. this._verticalScrollbar.updateOptions(this._options);
  243. if (!this._options.lazyRender) {
  244. this._render();
  245. }
  246. }
  247. // -------------------- mouse wheel scrolling --------------------
  248. _setListeningToMouseWheel(shouldListen) {
  249. const isListening = (this._mouseWheelToDispose.length > 0);
  250. if (isListening === shouldListen) {
  251. // No change
  252. return;
  253. }
  254. // Stop listening (if necessary)
  255. this._mouseWheelToDispose = dispose(this._mouseWheelToDispose);
  256. // Start listening (if necessary)
  257. if (shouldListen) {
  258. const onMouseWheel = (browserEvent) => {
  259. this._onMouseWheel(new StandardWheelEvent(browserEvent));
  260. };
  261. this._mouseWheelToDispose.push(dom.addDisposableListener(this._listenOnDomNode, dom.EventType.MOUSE_WHEEL, onMouseWheel, { passive: false }));
  262. }
  263. }
  264. _onMouseWheel(e) {
  265. const classifier = MouseWheelClassifier.INSTANCE;
  266. if (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED) {
  267. const osZoomFactor = window.devicePixelRatio / getZoomFactor();
  268. if (platform.isWindows || platform.isLinux) {
  269. // On Windows and Linux, the incoming delta events are multiplied with the OS zoom factor.
  270. // The OS zoom factor can be reverse engineered by using the device pixel ratio and the configured zoom factor into account.
  271. classifier.accept(Date.now(), e.deltaX / osZoomFactor, e.deltaY / osZoomFactor);
  272. }
  273. else {
  274. classifier.accept(Date.now(), e.deltaX, e.deltaY);
  275. }
  276. }
  277. // console.log(`${Date.now()}, ${e.deltaY}, ${e.deltaX}`);
  278. let didScroll = false;
  279. if (e.deltaY || e.deltaX) {
  280. let deltaY = e.deltaY * this._options.mouseWheelScrollSensitivity;
  281. let deltaX = e.deltaX * this._options.mouseWheelScrollSensitivity;
  282. if (this._options.scrollPredominantAxis) {
  283. if (Math.abs(deltaY) >= Math.abs(deltaX)) {
  284. deltaX = 0;
  285. }
  286. else {
  287. deltaY = 0;
  288. }
  289. }
  290. if (this._options.flipAxes) {
  291. [deltaY, deltaX] = [deltaX, deltaY];
  292. }
  293. // Convert vertical scrolling to horizontal if shift is held, this
  294. // is handled at a higher level on Mac
  295. const shiftConvert = !platform.isMacintosh && e.browserEvent && e.browserEvent.shiftKey;
  296. if ((this._options.scrollYToX || shiftConvert) && !deltaX) {
  297. deltaX = deltaY;
  298. deltaY = 0;
  299. }
  300. if (e.browserEvent && e.browserEvent.altKey) {
  301. // fastScrolling
  302. deltaX = deltaX * this._options.fastScrollSensitivity;
  303. deltaY = deltaY * this._options.fastScrollSensitivity;
  304. }
  305. const futureScrollPosition = this._scrollable.getFutureScrollPosition();
  306. let desiredScrollPosition = {};
  307. if (deltaY) {
  308. const deltaScrollTop = SCROLL_WHEEL_SENSITIVITY * deltaY;
  309. // Here we convert values such as -0.3 to -1 or 0.3 to 1, otherwise low speed scrolling will never scroll
  310. const desiredScrollTop = futureScrollPosition.scrollTop - (deltaScrollTop < 0 ? Math.floor(deltaScrollTop) : Math.ceil(deltaScrollTop));
  311. this._verticalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollTop);
  312. }
  313. if (deltaX) {
  314. const deltaScrollLeft = SCROLL_WHEEL_SENSITIVITY * deltaX;
  315. // Here we convert values such as -0.3 to -1 or 0.3 to 1, otherwise low speed scrolling will never scroll
  316. const desiredScrollLeft = futureScrollPosition.scrollLeft - (deltaScrollLeft < 0 ? Math.floor(deltaScrollLeft) : Math.ceil(deltaScrollLeft));
  317. this._horizontalScrollbar.writeScrollPosition(desiredScrollPosition, desiredScrollLeft);
  318. }
  319. // Check that we are scrolling towards a location which is valid
  320. desiredScrollPosition = this._scrollable.validateScrollPosition(desiredScrollPosition);
  321. if (futureScrollPosition.scrollLeft !== desiredScrollPosition.scrollLeft || futureScrollPosition.scrollTop !== desiredScrollPosition.scrollTop) {
  322. const canPerformSmoothScroll = (SCROLL_WHEEL_SMOOTH_SCROLL_ENABLED
  323. && this._options.mouseWheelSmoothScroll
  324. && classifier.isPhysicalMouseWheel());
  325. if (canPerformSmoothScroll) {
  326. this._scrollable.setScrollPositionSmooth(desiredScrollPosition);
  327. }
  328. else {
  329. this._scrollable.setScrollPositionNow(desiredScrollPosition);
  330. }
  331. didScroll = true;
  332. }
  333. }
  334. let consumeMouseWheel = didScroll;
  335. if (!consumeMouseWheel && this._options.alwaysConsumeMouseWheel) {
  336. consumeMouseWheel = true;
  337. }
  338. if (!consumeMouseWheel && this._options.consumeMouseWheelIfScrollbarIsNeeded && (this._verticalScrollbar.isNeeded() || this._horizontalScrollbar.isNeeded())) {
  339. consumeMouseWheel = true;
  340. }
  341. if (consumeMouseWheel) {
  342. e.preventDefault();
  343. e.stopPropagation();
  344. }
  345. }
  346. _onDidScroll(e) {
  347. this._shouldRender = this._horizontalScrollbar.onDidScroll(e) || this._shouldRender;
  348. this._shouldRender = this._verticalScrollbar.onDidScroll(e) || this._shouldRender;
  349. if (this._options.useShadows) {
  350. this._shouldRender = true;
  351. }
  352. if (this._revealOnScroll) {
  353. this._reveal();
  354. }
  355. if (!this._options.lazyRender) {
  356. this._render();
  357. }
  358. }
  359. /**
  360. * Render / mutate the DOM now.
  361. * Should be used together with the ctor option `lazyRender`.
  362. */
  363. renderNow() {
  364. if (!this._options.lazyRender) {
  365. throw new Error('Please use `lazyRender` together with `renderNow`!');
  366. }
  367. this._render();
  368. }
  369. _render() {
  370. if (!this._shouldRender) {
  371. return;
  372. }
  373. this._shouldRender = false;
  374. this._horizontalScrollbar.render();
  375. this._verticalScrollbar.render();
  376. if (this._options.useShadows) {
  377. const scrollState = this._scrollable.getCurrentScrollPosition();
  378. const enableTop = scrollState.scrollTop > 0;
  379. const enableLeft = scrollState.scrollLeft > 0;
  380. const leftClassName = (enableLeft ? ' left' : '');
  381. const topClassName = (enableTop ? ' top' : '');
  382. const topLeftClassName = (enableLeft || enableTop ? ' top-left-corner' : '');
  383. this._leftShadowDomNode.setClassName(`shadow${leftClassName}`);
  384. this._topShadowDomNode.setClassName(`shadow${topClassName}`);
  385. this._topLeftShadowDomNode.setClassName(`shadow${topLeftClassName}${topClassName}${leftClassName}`);
  386. }
  387. }
  388. // -------------------- fade in / fade out --------------------
  389. _onDragStart() {
  390. this._isDragging = true;
  391. this._reveal();
  392. }
  393. _onDragEnd() {
  394. this._isDragging = false;
  395. this._hide();
  396. }
  397. _onMouseLeave(e) {
  398. this._mouseIsOver = false;
  399. this._hide();
  400. }
  401. _onMouseOver(e) {
  402. this._mouseIsOver = true;
  403. this._reveal();
  404. }
  405. _reveal() {
  406. this._verticalScrollbar.beginReveal();
  407. this._horizontalScrollbar.beginReveal();
  408. this._scheduleHide();
  409. }
  410. _hide() {
  411. if (!this._mouseIsOver && !this._isDragging) {
  412. this._verticalScrollbar.beginHide();
  413. this._horizontalScrollbar.beginHide();
  414. }
  415. }
  416. _scheduleHide() {
  417. if (!this._mouseIsOver && !this._isDragging) {
  418. this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);
  419. }
  420. }
  421. }
  422. export class ScrollableElement extends AbstractScrollableElement {
  423. constructor(element, options) {
  424. options = options || {};
  425. options.mouseWheelSmoothScroll = false;
  426. const scrollable = new Scrollable({
  427. forceIntegerValues: true,
  428. smoothScrollDuration: 0,
  429. scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback)
  430. });
  431. super(element, options, scrollable);
  432. this._register(scrollable);
  433. }
  434. setScrollPosition(update) {
  435. this._scrollable.setScrollPositionNow(update);
  436. }
  437. }
  438. export class SmoothScrollableElement extends AbstractScrollableElement {
  439. constructor(element, options, scrollable) {
  440. super(element, options, scrollable);
  441. }
  442. setScrollPosition(update) {
  443. if (update.reuseAnimation) {
  444. this._scrollable.setScrollPositionSmooth(update, update.reuseAnimation);
  445. }
  446. else {
  447. this._scrollable.setScrollPositionNow(update);
  448. }
  449. }
  450. getScrollPosition() {
  451. return this._scrollable.getCurrentScrollPosition();
  452. }
  453. }
  454. export class DomScrollableElement extends AbstractScrollableElement {
  455. constructor(element, options) {
  456. options = options || {};
  457. options.mouseWheelSmoothScroll = false;
  458. const scrollable = new Scrollable({
  459. forceIntegerValues: false,
  460. smoothScrollDuration: 0,
  461. scheduleAtNextAnimationFrame: (callback) => dom.scheduleAtNextAnimationFrame(callback)
  462. });
  463. super(element, options, scrollable);
  464. this._register(scrollable);
  465. this._element = element;
  466. this.onScroll((e) => {
  467. if (e.scrollTopChanged) {
  468. this._element.scrollTop = e.scrollTop;
  469. }
  470. if (e.scrollLeftChanged) {
  471. this._element.scrollLeft = e.scrollLeft;
  472. }
  473. });
  474. this.scanDomNode();
  475. }
  476. setScrollPosition(update) {
  477. this._scrollable.setScrollPositionNow(update);
  478. }
  479. getScrollPosition() {
  480. return this._scrollable.getCurrentScrollPosition();
  481. }
  482. scanDomNode() {
  483. // width, scrollLeft, scrollWidth, height, scrollTop, scrollHeight
  484. this.setScrollDimensions({
  485. width: this._element.clientWidth,
  486. scrollWidth: this._element.scrollWidth,
  487. height: this._element.clientHeight,
  488. scrollHeight: this._element.scrollHeight
  489. });
  490. this.setScrollPosition({
  491. scrollLeft: this._element.scrollLeft,
  492. scrollTop: this._element.scrollTop,
  493. });
  494. }
  495. }
  496. function resolveOptions(opts) {
  497. const result = {
  498. lazyRender: (typeof opts.lazyRender !== 'undefined' ? opts.lazyRender : false),
  499. className: (typeof opts.className !== 'undefined' ? opts.className : ''),
  500. useShadows: (typeof opts.useShadows !== 'undefined' ? opts.useShadows : true),
  501. handleMouseWheel: (typeof opts.handleMouseWheel !== 'undefined' ? opts.handleMouseWheel : true),
  502. flipAxes: (typeof opts.flipAxes !== 'undefined' ? opts.flipAxes : false),
  503. consumeMouseWheelIfScrollbarIsNeeded: (typeof opts.consumeMouseWheelIfScrollbarIsNeeded !== 'undefined' ? opts.consumeMouseWheelIfScrollbarIsNeeded : false),
  504. alwaysConsumeMouseWheel: (typeof opts.alwaysConsumeMouseWheel !== 'undefined' ? opts.alwaysConsumeMouseWheel : false),
  505. scrollYToX: (typeof opts.scrollYToX !== 'undefined' ? opts.scrollYToX : false),
  506. mouseWheelScrollSensitivity: (typeof opts.mouseWheelScrollSensitivity !== 'undefined' ? opts.mouseWheelScrollSensitivity : 1),
  507. fastScrollSensitivity: (typeof opts.fastScrollSensitivity !== 'undefined' ? opts.fastScrollSensitivity : 5),
  508. scrollPredominantAxis: (typeof opts.scrollPredominantAxis !== 'undefined' ? opts.scrollPredominantAxis : true),
  509. mouseWheelSmoothScroll: (typeof opts.mouseWheelSmoothScroll !== 'undefined' ? opts.mouseWheelSmoothScroll : true),
  510. arrowSize: (typeof opts.arrowSize !== 'undefined' ? opts.arrowSize : 11),
  511. listenOnDomNode: (typeof opts.listenOnDomNode !== 'undefined' ? opts.listenOnDomNode : null),
  512. horizontal: (typeof opts.horizontal !== 'undefined' ? opts.horizontal : 1 /* ScrollbarVisibility.Auto */),
  513. horizontalScrollbarSize: (typeof opts.horizontalScrollbarSize !== 'undefined' ? opts.horizontalScrollbarSize : 10),
  514. horizontalSliderSize: (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : 0),
  515. horizontalHasArrows: (typeof opts.horizontalHasArrows !== 'undefined' ? opts.horizontalHasArrows : false),
  516. vertical: (typeof opts.vertical !== 'undefined' ? opts.vertical : 1 /* ScrollbarVisibility.Auto */),
  517. verticalScrollbarSize: (typeof opts.verticalScrollbarSize !== 'undefined' ? opts.verticalScrollbarSize : 10),
  518. verticalHasArrows: (typeof opts.verticalHasArrows !== 'undefined' ? opts.verticalHasArrows : false),
  519. verticalSliderSize: (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : 0),
  520. scrollByPage: (typeof opts.scrollByPage !== 'undefined' ? opts.scrollByPage : false)
  521. };
  522. result.horizontalSliderSize = (typeof opts.horizontalSliderSize !== 'undefined' ? opts.horizontalSliderSize : result.horizontalScrollbarSize);
  523. result.verticalSliderSize = (typeof opts.verticalSliderSize !== 'undefined' ? opts.verticalSliderSize : result.verticalScrollbarSize);
  524. // Defaults are different on Macs
  525. if (platform.isMacintosh) {
  526. result.className += ' mac';
  527. }
  528. return result;
  529. }