~sircmpwn/public-inbox

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH gddo v2] Remove dependency on jQuery and Bootstrap.js

Details
Message ID
<20210112142940.26892-1-me@adnano.co>
DKIM signature
pass
Download raw message
Patch: +298 -220
Replace jQuery and Bootstrap JavaScript with plain JS and CSS.
---
This patch keeps all existing functionality intact, including keyboard
shortcuts and jumping to identifiers, as well as modals and the
collapsible navbar.
I would have also deleted gddo-server/assets/bootstrap.min.js and
jquery-2.0.3.min.js if it wouldn't have made for such a large patch.

 gddo-server/assets/site.css              |  50 ++-
 gddo-server/assets/site.js               | 453 ++++++++++++-----------
 gddo-server/assets/templates/layout.html |   8 +-
 gddo-server/assets/templates/pkg.html    |   4 +-
 gddo-server/main.go                      |   3 -
 5 files changed, 298 insertions(+), 220 deletions(-)

diff --git a/gddo-server/assets/site.css b/gddo-server/assets/site.css
index 37a6820..30fc093 100644
--- a/gddo-server/assets/site.css
+++ b/gddo-server/assets/site.css
@@ -1,4 +1,8 @@
html { background-color: whitesmoke; }
html {
    background-color: whitesmoke;
    /* for smooth scrolling transitions without extra JS */
    scroll-behavior: smooth;
}
body { background-color: white; }
h4 { margin-top: 20px; }
.container { max-width: 728px; }
@@ -18,7 +22,6 @@ h4 { margin-top: 20px; }
    background-color: #eee;
    border-top-style: solid;
    border-top-width: 1px;

}

