From b612cf9e4c4ab947aabac6bfc8bf38c69163e25f Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 1 Apr 2018 12:46:27 +0100 Subject: [PATCH] Refactored out page-display system --- resources/assets/js/components/index.js | 1 + .../assets/js/components/page-display.js | 224 ++++++++++++++++++ resources/assets/js/index.js | 9 +- resources/assets/js/pages/page-show.js | 216 ----------------- resources/views/pages/show.blade.php | 8 +- 5 files changed, 230 insertions(+), 228 deletions(-) create mode 100644 resources/assets/js/components/page-display.js delete mode 100644 resources/assets/js/pages/page-show.js diff --git a/resources/assets/js/components/index.js b/resources/assets/js/components/index.js index 6069dd18b..aa69f3265 100644 --- a/resources/assets/js/components/index.js +++ b/resources/assets/js/components/index.js @@ -17,6 +17,7 @@ let componentMapping = { 'image-picker': require('./image-picker'), 'collapsible': require('./collapsible'), 'toggle-switch': require('./toggle-switch'), + 'page-display': require('./page-display'), }; window.components = {}; diff --git a/resources/assets/js/components/page-display.js b/resources/assets/js/components/page-display.js new file mode 100644 index 000000000..5d9f9edee --- /dev/null +++ b/resources/assets/js/components/page-display.js @@ -0,0 +1,224 @@ +import Clipboard from "clipboard"; +import Code from "../libs/code"; + +class PageDisplay { + + constructor(elem) { + this.elem = elem; + this.pageId = elem.getAttribute('page-display'); + + Code.highlight(); + this.setupPointer(); + this.setupStickySidebar(); + this.setupNavHighlighting(); + + // Check the hash on load + if (window.location.hash) { + let text = window.location.hash.replace(/\%20/g, ' ').substr(1); + this.goToText(text); + } + + // Sidebar page nav click event + $('.sidebar-page-nav').on('click', 'a', event => { + goToText(event.target.getAttribute('href').substr(1)); + }); + } + + goToText(text) { + let idElem = document.getElementById(text); + $('.page-content [data-highlighted]').attr('data-highlighted', '').css('background-color', ''); + if (idElem !== null) { + window.scrollAndHighlight(idElem); + } else { + $('.page-content').find(':contains("' + text + '")').smoothScrollTo(); + } + } + + setupPointer() { + // Set up pointer + let $pointer = $('#pointer').detach(); + let pointerShowing = false; + let $pointerInner = $pointer.children('div.pointer').first(); + let isSelection = false; + let pointerModeLink = true; + let pointerSectionId = ''; + + // Select all contents on input click + $pointer.on('click', 'input', event => { + $(this).select(); + event.stopPropagation(); + }); + + $pointer.on('click focus', event => { + event.stopPropagation(); + }); + + // Pointer mode toggle + $pointer.on('click', 'span.icon', event => { + event.stopPropagation(); + let $icon = $(event.currentTarget); + pointerModeLink = !pointerModeLink; + $icon.find('[data-icon="include"]').toggle(!pointerModeLink); + $icon.find('[data-icon="link"]').toggle(pointerModeLink); + updatePointerContent(); + }); + + // Set up clipboard + let clipboard = new Clipboard($pointer[0].querySelector('button')); + + // Hide pointer when clicking away + $(document.body).find('*').on('click focus', event => { + if (!pointerShowing || isSelection) return; + $pointer.detach(); + pointerShowing = false; + }); + + let updatePointerContent = () => { + let inputText = pointerModeLink ? window.baseUrl(`/link/${this.pageId}#${pointerSectionId}`) : `{{@${this.pageId}#${pointerSectionId}}}`; + if (pointerModeLink && inputText.indexOf('http') !== 0) inputText = window.location.protocol + "//" + window.location.host + inputText; + + $pointer.find('input').val(inputText); + }; + + // Show pointer when selecting a single block of tagged content + $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { + e.stopPropagation(); + let selection = window.getSelection(); + if (selection.toString().length === 0) return; + + // Show pointer and set link + let $elem = $(this); + pointerSectionId = $elem.attr('id'); + updatePointerContent(); + + $elem.before($pointer); + $pointer.show(); + pointerShowing = true; + + // Set pointer to sit near mouse-up position + let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); + if (pointerLeftOffset < 0) pointerLeftOffset = 0; + let pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100; + $pointerInner.css('left', pointerLeftOffsetPercent + '%'); + + isSelection = true; + setTimeout(() => { + isSelection = false; + }, 100); + }); + } + + setupStickySidebar() { + // Make the sidebar stick in view on scroll + let $window = $(window); + let $sidebar = $("#sidebar .scroll-body"); + let $bookTreeParent = $sidebar.parent(); + + // Check the page is scrollable and the content is taller than the tree + let pageScrollable = ($(document).height() > $window.height()) && ($sidebar.height() < $('.page-content').height()); + + // Get current tree's width and header height + let headerHeight = $("#header").height() + $(".toolbar").height(); + let isFixed = $window.scrollTop() > headerHeight; + + // Fix the tree as a sidebar + function stickTree() { + $sidebar.width($bookTreeParent.width() + 15); + $sidebar.addClass("fixed"); + isFixed = true; + } + + // Un-fix the tree back into position + function unstickTree() { + $sidebar.css('width', 'auto'); + $sidebar.removeClass("fixed"); + isFixed = false; + } + + // Checks if the tree stickiness state should change + function checkTreeStickiness(skipCheck) { + let shouldBeFixed = $window.scrollTop() > headerHeight; + if (shouldBeFixed && (!isFixed || skipCheck)) { + stickTree(); + } else if (!shouldBeFixed && (isFixed || skipCheck)) { + unstickTree(); + } + } + // The event ran when the window scrolls + function windowScrollEvent() { + checkTreeStickiness(false); + } + + // If the page is scrollable and the window is wide enough listen to scroll events + // and evaluate tree stickiness. + if (pageScrollable && $window.width() > 1000) { + $window.on('scroll', windowScrollEvent); + checkTreeStickiness(true); + } + + // Handle window resizing and switch between desktop/mobile views + $window.on('resize', event => { + if (pageScrollable && $window.width() > 1000) { + $window.on('scroll', windowScrollEvent); + checkTreeStickiness(true); + } else { + $window.off('scroll', windowScrollEvent); + unstickTree(); + } + }); + } + + setupNavHighlighting() { + // Check if support is present for IntersectionObserver + if (!'IntersectionObserver' in window || + !'IntersectionObserverEntry' in window || + !'intersectionRatio' in window.IntersectionObserverEntry.prototype) { + return; + } + + let pageNav = document.querySelector('.sidebar-page-nav'); + + // fetch all the headings. + let headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6'); + // if headings are present, add observers. + if (headings.length > 0 && pageNav !== null) { + addNavObserver(headings); + } + + function addNavObserver(headings) { + // Setup the intersection observer. + let intersectOpts = { + rootMargin: '0px 0px 0px 0px', + threshold: 1.0 + }; + let pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); + + // observe each heading + for (let i = 0; i !== headings.length; ++i) { + pageNavObserver.observe(headings[i]); + } + } + + function headingVisibilityChange(entries, observer) { + for (let entry of entries) { + let isVisible = (entry.intersectionRatio === 1); + toggleAnchorHighlighting(entry.target.id, isVisible); + } + } + + function toggleAnchorHighlighting(elementId, shouldHighlight) { + let anchorsToHighlight = pageNav.querySelectorAll('a[href="#' + elementId + '"]'); + for (let i = 0; i < anchorsToHighlight.length; i++) { + // Change below to use classList.toggle when IE support is dropped. + if (shouldHighlight) { + anchorsToHighlight[i].classList.add('current-heading'); + } else { + anchorsToHighlight[i].classList.remove('current-heading'); + } + } + } + } + +} + +module.exports = PageDisplay; \ No newline at end of file diff --git a/resources/assets/js/index.js b/resources/assets/js/index.js index 6022cd0a5..d72ee3fdc 100644 --- a/resources/assets/js/index.js +++ b/resources/assets/js/index.js @@ -4,7 +4,6 @@ import "./dom-polyfills" import jQuery from "jquery" window.jQuery = window.$ = jQuery; -import "./pages/page-show" import Translations from "./translations" import vues from "./vues/vues" import components from "./components" @@ -71,10 +70,6 @@ let translator = new Translations(window.translations); window.trans = translator.get.bind(translator); window.trans_choice = translator.getPlural.bind(translator); -// Load vues and components -vues(); -components(); - //Global jQuery Config & Extensions @@ -131,3 +126,7 @@ if(navigator.userAgent.indexOf('MSIE')!==-1 || navigator.userAgent.indexOf('Safari') !== -1){ document.body.classList.add('flexbox-support'); } + +// Load vues and components +vues(); +components(); \ No newline at end of file diff --git a/resources/assets/js/pages/page-show.js b/resources/assets/js/pages/page-show.js deleted file mode 100644 index cb525b959..000000000 --- a/resources/assets/js/pages/page-show.js +++ /dev/null @@ -1,216 +0,0 @@ -const Clipboard = require("clipboard"); -const Code = require('../libs/code'); - -let setupPageShow = window.setupPageShow = function (pageId) { - - Code.highlight(); - - if (!pageId) return; - - // Set up pointer - let $pointer = $('#pointer').detach(); - let pointerShowing = false; - let $pointerInner = $pointer.children('div.pointer').first(); - let isSelection = false; - let pointerModeLink = true; - let pointerSectionId = ''; - - // Select all contents on input click - $pointer.on('click', 'input', event => { - $(this).select(); - event.stopPropagation(); - }); - - $pointer.on('click focus', event => { - event.stopPropagation(); - }); - - // Pointer mode toggle - $pointer.on('click', 'span.icon', event => { - event.stopPropagation(); - let $icon = $(event.currentTarget); - pointerModeLink = !pointerModeLink; - $icon.find('[data-icon="include"]').toggle(!pointerModeLink); - $icon.find('[data-icon="link"]').toggle(pointerModeLink); - updatePointerContent(); - }); - - // Set up clipboard - let clipboard = new Clipboard($pointer[0].querySelector('button')); - - // Hide pointer when clicking away - $(document.body).find('*').on('click focus', event => { - if (!pointerShowing || isSelection) return; - $pointer.detach(); - pointerShowing = false; - }); - - function updatePointerContent() { - let inputText = pointerModeLink ? window.baseUrl(`/link/${pageId}#${pointerSectionId}`) : `{{@${pageId}#${pointerSectionId}}}`; - if (pointerModeLink && inputText.indexOf('http') !== 0) inputText = window.location.protocol + "//" + window.location.host + inputText; - - $pointer.find('input').val(inputText); - } - - // Show pointer when selecting a single block of tagged content - $('.page-content [id^="bkmrk"]').on('mouseup keyup', function (e) { - e.stopPropagation(); - let selection = window.getSelection(); - if (selection.toString().length === 0) return; - - // Show pointer and set link - let $elem = $(this); - pointerSectionId = $elem.attr('id'); - updatePointerContent(); - - $elem.before($pointer); - $pointer.show(); - pointerShowing = true; - - // Set pointer to sit near mouse-up position - let pointerLeftOffset = (e.pageX - $elem.offset().left - ($pointerInner.width() / 2)); - if (pointerLeftOffset < 0) pointerLeftOffset = 0; - let pointerLeftOffsetPercent = (pointerLeftOffset / $elem.width()) * 100; - $pointerInner.css('left', pointerLeftOffsetPercent + '%'); - - isSelection = true; - setTimeout(() => { - isSelection = false; - }, 100); - }); - - // Go to, and highlight if necessary, the specified text. - function goToText(text) { - let idElem = document.getElementById(text); - $('.page-content [data-highlighted]').attr('data-highlighted', '').css('background-color', ''); - if (idElem !== null) { - window.scrollAndHighlight(idElem); - } else { - $('.page-content').find(':contains("' + text + '")').smoothScrollTo(); - } - } - - // Check the hash on load - if (window.location.hash) { - let text = window.location.hash.replace(/\%20/g, ' ').substr(1); - goToText(text); - } - - // Sidebar page nav click event - $('.sidebar-page-nav').on('click', 'a', event => { - goToText(event.target.getAttribute('href').substr(1)); - }); - - // Make the sidebar stick in view on scroll - let $window = $(window); - let $sidebar = $("#sidebar .scroll-body"); - let $bookTreeParent = $sidebar.parent(); - - // Check the page is scrollable and the content is taller than the tree - let pageScrollable = ($(document).height() > $window.height()) && ($sidebar.height() < $('.page-content').height()); - - // Get current tree's width and header height - let headerHeight = $("#header").height() + $(".toolbar").height(); - let isFixed = $window.scrollTop() > headerHeight; - - // Fix the tree as a sidebar - function stickTree() { - $sidebar.width($bookTreeParent.width() + 15); - $sidebar.addClass("fixed"); - isFixed = true; - } - - // Un-fix the tree back into position - function unstickTree() { - $sidebar.css('width', 'auto'); - $sidebar.removeClass("fixed"); - isFixed = false; - } - - // Checks if the tree stickiness state should change - function checkTreeStickiness(skipCheck) { - let shouldBeFixed = $window.scrollTop() > headerHeight; - if (shouldBeFixed && (!isFixed || skipCheck)) { - stickTree(); - } else if (!shouldBeFixed && (isFixed || skipCheck)) { - unstickTree(); - } - } - // The event ran when the window scrolls - function windowScrollEvent() { - checkTreeStickiness(false); - } - - // If the page is scrollable and the window is wide enough listen to scroll events - // and evaluate tree stickiness. - if (pageScrollable && $window.width() > 1000) { - $window.on('scroll', windowScrollEvent); - checkTreeStickiness(true); - } - - // Handle window resizing and switch between desktop/mobile views - $window.on('resize', event => { - if (pageScrollable && $window.width() > 1000) { - $window.on('scroll', windowScrollEvent); - checkTreeStickiness(true); - } else { - $window.off('scroll', windowScrollEvent); - unstickTree(); - } - }); - - - // Check if support is present for IntersectionObserver - if ('IntersectionObserver' in window && - 'IntersectionObserverEntry' in window && - 'intersectionRatio' in window.IntersectionObserverEntry.prototype) { - addPageHighlighting(); - } - - function addPageHighlighting() { - let pageNav = document.querySelector('.sidebar-page-nav'); - - // fetch all the headings. - let headings = document.querySelector('.page-content').querySelectorAll('h1, h2, h3, h4, h5, h6'); - // if headings are present, add observers. - if (headings.length > 0 && pageNav !== null) { - addNavObserver(headings); - } - - function addNavObserver(headings) { - // Setup the intersection observer. - let intersectOpts = { - rootMargin: '0px 0px 0px 0px', - threshold: 1.0 - }; - let pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts); - - // observe each heading - for (let i = 0; i !== headings.length; ++i) { - pageNavObserver.observe(headings[i]); - } - } - - function headingVisibilityChange(entries, observer) { - for (let i = 0; i < entries.length; i++) { - let currentEntry = entries[i]; - let isVisible = (currentEntry.intersectionRatio === 1); - toggleAnchorHighlighting(currentEntry.target.id, isVisible); - } - } - - function toggleAnchorHighlighting(elementId, shouldHighlight) { - let anchorsToHighlight = pageNav.querySelectorAll('a[href="#' + elementId + '"]'); - for (let i = 0; i < anchorsToHighlight.length; i++) { - // Change below to use classList.toggle when IE support is dropped. - if (shouldHighlight) { - anchorsToHighlight[i].classList.add('current-heading'); - } else { - anchorsToHighlight[i].classList.remove('current-heading'); - } - } - } - } -}; - -module.exports = setupPageShow; \ No newline at end of file diff --git a/resources/views/pages/show.blade.php b/resources/views/pages/show.blade.php index a6c4f329d..dabc7b965 100644 --- a/resources/views/pages/show.blade.php +++ b/resources/views/pages/show.blade.php @@ -126,7 +126,7 @@ @stop @section('body') -
+
@@ -145,9 +145,3 @@
@endif @stop - -@section('scripts') - -@stop