From 5a4adcce5307710de1d5dbfbc00277096d790d0e Mon Sep 17 00:00:00 2001 From: Sorin Davidoi Date: Fri, 12 Jan 2018 19:44:13 +0100 Subject: [PATCH] refactor(theme): Remove jQuery --- src/theme/book.css | 30 +++ src/theme/book.js | 366 +++++++++++++++++------------ src/theme/index.hbs | 30 ++- src/theme/playpen_editor/editor.js | 4 +- src/theme/stylus/themes/base.styl | 6 + 5 files changed, 263 insertions(+), 173 deletions(-) diff --git a/src/theme/book.css b/src/theme/book.css index 755f7939..1a794d9b 100644 --- a/src/theme/book.css +++ b/src/theme/book.css @@ -431,6 +431,12 @@ table thead td { .light pre > .buttons i { margin-left: 8px; } +.light pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} .light pre > .result { margin-top: 10px; } @@ -567,6 +573,12 @@ table thead td { .coal pre > .buttons i { margin-left: 8px; } +.coal pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} .coal pre > .result { margin-top: 10px; } @@ -703,6 +715,12 @@ table thead td { .navy pre > .buttons i { margin-left: 8px; } +.navy pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} .navy pre > .result { margin-top: 10px; } @@ -839,6 +857,12 @@ table thead td { .rust pre > .buttons i { margin-left: 8px; } +.rust pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} .rust pre > .result { margin-top: 10px; } @@ -975,6 +999,12 @@ table thead td { .ayu pre > .buttons i { margin-left: 8px; } +.ayu pre > .buttons button { + color: inherit; + background: transparent; + border: none; + cursor: inherit; +} .ayu pre > .result { margin-top: 10px; } diff --git a/src/theme/book.js b/src/theme/book.js index cfacb88a..2f0e5894 100644 --- a/src/theme/book.js +++ b/src/theme/book.js @@ -1,4 +1,6 @@ -$( document ).ready(function() { +var html, sidebar, sidebarLinks, sidebarToggleButton; + +document.addEventListener('DOMContentLoaded', function() { // url var url = window.location.pathname; @@ -21,20 +23,24 @@ $( document ).ready(function() { if (window.ace) { // language-rust class needs to be removed for editable // blocks or highlightjs will capture events - $('code.editable').removeClass('language-rust'); + Array + .from(document.querySelectorAll('code.editable')) + .forEach(function(block) { block.classList.remove('language-rust'); }); - $('code').not('.editable').each(function(i, block) { - hljs.highlightBlock(block); - }); + Array + .from(document.querySelectorAll('code:not(.editable)')) + .forEach(function(block) { hljs.highlightBlock(block); }); } else { - $('code').each(function(i, block) { - hljs.highlightBlock(block); - }); + Array + .from(document.querySelectorAll('code')) + .forEach(function(block) { hljs.highlightBlock(block); }); } // Adding the hljs class gives code blocks the color css // even if highlighting doesn't apply - $('code').addClass('hljs'); + Array + .from(document.querySelectorAll('code')) + .forEach(function(block) { block.classList.add('hljs'); }); var KEY_CODES = { PREVIOUS_KEY: 37, @@ -42,19 +48,21 @@ $( document ).ready(function() { ESCAPE_KEY: 27, }; - $(document).on('keydown', function (e) { + document.addEventListener('keydown', function (e) { if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } switch (e.keyCode) { case KEY_CODES.NEXT_KEY: e.preventDefault(); - if($('.nav-chapters.next').length) { - window.location.href = $('.nav-chapters.next').attr('href'); + var nextButton = document.querySelector('.nav-chapters.next'); + if(nextButton) { + window.location.href = nextButton.href; } break; case KEY_CODES.PREVIOUS_KEY: e.preventDefault(); - if($('.nav-chapters.previous').length) { - window.location.href = $('.nav-chapters.previous').attr('href'); + var previousButton = document.querySelector('.nav-chapters.previous'); + if(previousButton) { + window.location.href = previousButton.href; } break; case KEY_CODES.ESCAPE_KEY: @@ -65,31 +73,36 @@ $( document ).ready(function() { }); // Interesting DOM Elements - var sidebar = $("#sidebar"); + html = document.querySelector('html'); + sidebar = document.getElementById("sidebar"); + sidebarLinks = document.querySelectorAll('#sidebar a'); + sidebarToggleButton = document.getElementById("sidebar-toggle"); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); // Toggle sidebar - $("#sidebar-toggle").click(sidebarToggle); + sidebarToggleButton.addEventListener('click', sidebarToggle); // Scroll sidebar to current active section - var activeSection = sidebar.find(".active"); - if(activeSection.length) { - sidebar.scrollTop(activeSection.offset().top); + var activeSection = sidebar.querySelector(".active"); + if(activeSection) { + sidebar.scrollTop = activeSection.offsetTop; } var firstContact = null; - $(this).on('touchstart', function(e) { + document.addEventListener('touchstart', function(e) { firstContact = { - x: e.originalEvent.touches[0].clientX, + x: e.touches[0].clientX, time: Date.now() }; }); - $(this).on('touchmove', function(e) { + document.addEventListener('touchmove', function(e) { if (!firstContact) return; - var curX = e.originalEvent.touches[0].clientX; + var curX = e.touches[0].clientX; var xDiff = curX - firstContact.x, tDiff = Date.now() - firstContact.time; @@ -104,37 +117,33 @@ $( document ).ready(function() { }); function showThemes() { - $('.theme-popup').css('display', 'block'); - $('#theme-toggle').attr('aria-expanded', true); + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); } function hideThemes() { - $('.theme-popup').css('display', 'none'); - $('#theme-toggle').attr('aria-expanded', false); + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); } // Theme button - $("#theme-toggle").click(function(){ - if ($('.theme-popup').css('display') === 'block') { + themeToggleButton.addEventListener('click', function(){ + if (themePopup.style.display === 'block') { hideThemes(); } else { showThemes(); } }); - $('.theme').click(function(){ - var theme = $(this).attr('id'); + themePopup.addEventListener('click', function(e){ + var theme = e.target.id || e.target.parentElement.id; set_theme(theme); }); // Hide theme selector popup when clicking outside of it - $(document).click(function(event){ - var popup = $('.theme-popup'); - if(popup.css('display') === 'block') { - var target = $(event.target); - if(!target.closest('.theme').length && !target.closest('#theme-toggle').length) { - hideThemes(); - } + document.addEventListener('click', function(event){ + if(themePopup.style.display === 'block' && !themeToggleButton.contains(event.target) && !themePopup.contains(event.target)) { + hideThemes(); } }); @@ -142,21 +151,21 @@ $( document ).ready(function() { let ace_theme; if (theme == 'coal' || theme == 'navy') { - $("[href='ayu-highlight.css']").prop('disabled', true); - $("[href='tomorrow-night.css']").prop('disabled', false); - $("[href='highlight.css']").prop('disabled', true); + document.querySelector("[href='ayu-highlight.css']").setAttribute('disabled', true); + document.querySelector("[href='tomorrow-night.css']").removeAttribute('disabled'); + document.querySelector("[href='highlight.css']").setAttribute('disabled', true); ace_theme = "ace/theme/tomorrow_night"; } else if (theme == 'ayu') { - $("[href='ayu-highlight.css']").prop('disabled', false); - $("[href='tomorrow-night.css']").prop('disabled', true); - $("[href='highlight.css']").prop('disabled', true); + document.querySelector("[href='ayu-highlight.css']").removeAttribute('disabled'); + document.querySelector("[href='tomorrow-night.css']").setAttribute('disabled', true); + document.querySelector("[href='highlight.css']").setAttribute('disabled', true); ace_theme = "ace/theme/tomorrow_night"; } else { - $("[href='ayu-highlight.css']").prop('disabled', true); - $("[href='tomorrow-night.css']").prop('disabled', true); - $("[href='highlight.css']").prop('disabled', false); + document.querySelector("[href='ayu-highlight.css']").setAttribute('disabled', true); + document.querySelector("[href='tomorrow-night.css']").setAttribute('disabled', true); + document.querySelector("[href='highlight.css']").removeAttribute('disabled'); ace_theme = "ace/theme/dawn"; } @@ -173,24 +182,24 @@ $( document ).ready(function() { store.set('mdbook-theme', theme); - $('body').removeClass().addClass(theme); + document.body.className = theme; } // Hide Rust code lines prepended with a specific character var hiding_character = "#"; - $("code.language-rust").each(function(i, block){ + Array.from(document.querySelectorAll("code.language-rust")).forEach(function(block){ - var code_block = $(this); - var pre_block = $(this).parent(); + var code_block = block; + var pre_block = block.parentNode; // hide lines - var lines = code_block.html().split("\n"); + var lines = code_block.innerHTML.split("\n"); var first_non_hidden_line = false; var lines_hidden = false; for(var n = 0; n < lines.length; n++){ - if($.trim(lines[n])[0] == hiding_character){ + if(lines[n].trim()[0] == hiding_character){ if(first_non_hidden_line){ lines[n] = "" + "\n" + lines[n].replace(/(\s*)# ?/, "$1") + ""; } @@ -206,78 +215,117 @@ $( document ).ready(function() { first_non_hidden_line = true; } } - code_block.html(lines.join("")); + code_block.innerHTML = lines.join(""); // If no lines were hidden, return if(!lines_hidden) { return; } - // add expand button - pre_block.prepend("
"); + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; - pre_block.find("i").click(function(e){ - if( $(this).hasClass("fa-expand") ) { - $(this).removeClass("fa-expand").addClass("fa-compress"); - $(this).attr("title", "Hide lines"); - pre_block.find("span.hidden").removeClass("hidden").addClass("unhidden"); - } - else { - $(this).removeClass("fa-compress").addClass("fa-expand"); - $(this).attr("title", "Show hidden lines"); - pre_block.find("span.unhidden").removeClass("unhidden").addClass("hidden"); + // add expand button + pre_block.prepend(buttons); + + pre_block.querySelector('.buttons').addEventListener('click', function(e) { + if (e.target.classList.contains('fa-expand')) { + var lines = pre_block.querySelectorAll('span.hidden'); + + e.target.classList.remove('fa-expand'); + e.target.classList.add('fa-compress'); + e.target.title = 'Hide lines'; + + Array.from(lines).forEach(function(line) { + line.classList.remove('hidden'); + line.classList.add('unhidden'); + }); + } else if (e.target.classList.contains('fa-compress')) { + var lines = pre_block.querySelectorAll('span.unhidden'); + + e.target.classList.remove('fa-compress'); + e.target.classList.add('fa-expand'); + e.target.title = 'Show hidden lines'; + + Array.from(lines).forEach(function(line) { + line.classList.remove('unhidden'); + line.classList.add('hidden'); + }); } }); }); - - $("pre code").each(function(i, block){ - var pre_block = $(this).parent(); - if( !pre_block.hasClass('playpen') ) { - var buttons = pre_block.find(".buttons"); - if(buttons.length == 0) { - pre_block.prepend("
"); - buttons = pre_block.find(".buttons"); + + Array.from(document.querySelectorAll('pre code')).forEach(function(block) { + var pre_block = block.parentNode; + if( !pre_block.classList.contains('playpen') ) { + var buttons = pre_block.querySelector(".buttons"); + if(!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.prepend(buttons); } - buttons.prepend(""); - buttons.find(".clip-button").mouseout(function(e){ + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.innerHTML = ''; + + buttons.prepend(clipButton); + clipButton.addEventListener('mouseout', function(e){ hideTooltip(e.currentTarget); }); } }); // Process playpen code blocks - $(".playpen").each(function(block){ - var pre_block = $(this); + Array.from(document.querySelectorAll(".playpen")).forEach(function(pre_block){ // Add play button - var buttons = pre_block.find(".buttons"); - if( buttons.length === 0 ) { - pre_block.prepend("
"); - buttons = pre_block.find(".buttons"); - } - buttons.prepend(""); - buttons.prepend(""); - - let code_block = pre_block.find("code").first(); - if (window.ace && code_block.hasClass("editable")) { - buttons.prepend(""); + var buttons = pre_block.querySelector(".buttons"); + if(!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.prepend(buttons); } - buttons.find(".play-button").click(function(e){ + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + + buttons.prepend(runCodeButton); + buttons.prepend(copyCodeClipboardButton); + + runCodeButton.addEventListener('click', function(e){ run_rust_code(pre_block); }); - buttons.find(".clip-button").mouseout(function(e){ + copyCodeClipboardButton.addEventListener('mouseout', function(e){ hideTooltip(e.currentTarget); }); - buttons.find(".reset-button").click(function() { - if (!window.ace) { return; } - let editor = window.ace.edit(code_block.get(0)); - editor.setValue(editor.originalCode); - editor.clearSelection(); - }); + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + + buttons.prepend(undoChangesButton); + + undoChangesButton.addEventListener('click', function() { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } }); var clipboardSnippets = new Clipboard('.clip-button', { text: function(trigger) { hideTooltip(trigger); - let playpen = $(trigger).parents("pre"); + let playpen = trigger.closest("pre"); return playpen_text(playpen); } }); @@ -289,31 +337,34 @@ $( document ).ready(function() { showTooltip(e.trigger, "Clipboard error!"); }); - $.ajax({ - url: "https://play.rust-lang.org/meta/crates", - method: "POST", - crossDomain: true, - dataType: "json", - contentType: "application/json", - success: function(response){ + var request = fetch("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }); + + request + .then(function(response) { return response.json(); }) + .then(function(response) { // get list of crates available in the rust playground let playground_crates = response.crates.map(function(item) {return item["id"];} ); - $(".playpen").each(function(block) { - handle_crate_list_update($(this), playground_crates); + Array.from(document.querySelectorAll(".playpen")).forEach(function(block) { + handle_crate_list_update(block, playground_crates); }); - }, - }); + }); }); function playpen_text(playpen) { - let code_block = playpen.find("code").first(); + let code_block = playpen.querySelector("code"); - if (window.ace && code_block.hasClass("editable")) { - let editor = window.ace.edit(code_block.get(0)); + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); return editor.getValue(); } else { - return code_block.get(0).textContent; + return code_block.textContent; } } @@ -323,10 +374,10 @@ function handle_crate_list_update(playpen_block, playground_crates) { // and install on change listener to dynamically update ACE editors if (window.ace) { - let code_block = playpen_block.find("code").first(); - if (code_block.hasClass("editable")) { - let editor = window.ace.edit(code_block.get(0)); - editor.on("change", function(e){ + let code_block = playpen_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function(e){ update_play_button(playpen_block, playground_crates); }); } @@ -336,12 +387,11 @@ function handle_crate_list_update(playpen_block, playground_crates) { // updates the visibility of play button based on `no_run` class and // used crates vs ones available on http://play.rust-lang.org function update_play_button(pre_block, playground_crates) { - var play_button = pre_block.find(".play-button"); + var play_button = pre_block.querySelector(".play-button"); - var classes = pre_block.find("code").attr("class").split(" "); // skip if code is `no_run` - if (classes.indexOf("no_run") > -1) { - play_button.addClass("hidden"); + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); return; } @@ -359,30 +409,30 @@ function update_play_button(pre_block, playground_crates) { }); if (all_available) { - play_button.removeClass("hidden"); + play_button.classList.remove("hidden"); } else { - play_button.addClass("hidden"); + play_button.classList.add("hidden"); } } function hideTooltip(elem) { elem.firstChild.innerText=""; - elem.setAttribute('class', 'fa fa-copy clip-button'); + elem.className = 'fa fa-copy clip-button'; } function showTooltip(elem, msg) { elem.firstChild.innerText=msg; - elem.setAttribute('class', 'fa fa-copy tooltipped'); + elem.className = 'fa fa-copy tooltipped'; } function sidebarToggle() { - var html = $("html"); - if (html.hasClass("sidebar-hidden")) { + var html = document.querySelector("html"); + if (html.classList.contains("sidebar-hidden")) { showSidebar(); - } else if (html.hasClass("sidebar-visible")) { + } else if (html.classList.contains("sidebar-visible")) { hideSidebar(); } else { - if (getComputedStyle($('#sidebar')[0])['transform'] === 'none'){ + if (getComputedStyle(sidebar)['transform'] === 'none'){ hideSidebar(); } else { showSidebar(); @@ -391,26 +441,34 @@ function sidebarToggle() { } function showSidebar() { - $('html').removeClass('sidebar-hidden').addClass('sidebar-visible'); - $('#sidebar a').attr('tabIndex', 0); - $('#sidebar-toggle').attr('aria-expanded', true); - $('#sidebar').attr('aria-hidden', false); + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function(link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); store.set('mdbook-sidebar', 'visible'); } function hideSidebar() { - $('html').removeClass('sidebar-visible').addClass('sidebar-hidden'); - $('#sidebar a').attr('tabIndex', -1); - $('#sidebar-toggle').attr('aria-expanded', false); - $('#sidebar').attr('aria-hidden', true); + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function(link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); store.set('mdbook-sidebar', 'hidden'); } function run_rust_code(code_block) { - var result_block = code_block.find(".result"); - if(result_block.length === 0) { - code_block.append(""); - result_block = code_block.find(".result"); + var result_block = code_block.querySelector(".result"); + if(!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); } let text = playpen_text(code_block); @@ -427,21 +485,19 @@ function run_rust_code(code_block) { params.channel = "nightly"; } - result_block.text("Running..."); + result_block.innerText = "Running..."; - $.ajax({ - url: "https://play.rust-lang.org/execute", - method: "POST", - crossDomain: true, - dataType: "json", - contentType: "application/json", - data: JSON.stringify(params), - timeout: 15000, - success: function(response){ - result_block.text(response.success ? response.stdout : response.stderr); - }, - error: function(qXHR, textStatus, errorThrown){ - result_block.text("Playground communication " + textStatus); + var request = fetch("https://play.rust-lang.org/execute", { + headers: { + 'Content-Type': "application/json", }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) }); + + request + .then(function(response) { return response.json(); }) + .then(function(response) { result_block.innerText = response.success ? response.stdout : response.stderr; }) + .catch(function(error) { result_block.innerText = "Playground communication" + error.message; }); } diff --git a/src/theme/index.hbs b/src/theme/index.hbs index c5db607c..53b8ef71 100644 --- a/src/theme/index.hbs +++ b/src/theme/index.hbs @@ -41,14 +41,6 @@ } - - - - @@ -58,7 +50,7 @@ @@ -66,7 +58,7 @@ var sidebar = 'hidden'; if (document.body.clientWidth >= 1080) sidebar = store.get('mdbook-sidebar') || 'visible'; - $("html").addClass("sidebar-" + sidebar); + document.querySelector('html').classList.add("sidebar-" + sidebar);