:target {
@@ -147,7 +150,7 @@ summary:hover .permalink {
}

.synopsis {
  opacity: 0.87;
    opacity: 0.87;
}

.additional-info {
@@ -158,5 +161,44 @@ summary:hover .permalink {
}

.click-select {
	user-select: all;
    user-select: all;
}

.modal {
    display: none;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 1000;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.5);
    opacity: 0;
}

.modal.show {
    display: block;
    opacity: 1;
}

.navbar-collapse {
    display: none;
    animation: slide-in 1s linear;
}

@keyframes slide-in {
    from {
        max-height: 0;
        overflow: hidden;
    }
    to {
        max-height: 1000px;
    }
}

@media (min-width: 768px) {
    .navbar-collapse {
        display: block;
    }
}
diff --git a/gddo-server/assets/site.js b/gddo-server/assets/site.js
index 99b4a6a..3662501 100644
--- a/gddo-server/assets/site.js
+++ b/gddo-server/assets/site.js
@@ -1,209 +1,250 @@
// modal
function Modal(el) {
	if (el == null) {
		return null
	}

	this.el = el
	el.querySelector(".close").onclick = () => this.hide()
	el.onclick = () => this.hide()
	el.onkeydown = (e) => {
		if (e.key == "Escape") {
			this.hide()
		}
	}
	el.querySelector(".modal-dialog").onclick = function(e) {
		e.stopPropagation()
	}
}

Modal.prototype.show = function() {
	this.el.classList.add("show")
	this.el.focus()
}

Modal.prototype.hide = function() {
	this.el.classList.remove("show")
}

// jump modal
$(function() {

    var all;
    var visible;
    var active = -1;
    var lastFilter = '';
    var $body = $('#x-jump-body');
    var $list = $('#x-jump-list');
    var $filter = $('#x-jump-filter');
    var $modal = $('#x-jump');

    var update = function(filter) {
        lastFilter = filter;
        if (active >= 0) {
            visible[active].e.removeClass('active');
            active = -1;
        }
        visible = []
        var re = new RegExp(filter.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), "gi");
        all.forEach(function (id) {
            id.e.detach();
            var text = id.text;
            if (filter) {
                text = id.text.replace(re, function (s) { return '<b>' + s + '</b>'; });
                if (text == id.text) {
                    return
                }
            }
            id.e.html(text + ' ' + '<i>' + id.kind + '</i>');
            visible.push(id);
        });
        $body.scrollTop(0);
        if (visible.length > 0) {
            active = 0;
            visible[active].e.addClass('active');
        }
        $list.append($.map(visible, function(identifier) { return identifier.e; }));
    }

    var incrActive = function(delta) {
        if (visible.length == 0) {
            return
        }
        visible[active].e.removeClass('active');
        active += delta;
        if (active < 0) {
            active = 0;
            $body.scrollTop(0);
        } else if (active >= visible.length) {
            active = visible.length - 1;
            $body.scrollTop($body[0].scrollHeight - $body[0].clientHeight);
        } else {
            var $e = visible[active].e;
            var t = $e.position().top;
            var b = t + $e.outerHeight(false);
            if (t <= 0) {
                $body.scrollTop($body.scrollTop() + t);
            } else if (b >= $body.outerHeight(false)) {
                $body.scrollTop($body.scrollTop() + b - $body.outerHeight(false));
            }
        }
        visible[active].e.addClass('active');
    }

    $modal.on('show.bs.modal', function() {
        if (!all) {
            all = []
            var kinds = {'c': 'constant', 'v': 'variable', 'f': 'function', 't': 'type', 'd': 'field', 'm': 'method'}
            $('*[id]').each(function() {
                var e = $(this);
                var id = e.attr('id');
                if (/^[^_][^-]*$/.test(id)) {
                    all.push({
                        text: id,
                        ltext: id.toLowerCase(),
                        kind: kinds[e.closest('[data-kind]').attr('data-kind')],
                        e: $('<a/>', {href: '#' + id, 'class': 'list-group-item', tabindex: '-1'})
                    });
                }
            });
            all.sort(function (a, b) {
                if (a.ltext > b.ltext) { return 1; }
                if (a.ltext < b.ltext) { return -1; }
                return 0
            });
        }
    }).on('shown.bs.modal', function() {
        update('');
        $filter.val('').focus();
    }).on('hide.bs.modal', function() {
        $filter.blur();
    }).on('click', '.list-group-item', function() {
        $modal.modal('hide');
    });

    $filter.on('change keyup', function() {
        var filter = $filter.val();
        if (filter.toUpperCase() != lastFilter.toUpperCase()) {
            update(filter);
        }
    }).on('keydown', function(e) {
        switch(e.which) {
        case 38: // up
            incrActive(-1);
            e.preventDefault();
            break;
        case 40: // down
            incrActive(1);
            e.preventDefault();
            break;
        case 13: // enter
            if (active >= 0) {
                visible[active].e[0].click();
            }
            break
        }
    });

});
function JumpModal(el) {
	if (el == null) {
		return null
	}

	this.all = []
	this.visible = []
	this.active = -1
	this.lastFilter = ""
	this.modal = new Modal(el)
	this.body = el.querySelector("#x-jump-body")
	this.list = el.querySelector("#x-jump-list")
	this.filter = el.querySelector("#x-jump-filter")

	this.filter.oninput = e => {
		var filter = e.target.value
		if (filter.toLowerCase() != this.lastFilter.toLowerCase()) {
			this.update(filter)
		}
	}

	this.filter.onkeydown = e => {
		switch(e.key) {
		case "ArrowUp":
			this.incrActive(-1)
			e.preventDefault()
			break
		case "ArrowDown":
			this.incrActive(1)
			e.preventDefault()
			break
		case "Enter":
			if (this.active >= 0) {
				this.visible[this.active].el.click()
			}
			break
		}
	}
}

