From ac418afd3d8dd24caf9e49772153ba5b0648ab16 Mon Sep 17 00:00:00 2001 From: Oscar Cortez Date: Sun, 26 Jul 2015 22:31:28 -0600 Subject: [PATCH 1/2] Improve blog search Small vanilla js lib for search using only the feed.xml --- _includes/header.html | 3 +- _layouts/default.html | 11 +++- js/search.js | 139 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 js/search.js diff --git a/_includes/header.html b/_includes/header.html index fb6b5ffe..3d2c895f 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -18,8 +18,9 @@ -
+
diff --git a/_layouts/default.html b/_layouts/default.html index 9dae3c69..05c5b168 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -25,8 +25,17 @@ {% if site.pagination_type == 'ops' %} - + {% endif %} + + diff --git a/js/search.js b/js/search.js new file mode 100644 index 00000000..75c7da15 --- /dev/null +++ b/js/search.js @@ -0,0 +1,139 @@ +(function() { + var searchFile = '/feed.xml', + searchEl, + searchInputEl, + searchResultsEl, + currentInputValue = '', + lastSearchResultHash, + posts = []; + + // Changes XML to JSON + // Modified version from here: http://davidwalsh.name/convert-xml-json + function xmlToJson(xml) { + // Create the return object + var obj = {}; + if (xml.nodeType == 3) { // text + obj = xml.nodeValue; + } + + // do children + // If all text nodes inside, get concatenated text from them. + var textNodes = [].slice.call(xml.childNodes).filter(function(node) { + return node.nodeType === 3; + }); + if (xml.hasChildNodes() && xml.childNodes.length === textNodes.length) { + obj = [].slice.call(xml.childNodes).reduce(function(text, node) { + return text + node.nodeValue; + }, ''); + } else if (xml.hasChildNodes()) { + for (var i = 0; i < xml.childNodes.length; i++) { + var item = xml.childNodes.item(i); + var nodeName = item.nodeName; + if (typeof(obj[nodeName]) == "undefined") { + obj[nodeName] = xmlToJson(item); + } else { + if (typeof(obj[nodeName].push) == "undefined") { + var old = obj[nodeName]; + obj[nodeName] = []; + obj[nodeName].push(old); + } + obj[nodeName].push(xmlToJson(item)); + } + } + } + return obj; + } + + function getPostsFromXml(xml) { + var json = xmlToJson(xml); + return json.channel.item; + } + + window.toggleSearch = function toggleSearch() { + searchEl.classList.toggle('is-active'); + if (searchEl.classList.contains('is-active')) { + // while opening + searchInputEl.value = ''; + } else { + // while closing + searchResultsEl.classList.add('is-hidden'); + } + setTimeout(function() { + searchInputEl.focus(); + }, 210); + } + + function handleInput() { + var currentResultHash, d; + + currentInputValue = searchInputEl.value; + if (!currentInputValue || currentInputValue.length < 3) { + lastSearchResultHash = ''; + searchResultsEl.classList.add('is-hidden'); + return; + } + searchResultsEl.style.offsetWidth; + + var matchingPosts = posts.filter(function(post) { + if (post.title.indexOf(currentInputValue) !== -1 || post.description.indexOf(currentInputValue) !== -1) { + return true; + } + }); + if (!matchingPosts.length) { + searchResultsEl.classList.add('is-hidden'); + } + currentResultHash = matchingPosts.reduce(function(hash, post) { + return post.title + hash; + }, ''); + if (matchingPosts.length && currentResultHash !== lastSearchResultHash) { + searchResultsEl.classList.remove('is-hidden'); + searchResultsEl.innerHTML = matchingPosts.map(function(post) { + d = new Date(post.pubDate); + return '
  • ' + post.title + '' + d.toUTCString().replace(/.*(\d{2})\s+(\w{3})\s+(\d{4}).*/, '$2 $1, $3') + '
  • '; + }).join(''); + } + lastSearchResultHash = currentResultHash; + } + + function init(options) { + searchFile = options.searchFile || searchFile; + searchEl = document.querySelector(options.searchSelector || '#js-super-search'); + searchInputEl = document.querySelector(options.inputSelector || '#js-super-search__input'); + searchResultsEl = document.querySelector(options.resultsSelector || '#js-super-search__results'); + + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open('GET', searchFile); + xmlhttp.onreadystatechange = function() { + if (xmlhttp.readyState != 4) return; + if (xmlhttp.status != 200 && xmlhttp.status != 304) { + return; + } + var node = (new DOMParser).parseFromString(xmlhttp.responseText, 'text/xml'); + node = node.children[0]; + posts = getPostsFromXml(node); + } + xmlhttp.send(); + + // Toggle on ESC key + window.addEventListener('keyup', function onKeyPress(e) { + if (e.which === 27) { + toggleSearch(); + } + }); + // Open on '/' key + window.addEventListener('keypress', function onKeyPress(e) { + if (e.which === 47 && !searchEl.classList.contains('is-active')) { + toggleSearch(); + } + }); + + searchInputEl.addEventListener('input', function onInputChange() { + handleInput(); + }); + } + + init.toggle = toggleSearch; + + window.mdlSearch = init; + +})(); \ No newline at end of file From ecaadedbb098b4d5b706833c5f8a28a41c907e4f Mon Sep 17 00:00:00 2001 From: Oscar Cortez Date: Sun, 4 Oct 2015 18:46:07 -0600 Subject: [PATCH 2/2] JKM-2 search post now work thanks to chinchang - superSearch --- _includes/header.html | 21 +-- _includes/search_result.html | 8 ++ _layouts/default.html | 7 +- _sass/_search.scss | 57 +++++++++ css/main.scss | 3 +- js/search.js | 239 +++++++++++++++++------------------ 6 files changed, 197 insertions(+), 138 deletions(-) create mode 100644 _includes/search_result.html create mode 100644 _sass/_search.scss diff --git a/_includes/header.html b/_includes/header.html index 3d2c895f..0fa87312 100644 --- a/_includes/header.html +++ b/_includes/header.html @@ -5,22 +5,15 @@ {{ site.title }}
    - - - -