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.

1096 lines
35 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*!
  2. * # Semantic UI 2.0.0 - Visibility
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 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.visibility = 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.visibility.settings, parameters)
  29. : $.extend({}, $.fn.visibility.settings),
  30. className = settings.className,
  31. namespace = settings.namespace,
  32. error = settings.error,
  33. eventNamespace = '.' + namespace,
  34. moduleNamespace = 'module-' + namespace,
  35. $window = $(window),
  36. $module = $(this),
  37. $context = $(settings.context),
  38. selector = $module.selector || '',
  39. instance = $module.data(moduleNamespace),
  40. requestAnimationFrame = window.requestAnimationFrame
  41. || window.mozRequestAnimationFrame
  42. || window.webkitRequestAnimationFrame
  43. || window.msRequestAnimationFrame
  44. || function(callback) { setTimeout(callback, 0); },
  45. element = this,
  46. observer,
  47. module
  48. ;
  49. module = {
  50. initialize: function() {
  51. module.debug('Initializing', settings);
  52. module.setup.cache();
  53. module.save.position();
  54. if( module.should.trackChanges() ) {
  55. module.bind.events();
  56. if(settings.type == 'image') {
  57. module.setup.image();
  58. }
  59. if(settings.type == 'fixed') {
  60. module.setup.fixed();
  61. }
  62. if(settings.observeChanges) {
  63. module.observeChanges();
  64. }
  65. if( !module.is.visible() ) {
  66. module.error(error.visible, $module);
  67. }
  68. }
  69. if(settings.initialCheck) {
  70. module.checkVisibility();
  71. }
  72. module.instantiate();
  73. },
  74. instantiate: function() {
  75. module.debug('Storing instance', module);
  76. $module
  77. .data(moduleNamespace, module)
  78. ;
  79. instance = module;
  80. },
  81. destroy: function() {
  82. module.verbose('Destroying previous module');
  83. if(observer) {
  84. observer.disconnect();
  85. }
  86. $window
  87. .off('load' + eventNamespace, module.event.load)
  88. .off('resize' + eventNamespace, module.event.resize)
  89. ;
  90. $context.off('scrollchange' + eventNamespace, module.event.scrollchange);
  91. $module
  92. .off(eventNamespace)
  93. .removeData(moduleNamespace)
  94. ;
  95. },
  96. observeChanges: function() {
  97. var
  98. context = $context[0]
  99. ;
  100. if('MutationObserver' in window) {
  101. observer = new MutationObserver(function(mutations) {
  102. module.verbose('DOM tree modified, updating visibility calculations');
  103. module.timer = setTimeout(function() {
  104. module.verbose('DOM tree modified, updating sticky menu');
  105. module.refresh();
  106. }, 100);
  107. });
  108. observer.observe(element, {
  109. childList : true,
  110. subtree : true
  111. });
  112. module.debug('Setting up mutation observer', observer);
  113. }
  114. },
  115. bind: {
  116. events: function() {
  117. module.verbose('Binding visibility events to scroll and resize');
  118. $window
  119. .on('load' + eventNamespace, module.event.load)
  120. .on('resize' + eventNamespace, module.event.resize)
  121. ;
  122. // pub/sub pattern
  123. $context
  124. .off('scroll' + eventNamespace)
  125. .on('scroll' + eventNamespace, module.event.scroll)
  126. .on('scrollchange' + eventNamespace, module.event.scrollchange)
  127. ;
  128. },
  129. imageLoad: function() {
  130. var
  131. $images = $module.find('img'),
  132. imageCount = $images.length,
  133. index = imageCount,
  134. loadedCount = 0,
  135. images = [],
  136. cache = [],
  137. cacheImage = document.createElement('img'),
  138. handleLoad = function() {
  139. loadedCount++;
  140. if(loadedCount >= imageCount) {
  141. module.debug('Images finished loading inside element, refreshing position');
  142. module.refresh();
  143. }
  144. }
  145. ;
  146. if(imageCount > 0) {
  147. $images
  148. .each(function() {
  149. images.push( $(this).attr('src') );
  150. })
  151. ;
  152. while(index--) {
  153. cacheImage = document.createElement('img');
  154. cacheImage.onload = handleLoad;
  155. cacheImage.onerror = handleLoad;
  156. cacheImage.src = images[index];
  157. cache.push(cacheImage);
  158. }
  159. }
  160. }
  161. },
  162. event: {
  163. resize: function() {
  164. module.debug('Window resized');
  165. requestAnimationFrame(module.refresh);
  166. },
  167. load: function() {
  168. module.debug('Page finished loading');
  169. requestAnimationFrame(module.refresh);
  170. },
  171. // publishes scrollchange event on one scroll
  172. scroll: function() {
  173. if(settings.throttle) {
  174. clearTimeout(module.timer);
  175. module.timer = setTimeout(function() {
  176. $context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
  177. }, settings.throttle);
  178. }
  179. else {
  180. requestAnimationFrame(function() {
  181. $context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]);
  182. });
  183. }
  184. },
  185. // subscribes to scrollchange
  186. scrollchange: function(event, scrollPosition) {
  187. module.checkVisibility(scrollPosition);
  188. },
  189. },
  190. precache: function(images, callback) {
  191. if (!(images instanceof Array)) {
  192. images = [images];
  193. }
  194. var
  195. imagesLength = images.length,
  196. loadedCounter = 0,
  197. cache = [],
  198. cacheImage = document.createElement('img'),
  199. handleLoad = function() {
  200. loadedCounter++;
  201. if (loadedCounter >= images.length) {
  202. if ($.isFunction(callback)) {
  203. callback();
  204. }
  205. }
  206. }
  207. ;
  208. while (imagesLength--) {
  209. cacheImage = document.createElement('img');
  210. cacheImage.onload = handleLoad;
  211. cacheImage.onerror = handleLoad;
  212. cacheImage.src = images[imagesLength];
  213. cache.push(cacheImage);
  214. }
  215. },
  216. should: {
  217. trackChanges: function() {
  218. if(methodInvoked) {
  219. module.debug('One time query, no need to bind events');
  220. return false;
  221. }
  222. module.debug('Callbacks being attached');
  223. return true;
  224. }
  225. },
  226. setup: {
  227. cache: function() {
  228. module.cache = {
  229. occurred : {},
  230. screen : {},
  231. element : {},
  232. };
  233. },
  234. image: function() {
  235. var
  236. src = $module.data('src')
  237. ;
  238. if(src) {
  239. module.verbose('Lazy loading image', src);
  240. settings.observeChanges = false;
  241. // show when top visible
  242. module.topVisible(function() {
  243. module.debug('Image top visible', element);
  244. module.precache(src, function() {
  245. module.set.image(src);
  246. settings.onTopVisible = false;
  247. });
  248. });
  249. }
  250. },
  251. fixed: function() {
  252. module.verbose('Setting up fixed on element pass');
  253. settings.once = false;
  254. settings.onTopPassed = function() {
  255. $module
  256. .addClass(className.fixed)
  257. .css({
  258. top: settings.offset + 'px'
  259. })
  260. ;
  261. if(settings.transition) {
  262. if($.fn.transition !== undefined) {
  263. $module.transition(settings.transition, settings.duration);
  264. }
  265. }
  266. };
  267. settings.onTopPassedReverse = function() {
  268. $module
  269. .removeClass(className.fixed)
  270. .css({
  271. position: '',
  272. top: ''
  273. })
  274. ;
  275. };
  276. }
  277. },
  278. set: {
  279. image: function(src) {
  280. var
  281. offScreen = (module.cache.screen.bottom < module.cache.element.top)
  282. ;
  283. $module
  284. .attr('src', src)
  285. ;
  286. if(offScreen) {
  287. module.verbose('Image outside browser, no show animation');
  288. $module.show();
  289. }
  290. else {
  291. if(settings.transition) {
  292. if( $.fn.transition !== undefined ) {
  293. $module.transition(settings.transition, settings.duration);
  294. }
  295. else {
  296. $module.fadeIn(settings.duration);
  297. }
  298. }
  299. else {
  300. $module.show();
  301. }
  302. }
  303. }
  304. },
  305. is: {
  306. visible: function() {
  307. if(module.cache && module.cache.element) {
  308. return (module.cache.element.width > 0);
  309. }
  310. return false;
  311. }
  312. },
  313. refresh: function() {
  314. module.debug('Refreshing constants (width/height)');
  315. module.reset();
  316. module.save.position();
  317. module.checkVisibility();
  318. settings.onRefresh.call(element);
  319. },
  320. reset: function() {
  321. module.verbose('Reseting all cached values');
  322. if( $.isPlainObject(module.cache) ) {
  323. module.cache.screen = {};
  324. module.cache.element = {};
  325. }
  326. },
  327. checkVisibility: function(scroll) {
  328. module.verbose('Checking visibility of element', module.cache.element);
  329. if( module.is.visible() ) {
  330. // save scroll position
  331. module.save.scroll(scroll);
  332. // update calculations derived from scroll
  333. module.save.calculations();
  334. // percentage
  335. module.passed();
  336. // reverse (must be first)
  337. module.passingReverse();
  338. module.topVisibleReverse();
  339. module.bottomVisibleReverse();
  340. module.topPassedReverse();
  341. module.bottomPassedReverse();
  342. // one time
  343. module.passing();
  344. module.topVisible();
  345. module.bottomVisible();
  346. module.topPassed();
  347. module.bottomPassed();
  348. // on update callback
  349. if(settings.onUpdate) {
  350. settings.onUpdate.call(element, module.get.elementCalculations());
  351. }
  352. }
  353. },
  354. passed: function(amount, newCallback) {
  355. var
  356. calculations = module.get.elementCalculations(),
  357. amountInPixels
  358. ;
  359. // assign callback
  360. if(amount !== undefined && newCallback !== undefined) {
  361. settings.onPassed[amount] = newCallback;
  362. }
  363. else if(amount !== undefined) {
  364. return (module.get.pixelsPassed(amount) > calculations.pixelsPassed);
  365. }
  366. else if(calculations.passing) {
  367. $.each(settings.onPassed, function(amount, callback) {
  368. if(calculations.bottomVisible || calculations.pixelsPassed > module.get.pixelsPassed(amount)) {
  369. module.execute(callback, amount);
  370. }
  371. else if(!settings.once) {
  372. module.remove.occurred(callback);
  373. }
  374. });
  375. }
  376. },
  377. passing: function(newCallback) {
  378. var
  379. calculations = module.get.elementCalculations(),
  380. callback = newCallback || settings.onPassing,
  381. callbackName = 'passing'
  382. ;
  383. if(newCallback) {
  384. module.debug('Adding callback for passing', newCallback);
  385. settings.onPassing = newCallback;
  386. }
  387. if(calculations.passing) {
  388. module.execute(callback, callbackName);
  389. }
  390. else if(!settings.once) {
  391. module.remove.occurred(callbackName);
  392. }
  393. if(newCallback !== undefined) {
  394. return calculations.passing;
  395. }
  396. },
  397. topVisible: function(newCallback) {
  398. var
  399. calculations = module.get.elementCalculations(),
  400. callback = newCallback || settings.onTopVisible,
  401. callbackName = 'topVisible'
  402. ;
  403. if(newCallback) {
  404. module.debug('Adding callback for top visible', newCallback);
  405. settings.onTopVisible = newCallback;
  406. }
  407. if(calculations.topVisible) {
  408. module.execute(callback, callbackName);
  409. }
  410. else if(!settings.once) {
  411. module.remove.occurred(callbackName);
  412. }
  413. if(newCallback === undefined) {
  414. return calculations.topVisible;
  415. }
  416. },
  417. bottomVisible: function(newCallback) {
  418. var
  419. calculations = module.get.elementCalculations(),
  420. callback = newCallback || settings.onBottomVisible,
  421. callbackName = 'bottomVisible'
  422. ;
  423. if(newCallback) {
  424. module.debug('Adding callback for bottom visible', newCallback);
  425. settings.onBottomVisible = newCallback;
  426. }
  427. if(calculations.bottomVisible) {
  428. module.execute(callback, callbackName);
  429. }
  430. else if(!settings.once) {
  431. module.remove.occurred(callbackName);
  432. }
  433. if(newCallback === undefined) {
  434. return calculations.bottomVisible;
  435. }
  436. },
  437. topPassed: function(newCallback) {
  438. var
  439. calculations = module.get.elementCalculations(),
  440. callback = newCallback || settings.onTopPassed,
  441. callbackName = 'topPassed'
  442. ;
  443. if(newCallback) {
  444. module.debug('Adding callback for top passed', newCallback);
  445. settings.onTopPassed = newCallback;
  446. }
  447. if(calculations.topPassed) {
  448. module.execute(callback, callbackName);
  449. }
  450. else if(!settings.once) {
  451. module.remove.occurred(callbackName);
  452. }
  453. if(newCallback === undefined) {
  454. return calculations.topPassed;
  455. }
  456. },
  457. bottomPassed: function(newCallback) {
  458. var
  459. calculations = module.get.elementCalculations(),
  460. callback = newCallback || settings.onBottomPassed,
  461. callbackName = 'bottomPassed'
  462. ;
  463. if(newCallback) {
  464. module.debug('Adding callback for bottom passed', newCallback);
  465. settings.onBottomPassed = newCallback;
  466. }
  467. if(calculations.bottomPassed) {
  468. module.execute(callback, callbackName);
  469. }
  470. else if(!settings.once) {
  471. module.remove.occurred(callbackName);
  472. }
  473. if(newCallback === undefined) {
  474. return calculations.bottomPassed;
  475. }
  476. },
  477. passingReverse: function(newCallback) {
  478. var
  479. calculations = module.get.elementCalculations(),
  480. callback = newCallback || settings.onPassingReverse,
  481. callbackName = 'passingReverse'
  482. ;
  483. if(newCallback) {
  484. module.debug('Adding callback for passing reverse', newCallback);
  485. settings.onPassingReverse = newCallback;
  486. }
  487. if(!calculations.passing) {
  488. if(module.get.occurred('passing')) {
  489. module.execute(callback, callbackName);
  490. }
  491. }
  492. else if(!settings.once) {
  493. module.remove.occurred(callbackName);
  494. }
  495. if(newCallback !== undefined) {
  496. return !calculations.passing;
  497. }
  498. },
  499. topVisibleReverse: function(newCallback) {
  500. var
  501. calculations = module.get.elementCalculations(),
  502. callback = newCallback || settings.onTopVisibleReverse,
  503. callbackName = 'topVisibleReverse'
  504. ;
  505. if(newCallback) {
  506. module.debug('Adding callback for top visible reverse', newCallback);
  507. settings.onTopVisibleReverse = newCallback;
  508. }
  509. if(!calculations.topVisible) {
  510. if(module.get.occurred('topVisible')) {
  511. module.execute(callback, callbackName);
  512. }
  513. }
  514. else if(!settings.once) {
  515. module.remove.occurred(callbackName);
  516. }
  517. if(newCallback === undefined) {
  518. return !calculations.topVisible;
  519. }
  520. },
  521. bottomVisibleReverse: function(newCallback) {
  522. var
  523. calculations = module.get.elementCalculations(),
  524. callback = newCallback || settings.onBottomVisibleReverse,
  525. callbackName = 'bottomVisibleReverse'
  526. ;
  527. if(newCallback) {
  528. module.debug('Adding callback for bottom visible reverse', newCallback);
  529. settings.onBottomVisibleReverse = newCallback;
  530. }
  531. if(!calculations.bottomVisible) {
  532. if(module.get.occurred('bottomVisible')) {
  533. module.execute(callback, callbackName);
  534. }
  535. }
  536. else if(!settings.once) {
  537. module.remove.occurred(callbackName);
  538. }
  539. if(newCallback === undefined) {
  540. return !calculations.bottomVisible;
  541. }
  542. },
  543. topPassedReverse: function(newCallback) {
  544. var
  545. calculations = module.get.elementCalculations(),
  546. callback = newCallback || settings.onTopPassedReverse,
  547. callbackName = 'topPassedReverse'
  548. ;
  549. if(newCallback) {
  550. module.debug('Adding callback for top passed reverse', newCallback);
  551. settings.onTopPassedReverse = newCallback;
  552. }
  553. if(!calculations.topPassed) {
  554. if(module.get.occurred('topPassed')) {
  555. module.execute(callback, callbackName);
  556. }
  557. }
  558. else if(!settings.once) {
  559. module.remove.occurred(callbackName);
  560. }
  561. if(newCallback === undefined) {
  562. return !calculations.onTopPassed;
  563. }
  564. },
  565. bottomPassedReverse: function(newCallback) {
  566. var
  567. calculations = module.get.elementCalculations(),
  568. callback = newCallback || settings.onBottomPassedReverse,
  569. callbackName = 'bottomPassedReverse'
  570. ;
  571. if(newCallback) {
  572. module.debug('Adding callback for bottom passed reverse', newCallback);
  573. settings.onBottomPassedReverse = newCallback;
  574. }
  575. if(!calculations.bottomPassed) {
  576. if(module.get.occurred('bottomPassed')) {
  577. module.execute(callback, callbackName);
  578. }
  579. }
  580. else if(!settings.once) {
  581. module.remove.occurred(callbackName);
  582. }
  583. if(newCallback === undefined) {
  584. return !calculations.bottomPassed;
  585. }
  586. },
  587. execute: function(callback, callbackName) {
  588. var
  589. calculations = module.get.elementCalculations(),
  590. screen = module.get.screenCalculations()
  591. ;
  592. callback = callback || false;
  593. if(callback) {
  594. if(settings.continuous) {
  595. module.debug('Callback being called continuously', callbackName, calculations);
  596. callback.call(element, calculations, screen);
  597. }
  598. else if(!module.get.occurred(callbackName)) {
  599. module.debug('Conditions met', callbackName, calculations);
  600. callback.call(element, calculations, screen);
  601. }
  602. }
  603. module.save.occurred(callbackName);
  604. },
  605. remove: {
  606. occurred: function(callback) {
  607. if(callback) {
  608. if(module.cache.occurred[callback] !== undefined && module.cache.occurred[callback] === true) {
  609. module.debug('Callback can now be called again', callback);
  610. module.cache.occurred[callback] = false;
  611. }
  612. }
  613. else {
  614. module.cache.occurred = {};
  615. }
  616. }
  617. },
  618. save: {
  619. calculations: function() {
  620. module.verbose('Saving all calculations necessary to determine positioning');
  621. module.save.direction();
  622. module.save.screenCalculations();
  623. module.save.elementCalculations();
  624. },
  625. occurred: function(callback) {
  626. if(callback) {
  627. if(module.cache.occurred[callback] === undefined || (module.cache.occurred[callback] !== true)) {
  628. module.verbose('Saving callback occurred', callback);
  629. module.cache.occurred[callback] = true;
  630. }
  631. }
  632. },
  633. scroll: function(scrollPosition) {
  634. scrollPosition = scrollPosition + settings.offset || $context.scrollTop() + settings.offset;
  635. module.cache.scroll = scrollPosition;
  636. },
  637. direction: function() {
  638. var
  639. scroll = module.get.scroll(),
  640. lastScroll = module.get.lastScroll(),
  641. direction
  642. ;
  643. if(scroll > lastScroll && lastScroll) {
  644. direction = 'down';
  645. }
  646. else if(scroll < lastScroll && lastScroll) {
  647. direction = 'up';
  648. }
  649. else {
  650. direction = 'static';
  651. }
  652. module.cache.direction = direction;
  653. return module.cache.direction;
  654. },
  655. elementPosition: function() {
  656. var
  657. element = module.cache.element,
  658. screen = module.get.screenSize()
  659. ;
  660. module.verbose('Saving element position');
  661. // (quicker than $.extend)
  662. element.fits = (element.height < screen.height);
  663. element.offset = $module.offset();
  664. element.width = $module.outerWidth();
  665. element.height = $module.outerHeight();
  666. // store
  667. module.cache.element = element;
  668. return element;
  669. },
  670. elementCalculations: function() {
  671. var
  672. screen = module.get.screenCalculations(),
  673. element = module.get.elementPosition()
  674. ;
  675. // offset
  676. if(settings.includeMargin) {
  677. element.margin = {};
  678. element.margin.top = parseInt($module.css('margin-top'), 10);
  679. element.margin.bottom = parseInt($module.css('margin-bottom'), 10);
  680. element.top = element.offset.top - element.margin.top;
  681. element.bottom = element.offset.top + element.height + element.margin.bottom;
  682. }
  683. else {
  684. element.top = element.offset.top;
  685. element.bottom = element.offset.top + element.height;
  686. }
  687. // visibility
  688. element.topVisible = (screen.bottom >= element.top);
  689. element.topPassed = (screen.top >= element.top);
  690. element.bottomVisible = (screen.bottom >= element.bottom);
  691. element.bottomPassed = (screen.top >= element.bottom);
  692. element.pixelsPassed = 0;
  693. element.percentagePassed = 0;
  694. // meta calculations
  695. element.visible = (element.topVisible || element.bottomVisible);
  696. element.passing = (element.topPassed && !element.bottomPassed);
  697. element.hidden = (!element.topVisible && !element.bottomVisible);
  698. // passing calculations
  699. if(element.passing) {
  700. element.pixelsPassed = (screen.top - element.top);
  701. element.percentagePassed = (screen.top - element.top) / element.height;
  702. }
  703. module.cache.element = element;
  704. module.verbose('Updated element calculations', element);
  705. return element;
  706. },
  707. screenCalculations: function() {
  708. var
  709. scroll = module.get.scroll()
  710. ;
  711. module.save.direction();
  712. module.cache.screen.top = scroll;
  713. module.cache.screen.bottom = scroll + module.cache.screen.height;
  714. return module.cache.screen;
  715. },
  716. screenSize: function() {
  717. module.verbose('Saving window position');
  718. module.cache.screen = {
  719. height: $context.height()
  720. };
  721. },
  722. position: function() {
  723. module.save.screenSize();
  724. module.save.elementPosition();
  725. }
  726. },
  727. get: {
  728. pixelsPassed: function(amount) {
  729. var
  730. element = module.get.elementCalculations()
  731. ;
  732. if(amount.search('%') > -1) {
  733. return ( element.height * (parseInt(amount, 10) / 100) );
  734. }
  735. return parseInt(amount, 10);
  736. },
  737. occurred: function(callback) {
  738. return (module.cache.occurred !== undefined)
  739. ? module.cache.occurred[callback] || false
  740. : false
  741. ;
  742. },
  743. direction: function() {
  744. if(module.cache.direction === undefined) {
  745. module.save.direction();
  746. }
  747. return module.cache.direction;
  748. },
  749. elementPosition: function() {
  750. if(module.cache.element === undefined) {
  751. module.save.elementPosition();
  752. }
  753. return module.cache.element;
  754. },
  755. elementCalculations: function() {
  756. if(module.cache.element === undefined) {
  757. module.save.elementCalculations();
  758. }
  759. return module.cache.element;
  760. },
  761. screenCalculations: function() {
  762. if(module.cache.screen === undefined) {
  763. module.save.screenCalculations();
  764. }
  765. return module.cache.screen;
  766. },
  767. screenSize: function() {
  768. if(module.cache.screen === undefined) {
  769. module.save.screenSize();
  770. }
  771. return module.cache.screen;
  772. },
  773. scroll: function() {
  774. if(module.cache.scroll === undefined) {
  775. module.save.scroll();
  776. }
  777. return module.cache.scroll;
  778. },
  779. lastScroll: function() {
  780. if(module.cache.screen === undefined) {
  781. module.debug('First scroll event, no last scroll could be found');
  782. return false;
  783. }
  784. return module.cache.screen.top;
  785. }
  786. },
  787. setting: function(name, value) {
  788. if( $.isPlainObject(name) ) {
  789. $.extend(true, settings, name);
  790. }
  791. else if(value !== undefined) {
  792. settings[name] = value;
  793. }
  794. else {
  795. return settings[name];
  796. }
  797. },
  798. internal: function(name, value) {
  799. if( $.isPlainObject(name) ) {
  800. $.extend(true, module, name);
  801. }
  802. else if(value !== undefined) {
  803. module[name] = value;
  804. }
  805. else {
  806. return module[name];
  807. }
  808. },
  809. debug: function() {
  810. if(settings.debug) {
  811. if(settings.performance) {
  812. module.performance.log(arguments);
  813. }
  814. else {
  815. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  816. module.debug.apply(console, arguments);
  817. }
  818. }
  819. },
  820. verbose: function() {
  821. if(settings.verbose && settings.debug) {
  822. if(settings.performance) {
  823. module.performance.log(arguments);
  824. }
  825. else {
  826. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  827. module.verbose.apply(console, arguments);
  828. }
  829. }
  830. },
  831. error: function() {
  832. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  833. module.error.apply(console, arguments);
  834. },
  835. performance: {
  836. log: function(message) {
  837. var
  838. currentTime,
  839. executionTime,
  840. previousTime
  841. ;
  842. if(settings.performance) {
  843. currentTime = new Date().getTime();
  844. previousTime = time || currentTime;
  845. executionTime = currentTime - previousTime;
  846. time = currentTime;
  847. performance.push({
  848. 'Name' : message[0],
  849. 'Arguments' : [].slice.call(message, 1) || '',
  850. 'Element' : element,
  851. 'Execution Time' : executionTime
  852. });
  853. }
  854. clearTimeout(module.performance.timer);
  855. module.performance.timer = setTimeout(module.performance.display, 500);
  856. },
  857. display: function() {
  858. var
  859. title = settings.name + ':',
  860. totalTime = 0
  861. ;
  862. time = false;
  863. clearTimeout(module.performance.timer);
  864. $.each(performance, function(index, data) {
  865. totalTime += data['Execution Time'];
  866. });
  867. title += ' ' + totalTime + 'ms';
  868. if(moduleSelector) {
  869. title += ' \'' + moduleSelector + '\'';
  870. }
  871. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  872. console.groupCollapsed(title);
  873. if(console.table) {
  874. console.table(performance);
  875. }
  876. else {
  877. $.each(performance, function(index, data) {
  878. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  879. });
  880. }
  881. console.groupEnd();
  882. }
  883. performance = [];
  884. }
  885. },
  886. invoke: function(query, passedArguments, context) {
  887. var
  888. object = instance,
  889. maxDepth,
  890. found,
  891. response
  892. ;
  893. passedArguments = passedArguments || queryArguments;
  894. context = element || context;
  895. if(typeof query == 'string' && object !== undefined) {
  896. query = query.split(/[\. ]/);
  897. maxDepth = query.length - 1;
  898. $.each(query, function(depth, value) {
  899. var camelCaseValue = (depth != maxDepth)
  900. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  901. : query
  902. ;
  903. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  904. object = object[camelCaseValue];
  905. }
  906. else if( object[camelCaseValue] !== undefined ) {
  907. found = object[camelCaseValue];
  908. return false;
  909. }
  910. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  911. object = object[value];
  912. }
  913. else if( object[value] !== undefined ) {
  914. found = object[value];
  915. return false;
  916. }
  917. else {
  918. module.error(error.method, query);
  919. return false;
  920. }
  921. });
  922. }
  923. if ( $.isFunction( found ) ) {
  924. response = found.apply(context, passedArguments);
  925. }
  926. else if(found !== undefined) {
  927. response = found;
  928. }
  929. if($.isArray(returnedValue)) {
  930. returnedValue.push(response);
  931. }
  932. else if(returnedValue !== undefined) {
  933. returnedValue = [returnedValue, response];
  934. }
  935. else if(response !== undefined) {
  936. returnedValue = response;
  937. }
  938. return found;
  939. }
  940. };
  941. if(methodInvoked) {
  942. if(instance === undefined) {
  943. module.initialize();
  944. }
  945. module.invoke(query);
  946. }
  947. else {
  948. if(instance !== undefined) {
  949. instance.invoke('destroy');
  950. }
  951. module.initialize();
  952. }
  953. })
  954. ;
  955. return (returnedValue !== undefined)
  956. ? returnedValue
  957. : this
  958. ;
  959. };
  960. $.fn.visibility.settings = {
  961. name : 'Visibility',
  962. namespace : 'visibility',
  963. debug : false,
  964. verbose : false,
  965. performance : true,
  966. // whether to use mutation observers to follow changes
  967. observeChanges : true,
  968. // whether to refresh calculations after all page images load
  969. refreshOnLoad : true,
  970. // callback should only occur one time
  971. once : true,
  972. // callback should fire continuously whe evaluates to true
  973. continuous : false,
  974. // offset to use with scroll top
  975. offset : 0,
  976. // whether to include margin in elements position
  977. includeMargin : false,
  978. // scroll context for visibility checks
  979. context : window,
  980. // check position immediately on init
  981. initialCheck : true,
  982. // visibility check delay in ms (defaults to animationFrame)
  983. throttle : false,
  984. // special visibility type (image, fixed)
  985. type : false,
  986. // image only animation settings
  987. transition : false,
  988. duration : 1000,
  989. // array of callbacks for percentage
  990. onPassed : {},
  991. // standard callbacks
  992. onPassing : false,
  993. onTopVisible : false,
  994. onBottomVisible : false,
  995. onTopPassed : false,
  996. onBottomPassed : false,
  997. // reverse callbacks
  998. onPassingReverse : false,
  999. onTopVisibleReverse : false,
  1000. onBottomVisibleReverse : false,
  1001. onTopPassedReverse : false,
  1002. onBottomPassedReverse : false,
  1003. // utility callbacks
  1004. onUpdate : false, // disabled by default for performance
  1005. onRefresh : function(){},
  1006. className: {
  1007. fixed: 'fixed'
  1008. },
  1009. error : {
  1010. method : 'The method you called is not defined.',
  1011. visible : 'Element is hidden, you must call refresh after element becomes visible'
  1012. }
  1013. };
  1014. })( jQuery, window , document );