JumpModal.prototype.update = function(filter) {
	this.lastFilter = filter
	if (this.active >= 0) {
		this.visible[this.active].el.classList.remove("active")
		this.active = -1
	}

	// Update visible elements
	this.visible = []
	var re = new RegExp(filter.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"), "gi")
	this.visible = this.all.filter((id) => {
		// Detatch element
		if (id.el.parentElement != null) {
			id.el.parentElement.removeChild(id.el)
		}

		var text = id.text
		if (filter.length > 0) {
			text = id.text.replace(re, function(s) {
				return "<b>" + s + "</b>"
			})
			if (text == id.text) {
				return false
			}
		}

		id.el.innerHTML = text + " " + "<i>" + id.kind + "</i>"
		return true
	})

	this.body.scrollTop = 0
	if (this.visible.length > 0) {
		this.active = 0
		this.visible[this.active].el.classList.add("active")
	}

	for (var i = 0; i < this.visible.length; i++) {
		this.list.appendChild(this.visible[i].el)
	}
}

JumpModal.prototype.incrActive = function(delta) {
	if (this.visible.length == 0) {
		return
	}

	this.visible[this.active].el.classList.remove("active")

	this.active += delta
	if (this.active < 0) {
		this.active = 0
	} else if (this.active >= this.visible.length) {
		this.active = this.visible.length - 1
	}

	var el = this.visible[this.active].el
	el.scrollIntoView({
		block: "nearest",
	})
	el.classList.add("active")
}

JumpModal.prototype.show = function() {
	if (this.all.length == 0) {
		document.querySelectorAll("[id]").forEach(e => {
			var id = e.id
			if (/^[^_][^-]*$/.test(id)) {
				var el = document.createElement("a")
				el.href = "#" + id
				el.classList.add("list-group-item")
				el.tabindex = -1
				el.onclick = () => {
					this.hide()
				}

				this.all.push({
					text: id,
					ltext: id.toLowerCase(),
					kind: e.closest("[data-kind]").dataset.kind,
					el: el,
				})
			}
		})

		this.all.sort(function(a, b) {
			if (a.ltext > b.ltext) {
				return 1
			}
			if (a.ltext < b.ltext) {
				return -1
			}
			return 0
		})
	}

	this.update("")
	this.modal.show()
	this.filter.value = ""
	this.filter.focus()
}

// navbar toggle
var navToggle = document.querySelector(".navbar-toggle")
navToggle.onclick = function() {
	document.querySelector(".navbar-collapse").classList.toggle("show")
}

// keyboard shortcuts
$(function() {
    var prevCh = null, prevTime = 0, modal = false;

    $('.modal').on({
        show: function() { modal = true; },
        hidden: function() { modal = false; }
    });

    $(document).on('keypress', function(e) {
        var combo = e.timeStamp - prevTime <= 1000;
        prevTime = 0;

        if (modal) {
            return true;
        }

        var t = e.target.tagName
        if (t == 'INPUT' ||
            t == 'SELECT' ||
            t == 'TEXTAREA' ) {
            return true;
        }

        if (e.target.contentEditable && e.target.contentEditable == 'true') {
            return true;
        }

        if (e.metaKey || e.ctrlKey) {
            return true;
        }

        var ch = String.fromCharCode(e.which);

        if (combo) {
            switch (prevCh + ch) {
            case "gg":
                $('html,body').animate({scrollTop: 0},'fast');
                return false;
            case "gb":
                $('html,body').animate({scrollTop: $(document).height()},'fast');
                return false;
            case "gi":
                if ($('#pkg-index').length > 0) {
                    $('html,body').animate({scrollTop: $("#pkg-index").offset().top},'fast');
                    return false;
                }
            case "ge":
                if ($('#pkg-examples').length > 0) {
                    $('html,body').animate({scrollTop: $("#pkg-examples").offset().top},'fast');
                    return false;
                }
            }
        }

        switch (ch) {
        case "/":
            $('#x-search-query').focus();
            return false;
        case "?":
            $('#x-shortcuts').modal();
            return false;
        case  "f":
            if ($('#x-jump').length > 0) {
                $('#x-jump').modal();
                return false;
            }
        }

        prevCh = ch
        prevTime = e.timeStamp
        return true;
    });
});

// misc
$(function() {
    var hash = window.location.hash;

    if (hash.startsWith('#example-')) {
        $(hash).parent().attr('open', '');
    }

    $('body').scrollspy({
        target: '.gddo-sidebar',
        offset: 10
    });
});
var search = document.querySelector("#x-search-query")
var shortcuts = new Modal(document.querySelector("#x-shortcuts"))
var jump = new JumpModal(document.querySelector("#x-jump"))
var prevCh = null
var prevTime = 0

document.onkeydown = function(e) {
	var combo = e.timeStamp - prevTime <= 1000
	prevTime = 0

	if (e.target != document.body) {
		return
	}
	if (e.metaKey || e.ctrlKey) {
		return true
	}

	var ch = e.key
	if (combo) {
		switch (prevCh + ch) {
		case "gg":
			window.scrollTo(0, 0)
			return false
		case "gb":
			window.scrollTo(0, document.body.scrollHeight)
			return false
		case "gi":
			var pkgIndex = document.querySelector("#pkg-index")
			if (pkgIndex != null) {
				pkgIndex.scrollIntoView()
				return false
			}
		case "ge":
			var pkgExamples = document.querySelector("#pkg-examples")
			if (pkgExamples != null) {
				pkgExamples.scrollIntoView()
				return false
			}
		}
	}

	switch (ch) {
	case "/":
		if (search != null) {
			search.focus()
		}
		return false
	case "?":
		if (shortcuts != null) {
			shortcuts.show()
		}
		return false
	case "f":
		if (jump != null) {
			jump.show()
			return false
		}
	}

	prevCh = ch
	prevTime = e.timeStamp
	return true
}

function onhashchanged() {
	// open selected example
	var hash = window.location.hash
	if (hash.startsWith("#example-")) {
		document.querySelector(hash).parentElement.setAttribute("open", "")
	}
}
window.onhashchanged = onhashchanged
onhashchanged()
diff --git a/gddo-server/assets/templates/layout.html b/gddo-server/assets/templates/layout.html
index ff25e5a..8e6aece 100644
--- a/gddo-server/assets/templates/layout.html
+++ b/gddo-server/assets/templates/layout.html
@@ -10,7 +10,7 @@
<nav class="navbar navbar-default" role="navigation">
  <div class="container">
  <div class="navbar-header">
    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
    <button type="button" class="navbar-toggle">
      <span class="sr-only">Toggle navigation</span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
@@ -18,7 +18,7 @@
    </button>
    <a class="navbar-brand" href="/"><strong>godocs.io</strong></a>
  </div>
  <div class="collapse navbar-collapse">
  <div class="navbar-collapse">
    <ul class="nav navbar-nav">
        <li{{if equal "home.html" templateName}} class="active"{{end}}><a href="/">Home</a></li>
        <li{{if equal "about.html" templateName}} class="active"{{end}}><a href="/-/about">About</a></li>
@@ -43,7 +43,7 @@
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
          <button type="button" class="close" aria-hidden="true">&times;</button>
          <h4 class="modal-title">Keyboard shortcuts</h4>
        </div>
        <div class="modal-body">
@@ -58,7 +58,7 @@
          </table>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn" data-dismiss="modal">Close</button>
          <button type="button" class="btn">Close</button>
      </div>
    </div>
  </div>
diff --git a/gddo-server/assets/templates/pkg.html b/gddo-server/assets/templates/pkg.html
index 2242f69..32d435d 100644
--- a/gddo-server/assets/templates/pkg.html
+++ b/gddo-server/assets/templates/pkg.html
@@ -142,6 +142,7 @@
            <div class="modal-dialog">
            <div class="modal-content">
              <div class="modal-header">
                <button type="button" class="close" aria-hidden="true">&times;</button>
                <h4 class="modal-title">Jump to identifier</h4>
                <br class="clearfix">
                <input id="x-jump-filter" class="form-control" autocomplete="off" type="text">
@@ -149,9 +150,6 @@
              <div id="x-jump-body" class="modal-body" style="height: 260px; overflow: auto;">
                <div id="x-jump-list" class="list-group" style="margin-bottom: 0;"></div>
              </div>
              <div class="modal-footer">
                <button type="button" class="btn" data-dismiss="modal">Close</button>
            </div>
          </div>
        </div>
      </div>
diff --git a/gddo-server/main.go b/gddo-server/main.go
index 2b5ec88..d59c9e4 100644
--- a/gddo-server/main.go
+++ b/gddo-server/main.go
@@ -870,8 +870,6 @@ func newServer(ctx context.Context, v *viper.Viper) (*server, error) {
	mux.Handle("/-/site.js", staticServer.FilesHandler("site.js"))
	mux.Handle("/-/site.css", staticServer.FilesHandler("site.css"))
	mux.Handle("/-/bootstrap.min.css", staticServer.FilesHandler("bootstrap.min.css"))
	mux.Handle("/-/bootstrap.min.js", staticServer.FilesHandler("bootstrap.min.js"))
	mux.Handle("/-/jquery-2.0.3.min.js", staticServer.FilesHandler("jquery-2.0.3.min.js"))
	if s.v.GetBool(ConfigSidebar) {
		mux.Handle("/-/sidebar.css", staticServer.FilesHandler("sidebar.css"))
	}
@@ -893,7 +891,6 @@ func newServer(ctx context.Context, v *viper.Viper) (*server, error) {
	mux.Handle("/-/refresh", handler(s.serveRefresh))
	mux.Handle("/about", http.RedirectHandler("/-/about", http.StatusMovedPermanently))
	mux.Handle("/favicon.ico", staticServer.FileHandler("favicon.ico"))
	mux.Handle("/humans.txt", staticServer.FileHandler("humans.txt"))
	mux.Handle("/robots.txt", staticServer.FileHandler("robots.txt"))
	mux.Handle("/C", http.RedirectHandler("http://golang.org/doc/articles/c_go_cgo.html", http.StatusMovedPermanently))
	mux.Handle("/code.jquery.com/", http.NotFoundHandler())
-- 
2.30.0
Details
Message ID
<C8I3IC01TLGT.25FVGQJD6H21T@taiga>
In-Reply-To
<20210112142940.26892-1-me@adnano.co> (view parent)
DKIM signature
fail
Download raw message
DKIM signature: fail
Also does not apply.
Reply to thread Export thread (mbox)