learn.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. // Scrollbar Width function
  2. function getScrollBarWidth() {
  3. var inner = document.createElement('p');
  4. inner.style.width = "100%";
  5. inner.style.height = "200px";
  6. var outer = document.createElement('div');
  7. outer.style.position = "absolute";
  8. outer.style.top = "0px";
  9. outer.style.left = "0px";
  10. outer.style.visibility = "hidden";
  11. outer.style.width = "200px";
  12. outer.style.height = "150px";
  13. outer.style.overflow = "hidden";
  14. outer.appendChild(inner);
  15. document.body.appendChild(outer);
  16. var w1 = inner.offsetWidth;
  17. outer.style.overflow = 'scroll';
  18. var w2 = inner.offsetWidth;
  19. if (w1 == w2) w2 = outer.clientWidth;
  20. document.body.removeChild(outer);
  21. return (w1 - w2);
  22. };
  23. var topics = {};
  24. var hOP = topics.hasOwnProperty;
  25. var huePubSub = {
  26. subscribe: function(topic, listener, app) {
  27. if (!hOP.call(topics, topic)) {
  28. topics[topic] = [];
  29. }
  30. var index =
  31. topics[topic].push({
  32. listener: listener,
  33. app: app,
  34. status: 'running'
  35. }) - 1;
  36. return {
  37. remove: function() {
  38. delete topics[topic][index];
  39. }
  40. };
  41. },
  42. removeAll: function(topic) {
  43. topics[topic] = [];
  44. },
  45. subscribeOnce: function(topic, listener, app) {
  46. var ephemeral = this.subscribe(
  47. topic,
  48. function() {
  49. listener.apply(listener, arguments);
  50. ephemeral.remove();
  51. },
  52. app
  53. );
  54. },
  55. publish: function(topic, info) {
  56. if (!hOP.call(topics, topic)) {
  57. return;
  58. }
  59. topics[topic].forEach(item => {
  60. if (item.status === 'running') {
  61. item.listener(info);
  62. }
  63. });
  64. },
  65. getTopics: function() {
  66. return topics;
  67. },
  68. pauseAppSubscribers: function(app) {
  69. if (app) {
  70. Object.keys(topics).forEach(topicName => {
  71. topics[topicName].forEach(topic => {
  72. if (
  73. typeof topic.app !== 'undefined' &&
  74. topic.app !== null &&
  75. (topic.app === app || topic.app.split('-')[0] === app)
  76. ) {
  77. topic.status = 'paused';
  78. }
  79. });
  80. });
  81. }
  82. },
  83. resumeAppSubscribers: function(app) {
  84. if (app) {
  85. Object.keys(topics).forEach(topicName => {
  86. topics[topicName].forEach(topic => {
  87. if (
  88. typeof topic.app !== 'undefined' &&
  89. topic.app !== null &&
  90. (topic.app === app || topic.app.split('-')[0] === app)
  91. ) {
  92. topic.status = 'running';
  93. }
  94. });
  95. });
  96. }
  97. },
  98. clearAppSubscribers: function(app) {
  99. if (app) {
  100. Object.keys(topics).forEach(topicName => {
  101. topics[topicName] = topics[topicName].filter(obj => {
  102. return obj.app !== app;
  103. });
  104. });
  105. }
  106. }
  107. };
  108. (function ($, window, document, undefined) {
  109. var pluginName = "jHueScrollUp",
  110. defaults = {
  111. threshold: 100, // it displays it after 100 px of scroll
  112. scrollLeft: false
  113. };
  114. function Plugin(element, options) {
  115. this.element = element;
  116. this.options = $.extend({}, defaults, options);
  117. this._defaults = defaults;
  118. this._name = pluginName;
  119. if ($(element).attr('jHueScrollified') !== 'true') {
  120. this.setupScrollUp();
  121. }
  122. if (this.options.scrollLeft) {
  123. $(element).jHueScrollLeft(this.options.threshold);
  124. }
  125. }
  126. Plugin.prototype.setOptions = function (options) {
  127. this.options = $.extend({}, defaults, options);
  128. };
  129. Plugin.prototype.setupScrollUp = function () {
  130. var _this = this,
  131. link = null;
  132. if ($("#jHueScrollUpAnchor").length > 0) { // just one scroll up per page
  133. link = $("#jHueScrollUpAnchor");
  134. $(document).off("click", "#jHueScrollUpAnchor");
  135. } else {
  136. link = $("<a/>").attr("id", "jHueScrollUpAnchor")
  137. .hide()
  138. .addClass("hueAnchor hueAnchorScroller")
  139. .attr("href", "javascript:void(0)")
  140. .html("<i class='fa fa-fw fa-chevron-up'></i>")
  141. .appendTo('#body-inner');
  142. }
  143. if ($(window).scrollTop() > _this.options.threshold) {
  144. link.show();
  145. }
  146. $(_this.element).attr("jHueScrollified", "true");
  147. if ($(_this.element).is("body")) {
  148. setScrollBehavior($(window), $("body, html"));
  149. } else {
  150. setScrollBehavior($(_this.element), $(_this.element));
  151. }
  152. huePubSub.subscribe('reposition.scroll.anchor.up', function(){
  153. $('#jHueScrollUpAnchor').css('right', '20px');
  154. if (!$(_this.element).is('body') && $(_this.element).is(':visible')) {
  155. var adjustRight = $(window).width() - ($(_this.element).width() + $(_this.element).offset().left);
  156. if (adjustRight > 0) {
  157. $('#jHueScrollUpAnchor').css('right', adjustRight + 'px');
  158. }
  159. }
  160. });
  161. function setScrollBehavior(scrolled, scrollable) {
  162. scrolled.scroll(function () {
  163. if (scrolled.scrollTop() > _this.options.threshold) {
  164. if (link.is(":hidden")) {
  165. huePubSub.publish('reposition.scroll.anchor.up');
  166. link.fadeIn(200, function(){
  167. huePubSub.publish('reposition.scroll.anchor.up');
  168. });
  169. }
  170. if ($(_this.element).data("lastScrollTop") == null || $(_this.element).data("lastScrollTop") < scrolled.scrollTop()) {
  171. $("#jHueScrollUpAnchor").data("caller", scrollable);
  172. }
  173. $(_this.element).data("lastScrollTop", scrolled.scrollTop());
  174. } else {
  175. checkForAllScrolls();
  176. }
  177. });
  178. window.setTimeout(function() {
  179. huePubSub.publish('reposition.scroll.anchor.up');
  180. }, 0);
  181. }
  182. function checkForAllScrolls() {
  183. var _allOk = true;
  184. $(document).find("[jHueScrollified='true']").each(function (cnt, item) {
  185. if ($(item).is("body")) {
  186. if ($(window).scrollTop() > _this.options.threshold) {
  187. _allOk = false;
  188. $("#jHueScrollUpAnchor").data("caller", $("body, html"));
  189. }
  190. } else if ($(item).scrollTop() > _this.options.threshold) {
  191. _allOk = false;
  192. $("#jHueScrollUpAnchor").data("caller", $(item));
  193. }
  194. });
  195. if (_allOk) {
  196. link.fadeOut(200);
  197. $("#jHueScrollUpAnchor").data("caller", null);
  198. }
  199. }
  200. $(document).on("click", "#jHueScrollUpAnchor", function (event) {
  201. if ($("#jHueScrollUpAnchor").data("caller") != null) {
  202. $("html, body").animate({ scrollTop: 0 }, 200);
  203. if ($(document).find("[jHueScrollified='true']").not($("#jHueScrollUpAnchor").data("caller")).is("body") && $(window).scrollTop() > _this.options.threshold) {
  204. $("#jHueScrollUpAnchor").data("caller", $("body, html"));
  205. } else {
  206. checkForAllScrolls();
  207. }
  208. }
  209. return false;
  210. });
  211. };
  212. $.fn[pluginName] = function (options) {
  213. return this.each(function () {
  214. $.data(this, 'plugin_' + pluginName, new Plugin(this, options));
  215. });
  216. };
  217. $[pluginName] = function (options) {
  218. new Plugin($("body"), options);
  219. };
  220. })(jQuery, window, document);
  221. $('body').jHueScrollUp();
  222. function setMenuHeight() {
  223. $('#sidebar .highlightable').height($('#sidebar').innerHeight() - $('#header-wrapper').height() - 40);
  224. $('#sidebar .highlightable').perfectScrollbar('update');
  225. }
  226. function fallbackMessage(action) {
  227. var actionMsg = '';
  228. var actionKey = (action === 'cut' ? 'X' : 'C');
  229. if (/iPhone|iPad/i.test(navigator.userAgent)) {
  230. actionMsg = 'No support :(';
  231. }
  232. else if (/Mac/i.test(navigator.userAgent)) {
  233. actionMsg = 'Press ⌘-' + actionKey + ' to ' + action;
  234. }
  235. else {
  236. actionMsg = 'Press Ctrl-' + actionKey + ' to ' + action;
  237. }
  238. return actionMsg;
  239. }
  240. // for the window resize
  241. $(window).resize(function() {
  242. setMenuHeight();
  243. });
  244. // debouncing function from John Hann
  245. // http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
  246. (function($, sr) {
  247. var debounce = function(func, threshold, execAsap) {
  248. var timeout;
  249. return function debounced() {
  250. var obj = this, args = arguments;
  251. function delayed() {
  252. if (!execAsap)
  253. func.apply(obj, args);
  254. timeout = null;
  255. };
  256. if (timeout)
  257. clearTimeout(timeout);
  258. else if (execAsap)
  259. func.apply(obj, args);
  260. timeout = setTimeout(delayed, threshold || 100);
  261. };
  262. }
  263. // smartresize
  264. jQuery.fn[sr] = function(fn) { return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
  265. })(jQuery, 'smartresize');
  266. jQuery(document).ready(function() {
  267. jQuery('#sidebar .category-icon').on('click', function() {
  268. $( this ).toggleClass("fa-angle-down fa-angle-right") ;
  269. $( this ).parent().parent().children('ul').toggle() ;
  270. return false;
  271. });
  272. var sidebarStatus = searchStatus = 'open';
  273. $('#sidebar .highlightable').perfectScrollbar();
  274. setMenuHeight();
  275. jQuery('#overlay').on('click', function() {
  276. jQuery(document.body).toggleClass('sidebar-hidden');
  277. sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
  278. return false;
  279. });
  280. jQuery('[data-sidebar-toggle]').on('click', function() {
  281. jQuery(document.body).toggleClass('sidebar-hidden');
  282. sidebarStatus = (jQuery(document.body).hasClass('sidebar-hidden') ? 'closed' : 'open');
  283. return false;
  284. });
  285. jQuery('[data-clear-history-toggle]').on('click', function() {
  286. sessionStorage.clear();
  287. location.reload();
  288. return false;
  289. });
  290. jQuery('[data-search-toggle]').on('click', function() {
  291. if (sidebarStatus == 'closed') {
  292. jQuery('[data-sidebar-toggle]').trigger('click');
  293. jQuery(document.body).removeClass('searchbox-hidden');
  294. searchStatus = 'open';
  295. return false;
  296. }
  297. jQuery(document.body).toggleClass('searchbox-hidden');
  298. searchStatus = (jQuery(document.body).hasClass('searchbox-hidden') ? 'closed' : 'open');
  299. return false;
  300. });
  301. var ajax;
  302. jQuery('[data-search-input]').on('input', function() {
  303. var input = jQuery(this),
  304. value = input.val(),
  305. items = jQuery('[data-nav-id]');
  306. items.removeClass('search-match');
  307. if (!value.length) {
  308. $('ul.topics').removeClass('searched');
  309. items.css('display', 'block');
  310. sessionStorage.removeItem('search-value');
  311. $(".highlightable").unhighlight({ element: 'mark' })
  312. return;
  313. }
  314. sessionStorage.setItem('search-value', value);
  315. $(".highlightable").unhighlight({ element: 'mark' }).highlight(value, { element: 'mark' });
  316. if (ajax && ajax.abort) ajax.abort();
  317. jQuery('[data-search-clear]').on('click', function() {
  318. jQuery('[data-search-input]').val('').trigger('input');
  319. sessionStorage.removeItem('search-input');
  320. $(".highlightable").unhighlight({ element: 'mark' })
  321. });
  322. });
  323. $.expr[":"].contains = $.expr.createPseudo(function(arg) {
  324. return function( elem ) {
  325. return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
  326. };
  327. });
  328. if (sessionStorage.getItem('search-value')) {
  329. var searchValue = sessionStorage.getItem('search-value')
  330. $(document.body).removeClass('searchbox-hidden');
  331. $('[data-search-input]').val(searchValue);
  332. $('[data-search-input]').trigger('input');
  333. var searchedElem = $('#body-inner').find(':contains(' + searchValue + ')').get(0);
  334. if (searchedElem) {
  335. searchedElem.scrollIntoView(true);
  336. var scrolledY = window.scrollY;
  337. if(scrolledY){
  338. window.scroll(0, scrolledY - 125);
  339. }
  340. }
  341. }
  342. // clipboard
  343. var clipInit = false;
  344. $('code').each(function() {
  345. var code = $(this),
  346. text = code.text();
  347. if (text.length > 5) {
  348. if (!clipInit) {
  349. var text, clip = new Clipboard('.copy-to-clipboard', {
  350. text: function(trigger) {
  351. text = $(trigger).prev('code').text();
  352. return text.replace(/^\$\s/gm, '');
  353. }
  354. });
  355. var inPre;
  356. clip.on('success', function(e) {
  357. e.clearSelection();
  358. inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
  359. $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
  360. });
  361. clip.on('error', function(e) {
  362. inPre = $(e.trigger).parent().prop('tagName') == 'PRE';
  363. $(e.trigger).attr('aria-label', fallbackMessage(e.action)).addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
  364. $(document).one('copy', function(){
  365. $(e.trigger).attr('aria-label', 'Copied to clipboard!').addClass('tooltipped tooltipped-' + (inPre ? 'w' : 's'));
  366. });
  367. });
  368. clipInit = true;
  369. }
  370. code.after('<span class="copy-to-clipboard" title="Copy to clipboard" />');
  371. code.next('.copy-to-clipboard').on('mouseleave', function() {
  372. $(this).attr('aria-label', null).removeClass('tooltipped tooltipped-s tooltipped-w');
  373. });
  374. }
  375. });
  376. // allow keyboard control for prev/next links
  377. jQuery(function() {
  378. jQuery('.nav-prev').click(function(){
  379. location.href = jQuery(this).attr('href');
  380. });
  381. jQuery('.nav-next').click(function() {
  382. location.href = jQuery(this).attr('href');
  383. });
  384. });
  385. jQuery('input, textarea').keydown(function (e) {
  386. // left and right arrow keys
  387. if (e.which == '37' || e.which == '39') {
  388. e.stopPropagation();
  389. }
  390. });
  391. jQuery(document).keydown(function(e) {
  392. // prev links - left arrow key
  393. if(e.which == '37') {
  394. jQuery('.nav.nav-prev').click();
  395. }
  396. // next links - right arrow key
  397. if(e.which == '39') {
  398. jQuery('.nav.nav-next').click();
  399. }
  400. });
  401. $('#top-bar a:not(:has(img)):not(.btn)').addClass('highlight');
  402. $('#body-inner a:not(:has(img)):not(.btn):not(a[rel="footnote"])').addClass('highlight');
  403. var touchsupport = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)
  404. if (!touchsupport){ // browser doesn't support touch
  405. $('#toc-menu').hover(function() {
  406. $('.progress').stop(true, false, true).fadeToggle(100);
  407. });
  408. $('.progress').hover(function() {
  409. $('.progress').stop(true, false, true).fadeToggle(100);
  410. });
  411. }
  412. if (touchsupport){ // browser does support touch
  413. $('#toc-menu').click(function() {
  414. $('.progress').stop(true, false, true).fadeToggle(100);
  415. });
  416. $('.progress').click(function() {
  417. $('.progress').stop(true, false, true).fadeToggle(100);
  418. });
  419. }
  420. /**
  421. * Fix anchor scrolling that hides behind top nav bar
  422. * Courtesy of https://stackoverflow.com/a/13067009/28106
  423. *
  424. * We could use pure css for this if only heading anchors were
  425. * involved, but this works for any anchor, including footnotes
  426. **/
  427. (function (document, history, location) {
  428. var HISTORY_SUPPORT = !!(history && history.pushState);
  429. var anchorScrolls = {
  430. ANCHOR_REGEX: /^#[^ ]+$/,
  431. OFFSET_HEIGHT_PX: 50,
  432. /**
  433. * Establish events, and fix initial scroll position if a hash is provided.
  434. */
  435. init: function () {
  436. this.scrollToCurrent();
  437. $(window).on('hashchange', $.proxy(this, 'scrollToCurrent'));
  438. $('body').on('click', 'a', $.proxy(this, 'delegateAnchors'));
  439. },
  440. /**
  441. * Return the offset amount to deduct from the normal scroll position.
  442. * Modify as appropriate to allow for dynamic calculations
  443. */
  444. getFixedOffset: function () {
  445. return this.OFFSET_HEIGHT_PX;
  446. },
  447. /**
  448. * If the provided href is an anchor which resolves to an element on the
  449. * page, scroll to it.
  450. * @param {String} href
  451. * @return {Boolean} - Was the href an anchor.
  452. */
  453. scrollIfAnchor: function (href, pushToHistory) {
  454. var match, anchorOffset;
  455. if (!this.ANCHOR_REGEX.test(href)) {
  456. return false;
  457. }
  458. match = document.getElementById(href.slice(1));
  459. if (match) {
  460. anchorOffset = $(match).offset().top - this.getFixedOffset();
  461. $('html, body').animate({ scrollTop: anchorOffset });
  462. // Add the state to history as-per normal anchor links
  463. if (HISTORY_SUPPORT && pushToHistory) {
  464. history.pushState({}, document.title, location.pathname + href);
  465. }
  466. }
  467. return !!match;
  468. },
  469. /**
  470. * Attempt to scroll to the current location's hash.
  471. */
  472. scrollToCurrent: function (e) {
  473. if (this.scrollIfAnchor(window.location.hash) && e) {
  474. e.preventDefault();
  475. }
  476. },
  477. /**
  478. * If the click event's target was an anchor, fix the scroll position.
  479. */
  480. delegateAnchors: function (e) {
  481. var elem = e.target;
  482. if (this.scrollIfAnchor(elem.getAttribute('href'), true)) {
  483. e.preventDefault();
  484. }
  485. }
  486. };
  487. $(document).ready($.proxy(anchorScrolls, 'init'));
  488. })(window.document, window.history, window.location);
  489. });
  490. jQuery(window).on('load', function() {
  491. function adjustForScrollbar() {
  492. if ((parseInt(jQuery('#body-inner').height()) + 83) >= jQuery('#body').height()) {
  493. jQuery('.nav.nav-next').css({ 'margin-right': getScrollBarWidth() });
  494. } else {
  495. jQuery('.nav.nav-next').css({ 'margin-right': 0 });
  496. }
  497. }
  498. // adjust sidebar for scrollbar
  499. adjustForScrollbar();
  500. jQuery(window).smartresize(function() {
  501. adjustForScrollbar();
  502. });
  503. // store this page in session
  504. sessionStorage.setItem(jQuery('body').data('url'), 1);
  505. // loop through the sessionStorage and see if something should be marked as visited
  506. for (var url in sessionStorage) {
  507. if (sessionStorage.getItem(url) == 1) jQuery('[data-nav-id="' + url + '"]').addClass('visited');
  508. }
  509. $(".highlightable").highlight(sessionStorage.getItem('search-value'), { element: 'mark' });
  510. });
  511. $(function() {
  512. $('a[rel="lightbox"]').featherlight({
  513. root: 'section#body'
  514. });
  515. });
  516. jQuery.extend({
  517. highlight: function(node, re, nodeName, className) {
  518. if (node.nodeType === 3) {
  519. var match = node.data.match(re);
  520. if (match) {
  521. var highlight = document.createElement(nodeName || 'span');
  522. highlight.className = className || 'highlight';
  523. var wordNode = node.splitText(match.index);
  524. wordNode.splitText(match[0].length);
  525. var wordClone = wordNode.cloneNode(true);
  526. highlight.appendChild(wordClone);
  527. wordNode.parentNode.replaceChild(highlight, wordNode);
  528. return 1; //skip added node in parent
  529. }
  530. } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
  531. !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
  532. !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
  533. for (var i = 0; i < node.childNodes.length; i++) {
  534. i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
  535. }
  536. }
  537. return 0;
  538. }
  539. });
  540. jQuery.fn.unhighlight = function(options) {
  541. var settings = {
  542. className: 'highlight',
  543. element: 'span'
  544. };
  545. jQuery.extend(settings, options);
  546. return this.find(settings.element + "." + settings.className).each(function() {
  547. var parent = this.parentNode;
  548. parent.replaceChild(this.firstChild, this);
  549. parent.normalize();
  550. }).end();
  551. };
  552. jQuery.fn.highlight = function(words, options) {
  553. var settings = {
  554. className: 'highlight',
  555. element: 'span',
  556. caseSensitive: false,
  557. wordsOnly: false
  558. };
  559. jQuery.extend(settings, options);
  560. if (!words) { return; }
  561. if (words.constructor === String) {
  562. words = [words];
  563. }
  564. words = jQuery.grep(words, function(word, i) {
  565. return word != '';
  566. });
  567. words = jQuery.map(words, function(word, i) {
  568. return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
  569. });
  570. if (words.length == 0) { return this; }
  571. ;
  572. var flag = settings.caseSensitive ? "" : "i";
  573. var pattern = "(" + words.join("|") + ")";
  574. if (settings.wordsOnly) {
  575. pattern = "\\b" + pattern + "\\b";
  576. }
  577. var re = new RegExp(pattern, flag);
  578. return this.each(function() {
  579. jQuery.highlight(this, re, settings.element, settings.className);
  580. });
  581. };
  582. $(window).on('DOMContentLoaded load resize scroll', function () {
  583. function isElementInViewport (el) {
  584. var rect = el.getBoundingClientRect(),
  585. vWidth = window.innerWidth || doc.documentElement.clientWidth,
  586. vHeight = window.innerHeight || doc.documentElement.clientHeight,
  587. efp = function (x, y) { return document.elementFromPoint(x, y) };
  588. // Return false if it's not in the viewport
  589. if (rect.right < 0 || rect.bottom < 0
  590. || rect.left > vWidth || rect.top > vHeight)
  591. return false;
  592. // Return true if any of its four corners are visible
  593. return (
  594. el.contains(efp(rect.left, rect.top))
  595. || el.contains(efp(rect.right, rect.top))
  596. || el.contains(efp(rect.right, rect.bottom))
  597. || el.contains(efp(rect.left, rect.bottom))
  598. );
  599. }
  600. var visible = 0;
  601. $('#body-inner h1, #body-inner h2, #body-inner h3, #body-inner h4, #body-inner h5, #body-inner h6').each(function (i,e) {
  602. if(isElementInViewport(e)) {
  603. visible = $(e).attr('id');
  604. if (visible) {
  605. return false;
  606. }
  607. }
  608. });
  609. if(visible){
  610. $('.toc ul li a').removeClass('active');
  611. $('.toc ul li a[href="#'+visible+'"]').addClass('active');
  612. }
  613. });