| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"/> |
| |
| <!--/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| ~ Licensed to the Apache Software Foundation (ASF) under one |
| ~ or more contributor license agreements. See the NOTICE file |
| ~ distributed with this work for additional information |
| ~ regarding copyright ownership. The ASF licenses this file |
| ~ to you under the Apache License, Version 2.0 (the |
| ~ "License"); you may not use this file except in compliance |
| ~ with the License. You may obtain a copy of the License at |
| ~ |
| ~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~ |
| ~ Unless required by applicable law or agreed to in writing, |
| ~ software distributed under the License is distributed on an |
| ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| ~ KIND, either express or implied. See the License for the |
| ~ specific language governing permissions and limitations |
| ~ under the License. |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/--> |
| |
| <!-- |
| Example (basic) single-page app to search for articles, gets its initial |
| content using a GraphQL query and manipulates it locally, to filter found |
| articles by content section. |
| --> |
| <title>Search Articles</title> |
| <link rel="stylesheet" href="https://fonts.xz.style/serve/inter.css"> |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css"> |
| |
| <style type="text/css"> |
| .activefalse { |
| text-decoration: line-through; |
| } |
| .sectionButton { |
| text-decoration: none; |
| } |
| </style> |
| |
| <script |
| src="https://code.jquery.com/jquery-3.5.1.js" |
| integrity="sha256-QWo7LDvxbWT2tbbQ97B53yJnYU3WhH/C8ycbRAkjPDc=" |
| crossorigin="anonymous"> |
| </script> |
| |
| <script |
| src="https://cdn.jsdelivr.net/npm/handlebars@4.7.6/dist/handlebars.js" |
| integrity="sha256-ZafrO8ZXERYO794Tx1hPaAcdcXNZUNmXufXOSe0Hxj8=" |
| crossorigin="anonymous"> |
| </script> |
| |
| <script src="./js/graphql.js"></script> |
| |
| <script> |
| var templates = {}; |
| var searchResult = {}; |
| var sections = {}; |
| |
| // Filter articles locally based on selection content sections |
| // and render the result. |
| function renderArticles() { |
| var filtered = { |
| info: searchResult.info, |
| result: { article: []} |
| }; |
| if(sections) { |
| for(const article of searchResult.result.article) { |
| if(sections[article.section].active) { |
| filtered.result.article.push(article); |
| } |
| } |
| } else { |
| filtered.result.article = searchResult.result.article; |
| } |
| $("#results").html(templates.results({data:filtered})); |
| } |
| |
| // Render the content sections names of the result set and setup |
| // their selection links |
| function renderSections() { |
| $("#sections").html(templates.sections({data:sections})); |
| $(".sectionButton").click(function() { |
| const section = $(this).text(); |
| sections[section].active = !sections[section].active; |
| renderSections(); |
| renderArticles(); |
| return false; |
| }); |
| } |
| |
| // GraphQL query and results rendering |
| function queryAndRender(searchText) { |
| const query = `{ |
| navigation { |
| search |
| sections { |
| path |
| name |
| } |
| } |
| article(withText: "${searchText}") { |
| path |
| title |
| section |
| } |
| }`; |
| graphQL.query(query, { searchText: searchText}, function(data) { |
| // Render navigation |
| $("#navigation").html(templates.navigation({navigation:data.result.navigation})); |
| |
| // Setup section selection buttons |
| sections = {}; |
| for(const article of data.result.article) { |
| sections[article.section] = { name: article.section, active:true }; |
| } |
| |
| // Render articles |
| searchResult = data; |
| renderSections(); |
| renderArticles(); |
| }); |
| } |
| |
| // Initial setup |
| $(document).ready(function() { |
| // Compile templates |
| templates.results = Handlebars.compile($("#resultsTemplate").html()); |
| templates.navigation = Handlebars.compile($("#navigationTemplate").html()); |
| templates.sections = Handlebars.compile($("#sectionsTemplate").html()); |
| |
| // Setup search form |
| $("#search").submit(function() { |
| queryAndRender($("#searchText").val()); |
| return false; |
| }); |
| |
| // Init page state |
| $("#searchText").focus(); |
| queryAndRender($("#searchText").val()); |
| }); |
| </script> |
| </head> |
| <body> |
| <div> |
| <div id="navigation"/> |
| </div> |
| |
| <h1>Search in articles</h1> |
| <hr/> |
| |
| <form id="search"> |
| <input id="searchText" type="text" width="40" value="virtual"/> |
| <input type="submit" value="Search"/> |
| </form> |
| |
| <div> |
| <div id="sections"/> |
| </div> |
| |
| <div> |
| <div id="results"/> |
| </div> |
| |
| <div id="templates" style="display:none"> |
| <div id="navigationTemplate"> |
| {{#each navigation.sections}} |
| <span class="section"> |
| <a href="{{this.path}}.html">{{this.name}}</a> |
| </span> |
| {{/each}} |
| <br/> |
| <a href="{{navigation.search}}.html">Search</a> |
| <hr/> |
| </div> |
| <div id="resultsTemplate"> |
| {{#if data.result.article}} |
| <h2> |
| Found {{data.result.article.length}} articles |
| containing "{{data.info.searchText}}" |
| in the selected content sections |
| </h2> |
| <ul> |
| {{#each data.result.article}} |
| <li class="articleLink"> |
| <a href="{{this.path}}.html">{{this.title}}</a> |
| </li> |
| {{/each}} |
| </ul> |
| {{else}} |
| <div class="message">No articles found.</div> |
| {{/if}} |
| </div> |
| <div id="sectionsTemplate"> |
| {{#each data}} |
| <span class="active{{this.active}}"><a href="unused" class="sectionButton">{{this.name}}</a></span> |
| {{/each}} |
| <em><-- Click to select/deselect content sections</em> |
| <hr/> |
| </div> |
| </div> |
| </body> |
| </html> |