├── LICENSE ├── README.md ├── _config.yml ├── feeds └── feed.json ├── scripts └── search.js └── search └── index.html /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mat Hayward 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jekyll search 2 | ============= 3 | 4 | Basic Jekyll setup with search via JSON. 5 | 6 | Version history 7 | --------------- 8 | 9 | | Version no. | Description | Date | 10 | | --- | --- | --- | 11 | | 0.3 | Support for collections | 13 March 2014 | 12 | | 0.2.1 | Used built-in jsonify filter (thanks to [davekinkead](https://github.com/davekinkead)) | 25 September 2014 | 13 | | 0.2 | Used built-in ruby JSON (thanks to [mrvdb](https://github.com/mrvdb)) | 20th May 2014 | 14 | | 0.1 | First draft | 24th February 2014 | 15 | 16 | 17 | Usage 18 | ----- 19 | 20 | Use on an existing Jekyll installation (http://jekyllrb.com/) 21 | 22 | * Place ```_plugins```, ```feeds```, ```search``` and ```scripts``` directories in root of jekyll installation 23 | * Add ```url: http://example.com``` to ```_config.yml``` 24 | 25 | 26 | Omissions 27 | ---------- 28 | * To omit a page or post from search add ```search_omit: true``` to YAML Front Matter 29 | 30 | 31 | Notes 32 | ----- 33 | 34 | * To run locally use local domain as url in ```_config.yml``` e.g. ```url: http://localhost:4000``` 35 | * jQuery library is included in ```/search/index.html``` - Remove if not needed, or move to ```
``` if preferred 36 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | url: http://www.example.com 2 | -------------------------------------------------------------------------------- /feeds/feed.json: -------------------------------------------------------------------------------- 1 | --- 2 | layout: null 3 | --- 4 | {% assign first = true %} 5 | [ 6 | {% for collection in site.collections %} 7 | {% for item in collection %} 8 | {% for item in site.[item.label] %} 9 | {% if item.title != null and item.title != empty %} 10 | {% unless first %},{% endunless %}{ 11 | "title": {{item.title | jsonify}}, 12 | "content": {{item.content | markdownify | strip_html | jsonify}}, 13 | "link": "{{ site.baseurl }}{{ item.url }}", 14 | "date": "{{ item.date }}", 15 | "excerpt": "{{ item.snippet }}", 16 | "search_omit": "{{ item.search_omit }}" 17 | } 18 | {% assign first = false %} 19 | {% endif %} 20 | {% endfor %} 21 | {% endfor %} 22 | {% endfor %} 23 | 24 | 25 | {% for post in site.posts %} 26 | {% if post.title != null and post.title != empty %} 27 | {% unless first %},{% endunless %}{ 28 | "title": {{post.title | jsonify}}, 29 | "content": {{post.content | markdownify | strip_html | jsonify}}, 30 | "link": "{{ site.baseurl }}{{ post.url }}", 31 | "date": "{{ post.date }}", 32 | "excerpt": "{{ post.snippet }}", 33 | "search_omit": "{{ post.search_omit }}" 34 | } 35 | {% assign first = false %} 36 | {% endif %} 37 | {% endfor %} 38 | 39 | {% for page in site.pages %} 40 | {% if page.title != null and page.title != empty %} 41 | {% unless first %},{% endunless %}{ 42 | "title": {{page.title | jsonify}}, 43 | "content": {{page.content | strip_html | jsonify}}, 44 | "link": "{{ site.baseurl }}{{ page.url | replace: 'index.html', '' }}", 45 | "date": {{ page.date | jsonify }}, 46 | "excerpt": {{ page.description | jsonify }}, 47 | "search_omit": "{{ page.search_omit }}" 48 | } 49 | {% assign first = false %} 50 | {% endif %} 51 | {% endfor %} 52 | ] 53 | -------------------------------------------------------------------------------- /scripts/search.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A simple JSON search 3 | * Requires jQuery (v 1.7+) 4 | * 5 | * @author Mat Hayward - Erskine Design 6 | * @version 0.1 7 | */ 8 | 9 | 10 | /* ========================================================================== 11 | Initialisation 12 | ========================================================================== */ 13 | 14 | var q, jsonFeedUrl = "/feeds/feed.json", 15 | $searchForm = $("[data-search-form]"), 16 | $searchInput = $("[data-search-input]"), 17 | $resultTemplate = $("#search-result"), 18 | $resultsPlaceholder = $("[data-search-results]"), 19 | $foundContainer = $("[data-search-found]"), 20 | $foundTerm = $("[data-search-found-term]"), 21 | $foundCount = $("[data-search-found-count]"), 22 | allowEmpty = true, 23 | showLoader = true, 24 | loadingClass = "is--loading"; 25 | 26 | 27 | $(document).ready( function() { 28 | // hide items found string 29 | $foundContainer.hide(); 30 | 31 | // initiate search functionality 32 | initSearch(); 33 | }); 34 | 35 | 36 | 37 | 38 | /* ========================================================================== 39 | Search functions 40 | ========================================================================== */ 41 | 42 | 43 | /** 44 | * Initiate search functionality. 45 | * Shows results based on querystring if present. 46 | * Binds search function to form submission. 47 | */ 48 | function initSearch() { 49 | 50 | // Get search results if q parameter is set in querystring 51 | if (getParameterByName('q')) { 52 | q = decodeURIComponent(getParameterByName('q')); 53 | $searchInput.val(q); 54 | execSearch(q); 55 | } 56 | 57 | // Get search results on submission of form 58 | $(document).on("submit", $searchForm, function(e) { 59 | e.preventDefault(); 60 | q = $searchInput.val(); 61 | execSearch(q); 62 | }); 63 | } 64 | 65 | 66 | /** 67 | * Executes search 68 | * @param {String} q 69 | * @return null 70 | */ 71 | function execSearch(q) { 72 | if (q != '' || allowEmpty) { 73 | if (showLoader) { 74 | toggleLoadingClass(); 75 | } 76 | 77 | getSearchResults(processData()); 78 | } 79 | } 80 | 81 | 82 | /** 83 | * Toggles loading class on results and found string 84 | * @return null 85 | */ 86 | function toggleLoadingClass() { 87 | $resultsPlaceholder.toggleClass(loadingClass); 88 | $foundContainer.toggleClass(loadingClass); 89 | } 90 | 91 | 92 | /** 93 | * Get Search results from JSON 94 | * @param {Function} callbackFunction 95 | * @return null 96 | */ 97 | function getSearchResults(callbackFunction) { 98 | $.get(jsonFeedUrl, callbackFunction, 'json'); 99 | } 100 | 101 | 102 | /** 103 | * Process search result data 104 | * @return null 105 | */ 106 | function processData() { 107 | $results = []; 108 | 109 | return function(data) { 110 | 111 | var resultsCount = 0, 112 | results = ""; 113 | 114 | $.each(data, function(index, item) { 115 | // check if search term is in content or title 116 | if (item.search_omit != "true" && (item.content.toLowerCase().indexOf(q.toLowerCase()) > -1 || item.title.toLowerCase().indexOf(q.toLowerCase()) > -1)) { 117 | var result = populateResultContent($resultTemplate.html(), item); 118 | resultsCount++; 119 | results += result; 120 | } 121 | }); 122 | 123 | if (showLoader) { 124 | toggleLoadingClass(); 125 | } 126 | 127 | populateResultsString(resultsCount); 128 | showSearchResults(results); 129 | } 130 | } 131 | 132 | 133 | /** 134 | * Add search results to placeholder 135 | * @param {String} results 136 | * @return null 137 | */ 138 | function showSearchResults(results) { 139 | // Add results HTML to placeholder 140 | $resultsPlaceholder.html(results); 141 | } 142 | 143 | 144 | /** 145 | * Add results content to item template 146 | * @param {String} html 147 | * @param {object} item 148 | * @return {String} Populated HTML 149 | */ 150 | function populateResultContent(html, item) { 151 | html = injectContent(html, item.title, '##Title##'); 152 | html = injectContent(html, item.link, '##Url##'); 153 | html = injectContent(html, item.excerpt, '##Excerpt##'); 154 | html = injectContent(html, item.date, '##Date##'); 155 | return html; 156 | } 157 | 158 | 159 | /** 160 | * Populates results string 161 | * @param {String} count 162 | * @return null 163 | */ 164 | function populateResultsString(count) { 165 | $foundTerm.text(q); 166 | $foundCount.text(count); 167 | $foundContainer.show(); 168 | } 169 | 170 | 171 | 172 | 173 | /* ========================================================================== 174 | Helper functions 175 | ========================================================================== */ 176 | 177 | 178 | /** 179 | * Gets query string parameter - taken from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript 180 | * @param {String} name 181 | * @return {String} parameter value 182 | */ 183 | function getParameterByName(name) { 184 | var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search); 185 | return match && decodeURIComponent(match[1].replace(/\+/g, ' ')); 186 | } 187 | 188 | 189 | /** 190 | * Injects content into template using placeholder 191 | * @param {String} originalContent 192 | * @param {String} injection 193 | * @param {String} placeholder 194 | * @return {String} injected content 195 | */ 196 | function injectContent(originalContent, injection, placeholder) { 197 | var regex = new RegExp(placeholder, 'g'); 198 | return originalContent.replace(regex, injection); 199 | } 200 | -------------------------------------------------------------------------------- /search/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Search 4 | search_omit: true 5 | --- 6 | 7 |18 | result(s) found for “”. 19 |
20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | --------------------------------------------------------------------------------