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.

1095 lines
35 KiB

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
9 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
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 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
  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 );