You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

874 lines
27 KiB

9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
10 years ago
9 years ago
  1. /*!
  2. * # Semantic UI 2.0.0 - Sticky
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2015 Contributors
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. "use strict";
  13. $.fn.sticky = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. returnedValue
  23. ;
  24. $allModules
  25. .each(function() {
  26. var
  27. settings = ( $.isPlainObject(parameters) )
  28. ? $.extend(true, {}, $.fn.sticky.settings, parameters)
  29. : $.extend({}, $.fn.sticky.settings),
  30. className = settings.className,
  31. namespace = settings.namespace,
  32. error = settings.error,
  33. eventNamespace = '.' + namespace,
  34. moduleNamespace = 'module-' + namespace,
  35. $module = $(this),
  36. $window = $(window),
  37. $scroll = $(settings.scrollContext),
  38. $container,
  39. $context,
  40. selector = $module.selector || '',
  41. instance = $module.data(moduleNamespace),
  42. requestAnimationFrame = window.requestAnimationFrame
  43. || window.mozRequestAnimationFrame
  44. || window.webkitRequestAnimationFrame
  45. || window.msRequestAnimationFrame
  46. || function(callback) { setTimeout(callback, 0); },
  47. element = this,
  48. observer,
  49. module
  50. ;
  51. module = {
  52. initialize: function() {
  53. module.determineContainer();
  54. module.determineContext();
  55. module.verbose('Initializing sticky', settings, $container);
  56. module.save.positions();
  57. module.checkErrors();
  58. module.bind.events();
  59. if(settings.observeChanges) {
  60. module.observeChanges();
  61. }
  62. module.instantiate();
  63. },
  64. instantiate: function() {
  65. module.verbose('Storing instance of module', module);
  66. instance = module;
  67. $module
  68. .data(moduleNamespace, module)
  69. ;
  70. },
  71. destroy: function() {
  72. module.verbose('Destroying previous instance');
  73. module.reset();
  74. if(observer) {
  75. observer.disconnect();
  76. }
  77. $window
  78. .off('load' + eventNamespace, module.event.load)
  79. .off('resize' + eventNamespace, module.event.resize)
  80. ;
  81. $scroll
  82. .off('scrollchange' + eventNamespace, module.event.scrollchange)
  83. ;
  84. $module.removeData(moduleNamespace);
  85. },
  86. observeChanges: function() {
  87. var
  88. context = $context[0]
  89. ;
  90. if('MutationObserver' in window) {
  91. observer = new MutationObserver(function(mutations) {
  92. clearTimeout(module.timer);
  93. module.timer = setTimeout(function() {
  94. module.verbose('DOM tree modified, updating sticky menu', mutations);
  95. module.refresh();
  96. }, 100);
  97. });
  98. observer.observe(element, {
  99. childList : true,
  100. subtree : true
  101. });
  102. observer.observe(context, {
  103. childList : true,
  104. subtree : true
  105. });
  106. module.debug('Setting up mutation observer', observer);
  107. }
  108. },
  109. determineContainer: function() {
  110. $container = $module.offsetParent();
  111. },
  112. determineContext: function() {
  113. if(settings.context) {
  114. $context = $(settings.context);
  115. }
  116. else {
  117. $context = $container;
  118. }
  119. if($context.length === 0) {
  120. module.error(error.invalidContext, settings.context, $module);
  121. return;
  122. }
  123. },
  124. checkErrors: function() {
  125. if( module.is.hidden() ) {
  126. module.error(error.visible, $module);
  127. }
  128. if(module.cache.element.height > module.cache.context.height) {
  129. module.reset();
  130. module.error(error.elementSize, $module);
  131. return;
  132. }
  133. },
  134. bind: {
  135. events: function() {
  136. $window
  137. .on('load' + eventNamespace, module.event.load)
  138. .on('resize' + eventNamespace, module.event.resize)
  139. ;
  140. // pub/sub pattern
  141. $scroll
  142. .off('scroll' + eventNamespace)
  143. .on('scroll' + eventNamespace, module.event.scroll)
  144. .on('scrollchange' + eventNamespace, module.event.scrollchange)
  145. ;
  146. }
  147. },
  148. event: {
  149. load: function() {
  150. module.verbose('Page contents finished loading');
  151. requestAnimationFrame(module.refresh);
  152. },
  153. resize: function() {
  154. module.verbose('Window resized');
  155. requestAnimationFrame(module.refresh);
  156. },
  157. scroll: function() {
  158. requestAnimationFrame(function() {
  159. $scroll.triggerHandler('scrollchange' + eventNamespace, $scroll.scrollTop() );
  160. });
  161. },
  162. scrollchange: function(event, scrollPosition) {
  163. module.stick(scrollPosition);
  164. settings.onScroll.call(element);
  165. }
  166. },
  167. refresh: function(hardRefresh) {
  168. module.reset();
  169. if(!settings.context) {
  170. module.determineContext();
  171. }
  172. if(hardRefresh) {
  173. module.determineContainer();
  174. }
  175. module.save.positions();
  176. module.stick();
  177. settings.onReposition.call(element);
  178. },
  179. supports: {
  180. sticky: function() {
  181. var
  182. $element = $('<div/>'),
  183. element = $element[0]
  184. ;
  185. $element.addClass(className.supported);
  186. return($element.css('position').match('sticky'));
  187. }
  188. },
  189. save: {
  190. lastScroll: function(scroll) {
  191. module.lastScroll = scroll;
  192. },
  193. elementScroll: function(scroll) {
  194. module.elementScroll = scroll;
  195. },
  196. positions: function() {
  197. var
  198. window = {
  199. height: $window.height()
  200. },
  201. element = {
  202. margin: {
  203. top : parseInt($module.css('margin-top'), 10),
  204. bottom : parseInt($module.css('margin-bottom'), 10),
  205. },
  206. offset : $module.offset(),
  207. width : $module.outerWidth(),
  208. height : $module.outerHeight()
  209. },
  210. context = {
  211. offset : $context.offset(),
  212. height : $context.outerHeight(),
  213. bottomPadding : parseInt($context.css('padding-bottom'), 10)
  214. },
  215. container = {
  216. height: $container.outerHeight()
  217. }
  218. ;
  219. module.cache = {
  220. fits : ( element.height < window.height ),
  221. window: {
  222. height: window.height
  223. },
  224. element: {
  225. margin : element.margin,
  226. top : element.offset.top - element.margin.top,
  227. left : element.offset.left,
  228. width : element.width,
  229. height : element.height,
  230. bottom : element.offset.top + element.height
  231. },
  232. context: {
  233. top : context.offset.top,
  234. height : context.height,
  235. bottomPadding : context.bottomPadding,
  236. bottom : context.offset.top + context.height - context.bottomPadding
  237. }
  238. };
  239. module.set.containerSize();
  240. module.set.size();
  241. module.stick();
  242. module.debug('Caching element positions', module.cache);
  243. }
  244. },
  245. get: {
  246. direction: function(scroll) {
  247. var
  248. direction = 'down'
  249. ;
  250. scroll = scroll || $scroll.scrollTop();
  251. if(module.lastScroll !== undefined) {
  252. if(module.lastScroll < scroll) {
  253. direction = 'down';
  254. }
  255. else if(module.lastScroll > scroll) {
  256. direction = 'up';
  257. }
  258. }
  259. return direction;
  260. },
  261. scrollChange: function(scroll) {
  262. scroll = scroll || $scroll.scrollTop();
  263. return (module.lastScroll)
  264. ? (scroll - module.lastScroll)
  265. : 0
  266. ;
  267. },
  268. currentElementScroll: function() {
  269. if(module.elementScroll) {
  270. return module.elementScroll;
  271. }
  272. return ( module.is.top() )
  273. ? Math.abs(parseInt($module.css('top'), 10)) || 0
  274. : Math.abs(parseInt($module.css('bottom'), 10)) || 0
  275. ;
  276. },
  277. elementScroll: function(scroll) {
  278. scroll = scroll || $scroll.scrollTop();
  279. var
  280. element = module.cache.element,
  281. window = module.cache.window,
  282. delta = module.get.scrollChange(scroll),
  283. maxScroll = (element.height - window.height + settings.offset),
  284. elementScroll = module.get.currentElementScroll(),
  285. possibleScroll = (elementScroll + delta)
  286. ;
  287. if(module.cache.fits || possibleScroll < 0) {
  288. elementScroll = 0;
  289. }
  290. else if(possibleScroll > maxScroll ) {
  291. elementScroll = maxScroll;
  292. }
  293. else {
  294. elementScroll = possibleScroll;
  295. }
  296. return elementScroll;
  297. }
  298. },
  299. remove: {
  300. offset: function() {
  301. $module.css('margin-top', '');
  302. }
  303. },
  304. set: {
  305. offset: function() {
  306. module.verbose('Setting offset on element', settings.offset);
  307. $module
  308. .css('margin-top', settings.offset)
  309. ;
  310. },
  311. containerSize: function() {
  312. var
  313. tagName = $container.get(0).tagName
  314. ;
  315. if(tagName === 'HTML' || tagName == 'body') {
  316. // this can trigger for too many reasons
  317. //module.error(error.container, tagName, $module);
  318. module.determineContainer();
  319. }
  320. else {
  321. if( Math.abs($container.outerHeight() - module.cache.context.height) > settings.jitter) {
  322. module.debug('Context has padding, specifying exact height for container', module.cache.context.height);
  323. $container.css({
  324. height: module.cache.context.height
  325. });
  326. }
  327. }
  328. },
  329. minimumSize: function() {
  330. var
  331. element = module.cache.element
  332. ;
  333. $container
  334. .css('min-height', element.height)
  335. ;
  336. },
  337. scroll: function(scroll) {
  338. module.debug('Setting scroll on element', scroll);
  339. if(module.elementScroll == scroll) {
  340. return;
  341. }
  342. if( module.is.top() ) {
  343. $module
  344. .css('bottom', '')
  345. .css('top', -scroll)
  346. ;
  347. }
  348. if( module.is.bottom() ) {
  349. $module
  350. .css('top', '')
  351. .css('bottom', scroll)
  352. ;
  353. }
  354. },
  355. size: function() {
  356. if(module.cache.element.height !== 0 && module.cache.element.width !== 0) {
  357. $module
  358. .css({
  359. width : module.cache.element.width,
  360. height : module.cache.element.height
  361. })
  362. ;
  363. }
  364. }
  365. },
  366. is: {
  367. top: function() {
  368. return $module.hasClass(className.top);
  369. },
  370. bottom: function() {
  371. return $module.hasClass(className.bottom);
  372. },
  373. initialPosition: function() {
  374. return (!module.is.fixed() && !module.is.bound());
  375. },
  376. hidden: function() {
  377. return (!$module.is(':visible'));
  378. },
  379. bound: function() {
  380. return $module.hasClass(className.bound);
  381. },
  382. fixed: function() {
  383. return $module.hasClass(className.fixed);
  384. }
  385. },
  386. stick: function(scroll) {
  387. var
  388. cachedPosition = scroll || $scroll.scrollTop(),
  389. cache = module.cache,
  390. fits = cache.fits,
  391. element = cache.element,
  392. window = cache.window,
  393. context = cache.context,
  394. offset = (module.is.bottom() && settings.pushing)
  395. ? settings.bottomOffset
  396. : settings.offset,
  397. scroll = {
  398. top : cachedPosition + offset,
  399. bottom : cachedPosition + offset + window.height
  400. },
  401. direction = module.get.direction(scroll.top),
  402. elementScroll = (fits)
  403. ? 0
  404. : module.get.elementScroll(scroll.top),
  405. // shorthand
  406. doesntFit = !fits,
  407. elementVisible = (element.height !== 0)
  408. ;
  409. if(elementVisible) {
  410. if( module.is.initialPosition() ) {
  411. if(scroll.top > context.bottom) {
  412. module.debug('Element bottom of container');
  413. module.bindBottom();
  414. }
  415. else if(scroll.top > element.top) {
  416. module.debug('Element passed, fixing element to page');
  417. module.fixTop();
  418. }
  419. }
  420. else if( module.is.fixed() ) {
  421. // currently fixed top
  422. if( module.is.top() ) {
  423. if( scroll.top < element.top ) {
  424. module.debug('Fixed element reached top of container');
  425. module.setInitialPosition();
  426. }
  427. else if( (element.height + scroll.top - elementScroll) > context.bottom ) {
  428. module.debug('Fixed element reached bottom of container');
  429. module.bindBottom();
  430. }
  431. // scroll element if larger than screen
  432. else if(doesntFit) {
  433. module.set.scroll(elementScroll);
  434. }
  435. }
  436. // currently fixed bottom
  437. else if(module.is.bottom() ) {
  438. // top edge
  439. if( (scroll.bottom - element.height) < element.top) {
  440. module.debug('Bottom fixed rail has reached top of container');
  441. module.setInitialPosition();
  442. }
  443. // bottom edge
  444. else if(scroll.bottom > context.bottom) {
  445. module.debug('Bottom fixed rail has reached bottom of container');
  446. module.bindBottom();
  447. }
  448. // scroll element if larger than screen
  449. else if(doesntFit) {
  450. module.set.scroll(elementScroll);
  451. }
  452. }
  453. }
  454. else if( module.is.bottom() ) {
  455. if(settings.pushing) {
  456. if(module.is.bound() && scroll.bottom < context.bottom ) {
  457. module.debug('Fixing bottom attached element to bottom of browser.');
  458. module.fixBottom();
  459. }
  460. }
  461. else {
  462. if(module.is.bound() && (scroll.top < context.bottom - element.height) ) {
  463. module.debug('Fixing bottom attached element to top of browser.');
  464. module.fixTop();
  465. }
  466. }
  467. }
  468. }
  469. // save current scroll for next run
  470. module.save.lastScroll(scroll.top);
  471. module.save.elementScroll(elementScroll);
  472. },
  473. bindTop: function() {
  474. module.debug('Binding element to top of parent container');
  475. module.remove.offset();
  476. $module
  477. .css({
  478. left : '',
  479. top : '',
  480. marginBottom : ''
  481. })
  482. .removeClass(className.fixed)
  483. .removeClass(className.bottom)
  484. .addClass(className.bound)
  485. .addClass(className.top)
  486. ;
  487. settings.onTop.call(element);
  488. settings.onUnstick.call(element);
  489. },
  490. bindBottom: function() {
  491. module.debug('Binding element to bottom of parent container');
  492. module.remove.offset();
  493. $module
  494. .css({
  495. left : '',
  496. top : '',
  497. marginBottom : module.cache.context.bottomPadding
  498. })
  499. .removeClass(className.fixed)
  500. .removeClass(className.top)
  501. .addClass(className.bound)
  502. .addClass(className.bottom)
  503. ;
  504. settings.onBottom.call(element);
  505. settings.onUnstick.call(element);
  506. },
  507. setInitialPosition: function() {
  508. module.unfix();
  509. module.unbind();
  510. },
  511. fixTop: function() {
  512. module.debug('Fixing element to top of page');
  513. module.set.minimumSize();
  514. module.set.offset();
  515. $module
  516. .css({
  517. left : module.cache.element.left,
  518. bottom : '',
  519. marginBottom : ''
  520. })
  521. .removeClass(className.bound)
  522. .removeClass(className.bottom)
  523. .addClass(className.fixed)
  524. .addClass(className.top)
  525. ;
  526. settings.onStick.call(element);
  527. },
  528. fixBottom: function() {
  529. module.debug('Sticking element to bottom of page');
  530. module.set.minimumSize();
  531. module.set.offset();
  532. $module
  533. .css({
  534. left : module.cache.element.left,
  535. bottom : '',
  536. marginBottom : ''
  537. })
  538. .removeClass(className.bound)
  539. .removeClass(className.top)
  540. .addClass(className.fixed)
  541. .addClass(className.bottom)
  542. ;
  543. settings.onStick.call(element);
  544. },
  545. unbind: function() {
  546. module.debug('Removing absolute position on element');
  547. module.remove.offset();
  548. $module
  549. .removeClass(className.bound)
  550. .removeClass(className.top)
  551. .removeClass(className.bottom)
  552. ;
  553. },
  554. unfix: function() {
  555. module.debug('Removing fixed position on element');
  556. module.remove.offset();
  557. $module
  558. .removeClass(className.fixed)
  559. .removeClass(className.top)
  560. .removeClass(className.bottom)
  561. ;
  562. settings.onUnstick.call(element);
  563. },
  564. reset: function() {
  565. module.debug('Reseting elements position');
  566. module.unbind();
  567. module.unfix();
  568. module.resetCSS();
  569. module.remove.offset();
  570. },
  571. resetCSS: function() {
  572. $module
  573. .css({
  574. width : '',
  575. height : ''
  576. })
  577. ;
  578. $container
  579. .css({
  580. height: ''
  581. })
  582. ;
  583. },
  584. setting: function(name, value) {
  585. if( $.isPlainObject(name) ) {
  586. $.extend(true, settings, name);
  587. }
  588. else if(value !== undefined) {
  589. settings[name] = value;
  590. }
  591. else {
  592. return settings[name];
  593. }
  594. },
  595. internal: function(name, value) {
  596. if( $.isPlainObject(name) ) {
  597. $.extend(true, module, name);
  598. }
  599. else if(value !== undefined) {
  600. module[name] = value;
  601. }
  602. else {
  603. return module[name];
  604. }
  605. },
  606. debug: function() {
  607. if(settings.debug) {
  608. if(settings.performance) {
  609. module.performance.log(arguments);
  610. }
  611. else {
  612. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  613. module.debug.apply(console, arguments);
  614. }
  615. }
  616. },
  617. verbose: function() {
  618. if(settings.verbose && settings.debug) {
  619. if(settings.performance) {
  620. module.performance.log(arguments);
  621. }
  622. else {
  623. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  624. module.verbose.apply(console, arguments);
  625. }
  626. }
  627. },
  628. error: function() {
  629. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  630. module.error.apply(console, arguments);
  631. },
  632. performance: {
  633. log: function(message) {
  634. var
  635. currentTime,
  636. executionTime,
  637. previousTime
  638. ;
  639. if(settings.performance) {
  640. currentTime = new Date().getTime();
  641. previousTime = time || currentTime;
  642. executionTime = currentTime - previousTime;
  643. time = currentTime;
  644. performance.push({
  645. 'Name' : message[0],
  646. 'Arguments' : [].slice.call(message, 1) || '',
  647. 'Element' : element,
  648. 'Execution Time' : executionTime
  649. });
  650. }
  651. clearTimeout(module.performance.timer);
  652. module.performance.timer = setTimeout(module.performance.display, 0);
  653. },
  654. display: function() {
  655. var
  656. title = settings.name + ':',
  657. totalTime = 0
  658. ;
  659. time = false;
  660. clearTimeout(module.performance.timer);
  661. $.each(performance, function(index, data) {
  662. totalTime += data['Execution Time'];
  663. });
  664. title += ' ' + totalTime + 'ms';
  665. if(moduleSelector) {
  666. title += ' \'' + moduleSelector + '\'';
  667. }
  668. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  669. console.groupCollapsed(title);
  670. if(console.table) {
  671. console.table(performance);
  672. }
  673. else {
  674. $.each(performance, function(index, data) {
  675. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  676. });
  677. }
  678. console.groupEnd();
  679. }
  680. performance = [];
  681. }
  682. },
  683. invoke: function(query, passedArguments, context) {
  684. var
  685. object = instance,
  686. maxDepth,
  687. found,
  688. response
  689. ;
  690. passedArguments = passedArguments || queryArguments;
  691. context = element || context;
  692. if(typeof query == 'string' && object !== undefined) {
  693. query = query.split(/[\. ]/);
  694. maxDepth = query.length - 1;
  695. $.each(query, function(depth, value) {
  696. var camelCaseValue = (depth != maxDepth)
  697. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  698. : query
  699. ;
  700. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  701. object = object[camelCaseValue];
  702. }
  703. else if( object[camelCaseValue] !== undefined ) {
  704. found = object[camelCaseValue];
  705. return false;
  706. }
  707. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  708. object = object[value];
  709. }
  710. else if( object[value] !== undefined ) {
  711. found = object[value];
  712. return false;
  713. }
  714. else {
  715. return false;
  716. }
  717. });
  718. }
  719. if ( $.isFunction( found ) ) {
  720. response = found.apply(context, passedArguments);
  721. }
  722. else if(found !== undefined) {
  723. response = found;
  724. }
  725. if($.isArray(returnedValue)) {
  726. returnedValue.push(response);
  727. }
  728. else if(returnedValue !== undefined) {
  729. returnedValue = [returnedValue, response];
  730. }
  731. else if(response !== undefined) {
  732. returnedValue = response;
  733. }
  734. return found;
  735. }
  736. };
  737. if(methodInvoked) {
  738. if(instance === undefined) {
  739. module.initialize();
  740. }
  741. module.invoke(query);
  742. }
  743. else {
  744. if(instance !== undefined) {
  745. instance.invoke('destroy');
  746. }
  747. module.initialize();
  748. }
  749. })
  750. ;
  751. return (returnedValue !== undefined)
  752. ? returnedValue
  753. : this
  754. ;
  755. };
  756. $.fn.sticky.settings = {
  757. name : 'Sticky',
  758. namespace : 'sticky',
  759. debug : false,
  760. verbose : true,
  761. performance : true,
  762. // whether to stick in the opposite direction on scroll up
  763. pushing : false,
  764. context : false,
  765. // Context to watch scroll events
  766. scrollContext : window,
  767. // Offset to adjust scroll
  768. offset : 0,
  769. // Offset to adjust scroll when attached to bottom of screen
  770. bottomOffset : 0,
  771. jitter : 5, // will only set container height if difference between context and container is larger than this number
  772. // Whether to automatically observe changes with Mutation Observers
  773. observeChanges : false,
  774. // Called when position is recalculated
  775. onReposition : function(){},
  776. // Called on each scroll
  777. onScroll : function(){},
  778. // Called when element is stuck to viewport
  779. onStick : function(){},
  780. // Called when element is unstuck from viewport
  781. onUnstick : function(){},
  782. // Called when element reaches top of context
  783. onTop : function(){},
  784. // Called when element reaches bottom of context
  785. onBottom : function(){},
  786. error : {
  787. container : 'Sticky element must be inside a relative container',
  788. visible : 'Element is hidden, you must call refresh after element becomes visible',
  789. method : 'The method you called is not defined.',
  790. invalidContext : 'Context specified does not exist',
  791. elementSize : 'Sticky element is larger than its container, cannot create sticky.'
  792. },
  793. className : {
  794. bound : 'bound',
  795. fixed : 'fixed',
  796. supported : 'native',
  797. top : 'top',
  798. bottom : 'bottom'
  799. }
  800. };
  801. })( jQuery, window , document );