You've already forked AstralRinth
forked from didirus/AstralRinth
Working Search
This commit is contained in:
21
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
21
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,21 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="7">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||
<item index="6" class="java.lang.String" itemvalue="style" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
754
Cargo.lock
generated
754
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,6 @@ actix-rt = "1.1.1"
|
||||
handlebars = { version = "3.0.0", features = ["dir_source"] }
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0.107"
|
||||
serde = "1.0.107"
|
||||
tantivy = "0.12.0"
|
||||
tempdir = "0.3.7"
|
||||
|
||||
serde = {version="1.0", features=["derive"]}
|
||||
meilisearch-sdk = "0.1.1"
|
||||
actix-files = "0.2.1"
|
||||
|
||||
160
src/main.rs
160
src/main.rs
@@ -1,48 +1,48 @@
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
#[macro_use]
|
||||
extern crate tantivy;
|
||||
|
||||
use actix_web::{web, web::Data, web::Query, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
|
||||
use actix_web::{web, web::Data, App, HttpRequest, HttpResponse, HttpServer, Responder, get, post};
|
||||
use handlebars::Handlebars;
|
||||
use serde_derive::Deserialize;
|
||||
use meilisearch_sdk::{document::*, indexes::*, client::*, search::*};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use actix_files as fs;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Mod {
|
||||
mod_id: usize,
|
||||
title: String,
|
||||
description: String,
|
||||
}
|
||||
|
||||
impl Document for Mod {
|
||||
type UIDType = usize;
|
||||
|
||||
fn get_uid(&self) -> &Self::UIDType {
|
||||
&self.mod_id
|
||||
}
|
||||
}
|
||||
|
||||
use tantivy::collector::TopDocs;
|
||||
use tantivy::query::{QueryParser};
|
||||
use tantivy::schema::*;
|
||||
use tantivy::{Index, IndexReader};
|
||||
use tantivy::ReloadPolicy;
|
||||
use tempdir::TempDir;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct SearchRequest {
|
||||
q: Option<String>,
|
||||
f: Option<String>,
|
||||
}
|
||||
|
||||
#[post("search")]
|
||||
async fn search_post(Query(info): Query<SearchRequest>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> HttpResponse {
|
||||
let results = handle_search(Query(info), reader, parser, schema);
|
||||
async fn search_post(web::Query(info): web::Query<SearchRequest>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let mut data = "{ \"results\": [".to_owned();
|
||||
|
||||
for result in &results {
|
||||
data.push_str(&result);
|
||||
data.push_str(",");
|
||||
}
|
||||
|
||||
if &results.len() > &(0 as usize) {
|
||||
data.pop();
|
||||
}
|
||||
|
||||
data.push_str("] }");
|
||||
let data = json!({
|
||||
"results": results,
|
||||
});
|
||||
|
||||
HttpResponse::Ok().body(data)
|
||||
}
|
||||
|
||||
#[get("search")]
|
||||
async fn search(Query(info): Query<SearchRequest>, hb: Data<Handlebars<'_>>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> HttpResponse {
|
||||
let results = handle_search(Query(info), reader, parser, schema);
|
||||
async fn search_get(web::Query(info): web::Query<SearchRequest>, hb: Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let results = search(web::Query(info));
|
||||
|
||||
let data = json!({
|
||||
"results": results,
|
||||
@@ -53,28 +53,27 @@ async fn search(Query(info): Query<SearchRequest>, hb: Data<Handlebars<'_>>, rea
|
||||
HttpResponse::Ok().body(body)
|
||||
}
|
||||
|
||||
fn handle_search(Query(info): Query<SearchRequest>, reader: Data<IndexReader>, parser: Data<QueryParser<>>, schema: Data<Schema<>>) -> Vec<String>{
|
||||
let mut search_query : String = "".to_string();
|
||||
fn search(web::Query(info): web::Query<SearchRequest>) -> Vec<Mod> {
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
|
||||
let mut search_query = "".to_string();
|
||||
let mut filters = "".to_string();
|
||||
|
||||
if let Some(q) = info.q {
|
||||
search_query = q;
|
||||
}
|
||||
|
||||
let searcher = reader.searcher();
|
||||
|
||||
let mut results = vec![];
|
||||
|
||||
if let Ok(query) = parser.parse_query(&search_query) {
|
||||
if let Ok(top_docs) = searcher.search(&query, &TopDocs::with_limit(10)) {
|
||||
for (_score, doc_address) in top_docs {
|
||||
if let Ok(retrieved_doc) = searcher.doc(doc_address) {
|
||||
results.push(schema.to_json(&retrieved_doc));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(f) = info.f {
|
||||
filters = f;
|
||||
}
|
||||
|
||||
return results;
|
||||
let mut query = Query::new(&search_query).with_limit(10);
|
||||
|
||||
if !filters.is_empty() {
|
||||
query = Query::new(&search_query).with_limit(10).with_filters(&filters);
|
||||
}
|
||||
|
||||
client.get_index("mods").unwrap().search::<Mod>(&query).unwrap().hits
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
@@ -88,72 +87,53 @@ async fn index(hb: web::Data<Handlebars<'_>>) -> HttpResponse {
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> tantivy::Result<()> {
|
||||
async fn main() -> std::io::Result<()> {
|
||||
//Handlebars
|
||||
let mut handlebars = Handlebars::new();
|
||||
handlebars
|
||||
.register_templates_directory(".html", "./static/templates")
|
||||
.register_templates_directory(".hbs", "./templates")
|
||||
.unwrap();
|
||||
let handlebars_ref = web::Data::new(handlebars);
|
||||
|
||||
//Search
|
||||
let index_path = TempDir::new("search_index")?;
|
||||
|
||||
let mut schema_builder = Schema::builder();
|
||||
let client = Client::new("http://localhost:7700", "");
|
||||
let mut mods = client.get_or_create("mods").unwrap();
|
||||
|
||||
schema_builder.add_text_field("title", TEXT | STORED);
|
||||
schema_builder.add_text_field("keywords", TEXT | STORED);
|
||||
schema_builder.add_text_field("description", TEXT | STORED);
|
||||
schema_builder.add_text_field("body", TEXT);
|
||||
|
||||
let schema = schema_builder.build();
|
||||
let schema_ref = web::Data::new(schema.clone());
|
||||
|
||||
let title = schema.get_field("title").unwrap();
|
||||
let keywords = schema.get_field("keywords").unwrap();
|
||||
let description = schema.get_field("description").unwrap();
|
||||
let body = schema.get_field("body").unwrap();
|
||||
|
||||
let site_index = Index::create_in_dir(&index_path, schema.clone())?;
|
||||
let mut index_writer = site_index.writer(50_000_000)?;
|
||||
|
||||
index_writer.add_document(doc!(
|
||||
title => "Magic",
|
||||
keywords => "Magic Fun Adventure",
|
||||
description => "A magic mod for magical purposes!",
|
||||
body => "A cool magic mod made by your mom :)",
|
||||
));
|
||||
|
||||
index_writer.add_document(doc!(
|
||||
title => "Technology",
|
||||
keywords => "Technology Fun Adventure",
|
||||
description => "A tech mod for tech purposes!",
|
||||
body => "A tech mod made by your mom :)",
|
||||
));
|
||||
|
||||
index_writer.commit()?;
|
||||
|
||||
let reader = site_index.reader_builder().reload_policy(ReloadPolicy::OnCommit).try_into()?;
|
||||
let reader_ref = web::Data::new(reader);
|
||||
|
||||
let query_parser = QueryParser::for_index(&site_index, vec![title, body, keywords, description]);
|
||||
let query_parser_ref = web::Data::new(query_parser);
|
||||
mods.add_documents(vec![
|
||||
Mod {
|
||||
mod_id: 0,
|
||||
title: String::from("Magic Mod"),
|
||||
description: String::from("An illustrious magic mod for magical wizards"),
|
||||
},
|
||||
Mod {
|
||||
mod_id: 1,
|
||||
title: String::from("Tech Mod"),
|
||||
description: String::from("An technological mod for complete NERDS"),
|
||||
},
|
||||
Mod {
|
||||
mod_id: 2,
|
||||
title: String::from("Hood Mod"),
|
||||
description: String::from("A hood mod to roleplay as if you were a real street person. Some adventure stuff too"),
|
||||
},
|
||||
Mod {
|
||||
mod_id: 3,
|
||||
title: String::from("Adventure Mod"),
|
||||
description: String::from("An epic gamer adventure mod for epic adventure gamers"),
|
||||
},
|
||||
], Some("mod_id")).unwrap();
|
||||
|
||||
//Init App
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(handlebars_ref.clone())
|
||||
.app_data(reader_ref.clone())
|
||||
.app_data(query_parser_ref.clone())
|
||||
.app_data(schema_ref.clone())
|
||||
.service(fs::Files::new("/static", "./static").show_files_listing())
|
||||
.service(index)
|
||||
.service(search)
|
||||
.service(search_get)
|
||||
.service(search_post)
|
||||
})
|
||||
.bind("127.0.0.1:8000")?
|
||||
.run()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
.await
|
||||
}
|
||||
|
||||
|
||||
122
static/css/multiselect.css
Normal file
122
static/css/multiselect.css
Normal file
@@ -0,0 +1,122 @@
|
||||
.multiselect-wrapper {
|
||||
width: 180px;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
font-family: "Segoe UI", Verdana, Helvetica, Sans-Serif;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-input {
|
||||
width: 100%;
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.multiselect-wrapper label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight : 600;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
display: none;
|
||||
background-color: white;
|
||||
border: 1px solid grey;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list > span {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list .multiselect-checkbox {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list > span,
|
||||
.multiselect-wrapper .multiselect-list li {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.multiselect-wrapper .multiselect-list {
|
||||
padding: 5px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.multiselect-wrapper ul {
|
||||
list-style: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.multiselect-wrapper ul li {
|
||||
padding-right: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.multiselect-wrapper ul li.active {
|
||||
background-color: rgb(0, 102, 255);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.multiselect-wrapper ul li:hover {
|
||||
background-color: rgb(0, 102, 255);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.multiselect-input-div {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.multiselect-input-div input{
|
||||
border: 1px solid #ababab;
|
||||
background : #fff;
|
||||
margin: 5px 0 6px 0;
|
||||
padding: 5px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.multiselect-count {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
border-radius: 2px;
|
||||
background-color: lightblue;
|
||||
display: inline-block !important;
|
||||
padding: 2px 7px;
|
||||
left: -45px;
|
||||
}
|
||||
|
||||
.multiselect-wrapper.disabled .multiselect-dropdown-arrow {
|
||||
border-top: 5px solid lightgray;
|
||||
}
|
||||
|
||||
.multiselect-wrapper.disabled .multiselect-count {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
.multiselect-dropdown-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-top: 5px solid black;
|
||||
position: absolute;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
display: inline-block !important;
|
||||
margin-top: 17px;
|
||||
margin-left: -42px;
|
||||
}
|
||||
|
||||
48
static/css/style.css
Normal file
48
static/css/style.css
Normal file
@@ -0,0 +1,48 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,600');
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
#categories_multiSelect {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.results {
|
||||
width: 75%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.result {
|
||||
display: flex;
|
||||
height: 100px;
|
||||
margin: 30px auto;
|
||||
padding: 5px;
|
||||
width: 75%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.result img {
|
||||
padding: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.result-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.result-info * {
|
||||
padding: 0 5px 0 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rounded-border {
|
||||
border-radius: 10px;
|
||||
border: 1px;
|
||||
}
|
||||
|
||||
.gray-border {
|
||||
border: 1px solid darkgray;
|
||||
}
|
||||
44
static/js/multiselect.min.js
vendored
Normal file
44
static/js/multiselect.min.js
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
if(!m_helper)
|
||||
{var m_helper={removeNode:function(id){var el=document.getElementById(id);if(el){el.parentNode.removeChild(el)}},insertAfter:function(item,target){var parent=target.parentNode;if(target.nextElementSibling){parent.insertBefore(item,target.nextElementSibling)}else{parent.appendChild(item)}},hide:function(element){element.style.display='none'},hideAll:function(array){for(var i=0;i<array.length;i++){this.hide(array[i])}},show:function(element){element.style.display='block'},showAll:function(array){for(var i=0;i<array.length;i++){this.show(array[i])}},parent:function(element,id){var parent=element.parentElement;while(parent&&parent.tagName!='BODY'){if(parent.id==id){return parent}
|
||||
parent=parent.parentElement}
|
||||
return null},create:function(data){var result=document.createElement(data.tag);if(data.id){result.id=data.id}
|
||||
if(data.class){result.className=data.class}
|
||||
if(data.attributes){for(var prop in data.attributes){result.setAttribute(prop,data.attributes[prop])}}
|
||||
if(data.data){for(var prop in data.data){result.dataset[prop]=data.data[prop]}}
|
||||
return result},div:function(data){if(!data){data=new Object()}
|
||||
data.tag='div';return this.create(data)},label:function(data){if(!data){data=new Object()}
|
||||
data.tag='label';return this.create(data)},textField:function(data){if(!data){data=new Object()}
|
||||
data.tag='input';if(!data.attributes)
|
||||
data.attributes=new Object();data.attributes.type='text';return this.create(data)},checkbox:function(data){if(!data){data=new Object()}
|
||||
data.tag='input';if(!data.attributes)
|
||||
data.attributes=new Object();data.attributes.type='checkbox';return this.create(data)},each:function(array,handler){for(var i=0;i<array.length;i++){handler(array[i])}},setActive:function(element){element.classList.add('active')},setUnactive:function(element){element.classList.remove('active')},select:function(element){element.selected=!0;element.setAttribute('selected','selected')},deselect:function(element){element.selected=!1;element.removeAttribute('selected')},check:function(element){element.checked=!0},uncheck:function(element){element.checked=!1},click:function(element){if(element.fireEvent){el.fireEvent('onclick')}else{var evObj=document.createEvent('Events');evObj.initEvent('click',!0,!1);element.dispatchEvent(evObj)}},setDisabled:function(element,value){element.disabled=value},}}
|
||||
function Multiselect(item,opts){if((typeof($)!='undefined'&&!$(item).is('select'))||(typeof($)=='undefined'&&item.tagName!='SELECT')){throw "Multiselect: passed object must be a select"}
|
||||
if((typeof($)!='undefined'&&!$(item).attr('multiple'))||(typeof($)=='undefined'&&!item.hasAttribute('multiple'))){throw "Multiselect: passed object should contain 'multiple' attribute"}
|
||||
this._item=item;this._createUI();this._appendEvents();this._initSelectedFields();this._initIsEnabled()}
|
||||
Multiselect.prototype={_createUI:function(){m_helper.removeNode(this._getIdentifier());var wrapper=this._createWrapper();m_helper.insertAfter(wrapper,this._item);wrapper.appendChild(this._createInputField());wrapper.appendChild(this._createItemList());m_helper.hide(this._item)},_createWrapper:function(){var result=document.createElement('div');result.className='multiselect-wrapper';result.id=this._getIdentifier();return result},_createInputField:function(){var input=m_helper.textField({id:this._getInputFieldIdentifier(),class:'multiselect-input',attributes:{autocomplete:'off'}}),label=m_helper.label({id:this._getInputBadgeIdentifier(),class:'multiselect-count',attributes:{for:this._getInputFieldIdentifier()}}),dropDownArrow=m_helper.label({class:'multiselect-dropdown-arrow',attributes:{for:this._getInputFieldIdentifier()}}),result=m_helper.div({class:'multiselect-input-div'});label.style.visibility='hidden';label.innerHTML=0;result.appendChild(input);result.appendChild(label);result.appendChild(dropDownArrow);return result},_createItemList:function(){var list=m_helper.create({tag:'ul'});var self=this;m_helper.each(this._getItems(this._item),function(e){var insertItem=self._createItem('li',e.id,e.text,e.selected);list.appendChild(insertItem);var checkBox=insertItem.querySelector('input[type=checkbox]');e.multiselectElement=checkBox;checkBox.dataset.multiselectElement=JSON.stringify(e)});var selectAll=this._createItem('span',-1,'Select all');var result=m_helper.div({id:this._getItemListIdentifier(),class:'multiselect-list'});result.appendChild(selectAll);result.appendChild(m_helper.create({tag:'hr'}));result.appendChild(list);return result},_createItem:function(wrapper,value,text,selected){var checkBox=m_helper.checkbox({class:'multiselect-checkbox',data:{val:value}}),textBox=m_helper.create({tag:'span',class:'multiselect-text'}),result=m_helper.create({tag:wrapper}),label=m_helper.label();textBox.className='multiselect-text';textBox.innerHTML=text;label.appendChild(checkBox);label.appendChild(textBox);result.appendChild(label);return result},_initSelectedFields:function(){var itemResult=this._getItems().filter(function(obj){return obj.selected});if(itemResult.length!=0){var self=this;m_helper.each(itemResult,function(e){self.select(e.id)})}
|
||||
this._hideList(this)},_initIsEnabled:function(){this.setIsEnabled(!this._item.disabled)},destroy(){m_helper.removeNode(this._getIdentifier());m_helper.show(this._item);var index=window.multiselects._items.indexOf(this._item);if(index>-1){window.multiselects._items.splice(index,1);window.multiselects.splice(index,1)}},select:function(val){this._toggle(val,!0)},deselect:function(val){this._toggle(val,!1)},setIsEnabled(isEnabled){if(this._isEnabled===isEnabled)return;var wrapperItem=document.getElementById(this._getIdentifier());if(isEnabled){wrapperItem.classList.remove('disabled')}else{wrapperItem.classList.add('disabled')}
|
||||
m_helper.setDisabled(this._item,!isEnabled);m_helper.setDisabled(document.getElementById(this._getInputFieldIdentifier()),!isEnabled);this._isEnabled=isEnabled},_toggle:function(val,setCheck){var self=this;if(val){m_helper.each(document.getElementById(this._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){if(e.dataset.val==val){if(setCheck&&!e.checked){m_helper.check(e);self._onCheckBoxChange(e,self)}else if(!setCheck&&e.checked){m_helper.uncheck(e);self._onCheckBoxChange(e,self)}}});self._updateText(self)}},selectAll:function(val){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.check(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},deselectAll:function(){var selectAllChkBox=document.querySelector('#'+this._getIdentifier()+' .multiselect-checkbox');m_helper.uncheck(selectAllChkBox);this._onCheckBoxChange(selectAllChkBox,this);this._updateText(this)},_checkboxClickEvents:{},setCheckBoxClick(id,handler){if(typeof handler==="function"){this._checkboxClickEvents[id]=handler}else{console.error("Checkbox click handler for checkbox value="+id+" is not a function")}
|
||||
return this},_appendEvents:function(){var self=this;document.getElementById(self._getInputFieldIdentifier()).addEventListener('focus',function(event){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value='';m_helper.each(window.multiselects,function(e){if(document.getElementById(e._getItemListIdentifier()).offsetParent&&m_helper.parent(event.target,e._getIdentifier())){e._hideList(self)}})});document.getElementById(self._getInputFieldIdentifier()).addEventListener('click',function(){self._showList(self);document.getElementById(self._getInputFieldIdentifier()).value=''});document.getElementById(self._getIdentifier()).addEventListener('click',function(event){event=event||window.event;var target=event.target||event.srcElement;if(m_helper.parent(target,self._getIdentifier())){event.stopPropagation()}});document.getElementById(self._getItemListIdentifier()).addEventListener('mouseover',function(){self._showList(self)});m_helper.each(document.getElementById(self._getIdentifier()).querySelectorAll('.multiselect-checkbox'),function(e){e.addEventListener('change',function(event){self._onCheckBoxChange(e,self,event)})});var onInput=function(){var text=this.value.toLowerCase();if(!text||text==''){m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.show(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));m_helper.showAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'))}else{m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > span'));m_helper.hide(document.querySelector('#'+self._getItemListIdentifier()+' > hr'));var array=Array.prototype.filter.call(document.querySelectorAll('#'+self._getItemListIdentifier()+' li span'),function(obj){return obj.innerHTML.toLowerCase().indexOf(text)>-1});m_helper.hideAll(document.querySelectorAll('#'+self._getItemListIdentifier()+' li'));m_helper.each(array,function(e){m_helper.show(e.parentElement.parentElement)})}}
|
||||
document.getElementById(self._getInputFieldIdentifier()).addEventListener('propertychange',onInput);document.getElementById(self._getInputFieldIdentifier()).addEventListener('input',onInput)},_onCheckBoxChange:function(checkbox,self,event){if(!checkbox.dataset.multiselectElement){var checkedState=self._performSelectAll(checkbox,self);if(typeof self._checkboxClickEvents.checkboxAll==="function"){self._checkboxClickEvents.checkboxAll(checkbox,{checked:checkedState})}}
|
||||
else{var checkedState=self._performSelectItem(checkbox,self);if(typeof self._checkboxClickEvents[checkedState.id]==="function"){self._checkboxClickEvents[checkedState.id](checkbox,checkedState)}
|
||||
self._updateSelectAll(self)}
|
||||
self._forceUpdate()},_performSelectItem:function(checkbox,self){var item=JSON.parse(checkbox.dataset.multiselectElement);if(checkbox.checked){self._itemCounter++;m_helper.select(this._item.options[item.index]);m_helper.setActive(checkbox.parentElement.parentElement);return{id:item.id,checked:!0}}
|
||||
self._itemCounter--;m_helper.deselect(this._item.options[item.index]);m_helper.setUnactive(checkbox.parentElement.parentElement);return{id:item.id,checked:!1}},_performSelectAll:function(checkbox,self){var items=self._getItems();if(checkbox.checked){self._itemCounter=items.length;m_helper.each(items,function(e){m_helper.setActive(e.multiselectElement.parentElement.parentElement);m_helper.select(self._item.options[e.index]);m_helper.check(e.multiselectElement)});return!0}
|
||||
self._itemCounter=0;m_helper.each(items,function(e){e.multiselectElement.parentElement.parentElement.classList.remove('active');m_helper.deselect(self._item.options[e.index]);m_helper.uncheck(e.multiselectElement)});return!1},_updateSelectAll:function(self){var allChkBox=document.getElementById(self._getItemListIdentifier()).querySelector('input[type=checkbox]');if(self._itemCounter==self._getItems().length){allChkBox.checked=!0}
|
||||
else if(allChkBox.checked){allChkBox.checked=!1}},_hideList:function(context,event){m_helper.setUnactive(document.getElementById(context._getItemListIdentifier()));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('span'));m_helper.show(document.getElementById(context._getItemListIdentifier()).querySelector('hr'));m_helper.showAll(document.getElementById(context._getItemListIdentifier()).querySelectorAll('li'));context._updateText(context);if(event)
|
||||
event.stopPropagation()},_updateText:function(context){var activeItems=document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul .active');if(activeItems.length>0){var val='';for(var i=0;i<(activeItems.length<5?activeItems.length:5);i++){val+=activeItems[i].innerText+", "}
|
||||
val=val.substr(0,val.length-2);if(val.length>20){val=val.substr(0,17)+'...'}}
|
||||
if(activeItems.length==document.getElementById(context._getItemListIdentifier()).querySelectorAll('ul li').length){val='All selected'}
|
||||
document.getElementById(context._getInputFieldIdentifier()).value=val?val:''},_showList:function(context){m_helper.setActive(document.getElementById(context._getItemListIdentifier()))},_forceUpdate:function(){var badge=document.getElementById(this._getInputBadgeIdentifier());badge.style.visibility='hidden';if(this._itemCounter!=0){badge.innerHTML=this._itemCounter;badge.style.visibility='visible';var ddArrow=badge.nextElementSibling;if(this._itemCounter<10){badge.style.left='-45px';ddArrow.style.marginLeft='-42px'}
|
||||
else if(this._itemCounter<100){badge.style.left='-50px';ddArrow.style.marginLeft='-47px'}
|
||||
else if(this._itemCounter<1000){badge.style.left='-55px';ddArrow.style.marginLeft='-52px'}
|
||||
else if(this._itemCounter<10000){badge.style.left='-60px';ddArrow.style.marginLeft='-57px'}}},_items:undefined,_itemCounter:0,_isEnabled:!0,_getItems:function(){if(this._items==undefined){var result=[];var opts=this._item.options;for(var i=0;i<opts.length;i++){var insertItem={id:opts[i].value,index:i,text:opts[i].innerHTML,selected:!!opts[i].selected,selectElement:opts[i]};result.push(insertItem)}
|
||||
this._items=result}
|
||||
return this._items},_getItemUniqueIdentifier:function(){var id=this._item.getAttribute('id'),name=this._item.getAttribute('name');if(!(id||name)){throw "Multiselect: object does not contain any identifier (id or name)"}
|
||||
return id?id:name},_getIdentifier:function(){return this._getItemUniqueIdentifier()+'_multiSelect'},_getInputFieldIdentifier:function(){return this._getItemUniqueIdentifier()+'_input'},_getItemListIdentifier:function(){return this._getItemUniqueIdentifier()+'_itemList'},_getInputBadgeIdentifier:function(){return this._getItemUniqueIdentifier()+'_inputCount'}}
|
||||
window.multiselects=[];if(typeof($)!='undefined'){$.fn.multiselect=function(){var res=[];if(!window.multiselects._items){window.multiselects._items=[]}
|
||||
if(this.length!=0){$(this).each(function(i,e){var index=window.multiselects._items.indexOf(e);if(index==-1){var inputItem=new Multiselect(e);window.multiselects.push(inputItem);window.multiselects._items.push(e);res.push(inputItem)}else{res.push(window.multiselects[index])}})}
|
||||
return res.length==1?res[0]:$(res)};$(document).click(function(event){hideMultiselects(event)})}else{document.multiselect=function(selector){var res=[];if(!window.multiselects._items){window.multiselects._items=[]}
|
||||
m_helper.each(document.querySelectorAll(selector),function(e){var index=window.multiselects._items.indexOf(e);if(index==-1){var inputItem=new Multiselect(e);window.multiselects.push(inputItem);window.multiselects._items.push(e);res.push(inputItem)}else{res.push(window.multiselects[index])}});return res.length==1?res[0]:res}
|
||||
window.onclick=function(event){hideMultiselects(event)}}
|
||||
function hideMultiselects(event){m_helper.each(window.multiselects,function(e){if(document.getElementById(e._getItemListIdentifier()).offsetParent&&!m_helper.parent(event.target,e._getIdentifier())){e._hideList(e,event)}})}
|
||||
32
static/js/search.js
Normal file
32
static/js/search.js
Normal file
@@ -0,0 +1,32 @@
|
||||
let input = document.getElementById("search-input");
|
||||
let resultContainer = document.getElementById("results");
|
||||
|
||||
function handleSearch() {
|
||||
let safeName = encodeURIComponent(input.value).replace(/%20/g,'+');
|
||||
|
||||
let xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.onreadystatechange = function() {
|
||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
||||
let parsedResponse = JSON.parse(xmlHttp.responseText);
|
||||
let contentToSet = "";
|
||||
|
||||
for (let result of parsedResponse.results) {
|
||||
contentToSet += `
|
||||
<div class="result gray-border rounded-border">
|
||||
<img src="..." width="75px" height="75px">
|
||||
<div class="result-info">
|
||||
<h2>${result.title}</h2>
|
||||
<p>${result.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
resultContainer.innerHTML = contentToSet;
|
||||
}
|
||||
}
|
||||
xmlHttp.open("POST", "search?q=" + safeName, true);
|
||||
xmlHttp.send(null);
|
||||
|
||||
window.history.pushState('Search', 'Search', '/search?q=' + safeName);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Search</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Test Mod Search</h1>
|
||||
<form>
|
||||
<label for="search-input">Search for Mods:</label>
|
||||
<input type="text" id="search-input" oninput="handleSearch()">
|
||||
</form>
|
||||
|
||||
<p>{{results}}</p>
|
||||
|
||||
<script>
|
||||
let input = document.getElementById("search-input");
|
||||
|
||||
function handleSearch() {
|
||||
let safeName = encodeURIComponent(input.value).replace(/%20/g,'+');
|
||||
|
||||
let xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.onreadystatechange = function() {
|
||||
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
|
||||
console.log(xmlHttp.responseText);
|
||||
console.log(JSON.parse(xmlHttp.responseText));
|
||||
}
|
||||
}
|
||||
xmlHttp.open("POST", "search?q=" + safeName, true);
|
||||
xmlHttp.send(null);
|
||||
|
||||
window.history.pushState('Search', 'Search', '/search?q=' + safeName);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
51
templates/search.hbs
Normal file
51
templates/search.hbs
Normal file
@@ -0,0 +1,51 @@
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<link href="static/css/style.css" rel="stylesheet" type="text/css">
|
||||
<link href="static/css/multiselect.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<script src="static/js/multiselect.min.js"></script>
|
||||
|
||||
<title>Search</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Test Mod Search</h1>
|
||||
|
||||
<form>
|
||||
<label for="search-input">Search for Mods:</label>
|
||||
<input type="text" id="search-input" oninput="handleSearch()">
|
||||
|
||||
<label for="categories">Categories:</label>
|
||||
<select id="categories" multiple>
|
||||
<option value="Adventure">Adventure</option>
|
||||
<option value="Technology">Technology</option>
|
||||
<option value="Magic">Magic</option>
|
||||
<option value="Utility">Utility</option>
|
||||
</select>
|
||||
|
||||
</form>
|
||||
<div id="results" class="results">
|
||||
{{#each results}}
|
||||
<div class="result gray-border rounded-border">
|
||||
<img src="..." width="75px" height="75px">
|
||||
<div class="result-info">
|
||||
<h2>{{this.title}}</h2>
|
||||
<p>{{this.description}}</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<script src="static/js/search.js"></script>
|
||||
<script>
|
||||
document.multiselect('#categories')
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user