Replace tipuesearch's jQuery with a mix of JS and preact
This commit is contained in:
parent
75f34cae30
commit
194110ea6d
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,3 +5,7 @@ node_modules
|
|||||||
static
|
static
|
||||||
|
|
||||||
/vendor
|
/vendor
|
||||||
|
|
||||||
|
/prettier.config.js
|
||||||
|
/.eslintrc.js
|
||||||
|
/stylelint.config.js
|
||||||
|
@ -22,7 +22,7 @@ COPY bin/ /daux/bin/
|
|||||||
COPY libs/ /daux/libs/
|
COPY libs/ /daux/libs/
|
||||||
COPY templates/ /daux/templates/
|
COPY templates/ /daux/templates/
|
||||||
COPY themes/ /daux/themes/
|
COPY themes/ /daux/themes/
|
||||||
COPY tipuesearch/ /daux/tipuesearch/
|
COPY search/ /daux/search/
|
||||||
COPY global.json /daux/global.json
|
COPY global.json /daux/global.json
|
||||||
COPY index.php /daux/index.php
|
COPY index.php /daux/index.php
|
||||||
|
|
||||||
|
@ -1,48 +1,58 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
browsers: "> 0.25%, Edge >= 15, Safari >= 10, iOS >= 10, Chrome >= 56, Firefox >= 51, IE >= 11, not op_mini all",
|
browsers:
|
||||||
|
"> 0.25%, Edge >= 15, Safari >= 10, iOS >= 10, Chrome >= 56, Firefox >= 51, IE >= 11, not op_mini all",
|
||||||
presets: [
|
presets: [
|
||||||
"@swissquote/crafty-preset-postcss",
|
"@swissquote/crafty-preset-babel",
|
||||||
"@swissquote/crafty-runner-gulp"
|
"@swissquote/crafty-runner-rollup",
|
||||||
|
"@swissquote/crafty-preset-postcss",
|
||||||
|
"@swissquote/crafty-runner-gulp"
|
||||||
],
|
],
|
||||||
destination_css: "themes",
|
destination_css: "themes",
|
||||||
|
destination_js: ".",
|
||||||
stylelint_pattern: [
|
stylelint_pattern: [
|
||||||
"themes/daux/scss/**/*.scss",
|
"themes/daux/scss/**/*.scss",
|
||||||
"themes/daux_singlepage/scss/**/*.scss",
|
"themes/daux_singlepage/scss/**/*.scss",
|
||||||
"!*.min.css",
|
"!*.min.css",
|
||||||
"!**/vendor/**/*.scss"
|
"!**/vendor/**/*.scss"
|
||||||
],
|
],
|
||||||
stylelint: {
|
stylelint: {
|
||||||
rules: {
|
rules: {
|
||||||
"swissquote/no-type-outside-scope": null,
|
"swissquote/no-type-outside-scope": null,
|
||||||
"plugin/no-unsupported-browser-features": null
|
"plugin/no-unsupported-browser-features": null
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
search: {
|
||||||
|
runner: "rollup",
|
||||||
|
source: "src/js/search/index.js",
|
||||||
|
destination: "search/search.min.js"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
css: {
|
css: {
|
||||||
"theme_blue": {
|
theme_blue: {
|
||||||
source: "themes/daux/scss/theme-blue.scss",
|
source: "themes/daux/scss/theme-blue.scss",
|
||||||
destination: "daux/css/theme-blue.min.css",
|
destination: "daux/css/theme-blue.min.css",
|
||||||
watch: ["themes/daux/scss/**"]
|
watch: ["themes/daux/scss/**"]
|
||||||
},
|
},
|
||||||
"theme_green": {
|
theme_green: {
|
||||||
source: "themes/daux/scss/theme-green.scss",
|
source: "themes/daux/scss/theme-green.scss",
|
||||||
destination: "daux/css/theme-green.min.css",
|
destination: "daux/css/theme-green.min.css",
|
||||||
watch: ["themes/daux/scss/**"]
|
watch: ["themes/daux/scss/**"]
|
||||||
},
|
},
|
||||||
"theme_navy": {
|
theme_navy: {
|
||||||
source: "themes/daux/scss/theme-navy.scss",
|
source: "themes/daux/scss/theme-navy.scss",
|
||||||
destination: "daux/css/theme-navy.min.css",
|
destination: "daux/css/theme-navy.min.css",
|
||||||
watch: ["themes/daux/scss/**"]
|
watch: ["themes/daux/scss/**"]
|
||||||
},
|
},
|
||||||
"theme_red": {
|
theme_red: {
|
||||||
source: "themes/daux/scss/theme-red.scss",
|
source: "themes/daux/scss/theme-red.scss",
|
||||||
destination: "daux/css/theme-red.min.css",
|
destination: "daux/css/theme-red.min.css",
|
||||||
watch: ["themes/daux/scss/**"]
|
watch: ["themes/daux/scss/**"]
|
||||||
},
|
},
|
||||||
"daux_singlepage": {
|
daux_singlepage: {
|
||||||
source: "themes/daux_singlepage/scss/main.scss",
|
source: "themes/daux_singlepage/scss/main.scss",
|
||||||
destination: "daux_singlepage/css/main.min.css",
|
destination: "daux_singlepage/css/main.min.css",
|
||||||
watch: ["themes/daux_singlepage/scss/**"]
|
watch: ["themes/daux_singlepage/scss/**"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -74,11 +74,11 @@ class Generator implements \Todaymade\Daux\Format\Base\Generator, LiveGenerator
|
|||||||
|
|
||||||
if ($params['html']['search']) {
|
if ($params['html']['search']) {
|
||||||
GeneratorHelper::copyRecursive(
|
GeneratorHelper::copyRecursive(
|
||||||
$this->daux->local_base . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR,
|
$this->daux->local_base . DIRECTORY_SEPARATOR . 'search' . DIRECTORY_SEPARATOR,
|
||||||
$destination . DIRECTORY_SEPARATOR . 'tipuesearch'
|
$destination . DIRECTORY_SEPARATOR . 'search'
|
||||||
);
|
);
|
||||||
file_put_contents(
|
file_put_contents(
|
||||||
$destination . DIRECTORY_SEPARATOR . 'tipuesearch' . DIRECTORY_SEPARATOR . 'tipuesearch_content.json',
|
$destination . DIRECTORY_SEPARATOR . 'search' . DIRECTORY_SEPARATOR . 'search_index.json',
|
||||||
json_encode(['pages' => $this->indexed_pages])
|
json_encode(['pages' => $this->indexed_pages])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,8 +4,11 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@swissquote/crafty": "^1.7.3",
|
"@swissquote/crafty": "^1.7.3",
|
||||||
|
"@swissquote/crafty-preset-babel": "^1.8.0",
|
||||||
"@swissquote/crafty-preset-postcss": "^1.7.3",
|
"@swissquote/crafty-preset-postcss": "^1.7.3",
|
||||||
"@swissquote/crafty-runner-gulp": "^1.7.3"
|
"@swissquote/crafty-runner-gulp": "^1.7.3",
|
||||||
|
"@swissquote/crafty-runner-rollup": "^1.8.0",
|
||||||
|
"preact": "^8.5.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "crafty run",
|
"build": "crafty run",
|
||||||
|
@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
Tipue Search 5.0
|
|
||||||
Copyright (c) 2015 Tipue
|
|
||||||
Tipue Search is released under the MIT License
|
|
||||||
http://www.tipue.com/search
|
|
||||||
*/
|
|
||||||
|
|
||||||
body.with-search {
|
body.with-search {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -16,7 +9,7 @@ body.with-search {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
position:absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@ -27,18 +20,17 @@ body.with-search {
|
|||||||
.SearchResultsBackdrop {
|
.SearchResultsBackdrop {
|
||||||
z-index: 90;
|
z-index: 90;
|
||||||
|
|
||||||
width:100%;
|
width: 100%;
|
||||||
|
|
||||||
position:absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
|
||||||
background: #000;
|
background: #000;
|
||||||
opacity: .6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.homepage .SearchResults,
|
.homepage .SearchResults,
|
||||||
.homepage .SearchResultsBackdrop {
|
.homepage .SearchResultsBackdrop {
|
||||||
top: 50px;
|
top: 50px;
|
||||||
@ -54,11 +46,12 @@ body.with-search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__warning {
|
.SearchResults__warning {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:15px;
|
font-size: 15px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #555;
|
color: #555;
|
||||||
margin: 7px 0;
|
margin: 7px 0;
|
||||||
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__warning a {
|
.SearchResults__warning a {
|
||||||
@ -71,8 +64,8 @@ body.with-search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__count {
|
.SearchResults__count {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:15px;
|
font-size: 15px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
color: #555;
|
color: #555;
|
||||||
}
|
}
|
||||||
@ -86,12 +79,12 @@ body.with-search {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: .8em;
|
line-height: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__title {
|
.SearchResults__title {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:21px;
|
font-size: 21px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
margin-top: 23px;
|
margin-top: 23px;
|
||||||
}
|
}
|
||||||
@ -106,8 +99,8 @@ body.with-search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__url {
|
.SearchResults__url {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:14px;
|
font-size: 14px;
|
||||||
line-height: 1.9;
|
line-height: 1.9;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
@ -123,8 +116,8 @@ body.with-search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__text {
|
.SearchResults__text {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:15px;
|
font-size: 15px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #555;
|
color: #555;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@ -133,8 +126,8 @@ body.with-search {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.SearchResults__debug {
|
.SearchResults__debug {
|
||||||
font-weight:300;
|
font-weight: 300;
|
||||||
font-size:13px;
|
font-size: 13px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: #555;
|
color: #555;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
@ -164,7 +157,6 @@ body.with-search {
|
|||||||
border: 1px solid #e2e2e2;
|
border: 1px solid #e2e2e2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* spinner */
|
/* spinner */
|
||||||
|
|
||||||
@media (min-width: 650px) {
|
@media (min-width: 650px) {
|
2
search/search.min.js
vendored
Normal file
2
search/search.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
search/search.min.js.map
Normal file
1
search/search.min.js.map
Normal file
File diff suppressed because one or more lines are too long
64
src/js/search/Pagination.js
Normal file
64
src/js/search/Pagination.js
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import preact from "preact";
|
||||||
|
import { textLinkPrevious, textLinkNext } from "./translation";
|
||||||
|
/** @jsx preact.h */
|
||||||
|
|
||||||
|
export default function Pagination({ counter, start, settings, onPageSelect }) {
|
||||||
|
const pages = Math.ceil(counter / settings.show);
|
||||||
|
const page = start / settings.show;
|
||||||
|
|
||||||
|
let displayedPages;
|
||||||
|
if (page <= 2) {
|
||||||
|
// Display max three pages
|
||||||
|
displayedPages = Math.min(pages, 3);
|
||||||
|
} else {
|
||||||
|
// Display two more pages, but don't overflow
|
||||||
|
displayedPages = Math.min(pages, page + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [];
|
||||||
|
|
||||||
|
for (let f = 0; f < displayedPages; f++) {
|
||||||
|
if (f === page) {
|
||||||
|
items.push(<li className="current">{f + 1}</li>);
|
||||||
|
} else {
|
||||||
|
items.push(
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className="SearchResults__footer__link"
|
||||||
|
onClick={() => onPageSelect(f * settings.show)}
|
||||||
|
>
|
||||||
|
{f + 1}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="SearchResults__footer">
|
||||||
|
<ul className="SearchResults__footer__links Pager">
|
||||||
|
{start > 0 && (
|
||||||
|
<li className="Pager--prev">
|
||||||
|
<a
|
||||||
|
className="SearchResults__footer__link"
|
||||||
|
onClick={() => onPageSelect(start - settings.show)}
|
||||||
|
>
|
||||||
|
{textLinkPrevious}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
{items}
|
||||||
|
{page + 1 !== pages && (
|
||||||
|
<li className="Pager--next">
|
||||||
|
<a
|
||||||
|
className="SearchResults__footer__link"
|
||||||
|
onClick={() => onPageSelect(start + settings.show)}
|
||||||
|
>
|
||||||
|
{textLinkNext}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
55
src/js/search/Result.js
Normal file
55
src/js/search/Result.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import preact from "preact";
|
||||||
|
/** @jsx preact.h */
|
||||||
|
|
||||||
|
// TODO :: restore highlight
|
||||||
|
/*function highlightText(search, text) {
|
||||||
|
if (settings.highlightTerms) {
|
||||||
|
var pattern = new RegExp(
|
||||||
|
`(${search})`,
|
||||||
|
settings.highlightEveryTerm ? "gi" : "i"
|
||||||
|
);
|
||||||
|
text = text.replace(
|
||||||
|
pattern,
|
||||||
|
'<span class="SearchResults__highlight">$1</span>'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
export default function Result({ settings, item }) {
|
||||||
|
let description;
|
||||||
|
if (item.desc) {
|
||||||
|
description = item.desc
|
||||||
|
.split(" ")
|
||||||
|
.slice(0, settings.descriptiveWords)
|
||||||
|
.join(" ");
|
||||||
|
if (
|
||||||
|
item.desc.length < description.length &&
|
||||||
|
description.charAt(description.length - 1) !== "."
|
||||||
|
) {
|
||||||
|
description += " ...";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="SearchResult">
|
||||||
|
<div className="SearchResults__title">
|
||||||
|
<a href={settings.base_url + item.url}>{item.title}</a>
|
||||||
|
</div>
|
||||||
|
{settings.debug && (
|
||||||
|
<div className="SearchResults__debug">Score: {item.score}</div>
|
||||||
|
)}
|
||||||
|
{settings.showURL && (
|
||||||
|
<div className="SearchResults__url">
|
||||||
|
<a href={settings.base_url + item.url}>
|
||||||
|
{item.url.toLowerCase().replace(/https?:\/\//g, "")}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{description.desc && (
|
||||||
|
<div className="SearchResults__text">{description}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
161
src/js/search/Search.js
Normal file
161
src/js/search/Search.js
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import preact from "preact";
|
||||||
|
|
||||||
|
import Pagination from "./Pagination";
|
||||||
|
import Result from "./Result";
|
||||||
|
import {
|
||||||
|
textSearchCommonWordsIgnored,
|
||||||
|
textSearchNoResults,
|
||||||
|
textSearchOneCharacterOrMore,
|
||||||
|
textSearchOneResult,
|
||||||
|
textSearchResults,
|
||||||
|
textSearchShouldBeXOrMore,
|
||||||
|
textSearchTooShort
|
||||||
|
} from "./translation";
|
||||||
|
import { getResults, getSearchString } from "./utils";
|
||||||
|
|
||||||
|
/** @jsx preact.h */
|
||||||
|
|
||||||
|
export default class Search extends preact.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
search: this.props.settings.field.value || "",
|
||||||
|
start: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// "click", ".SearchResults__close"
|
||||||
|
handleClose = () => {
|
||||||
|
this.props.onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollTop = () => {
|
||||||
|
if (this.resultRef) {
|
||||||
|
this.resultRef.scrollTop = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handlePaginate = start => {
|
||||||
|
this.setState({ start }, this.scrollTop);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleChange = event => {
|
||||||
|
this.setState({ search: event.target.value, start: 0 }, this.scrollTop);
|
||||||
|
|
||||||
|
this.props.settings.field.value = event.target.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
getResults() {
|
||||||
|
const { settings, searchIndex } = this.props;
|
||||||
|
const { start } = this.state;
|
||||||
|
|
||||||
|
const searchString = getSearchString(
|
||||||
|
this.state.search.toLowerCase().trim()
|
||||||
|
);
|
||||||
|
const searchFor = searchString.searchFor;
|
||||||
|
|
||||||
|
const warnings = [];
|
||||||
|
let counter = 0;
|
||||||
|
let results = [];
|
||||||
|
|
||||||
|
if (searchFor.length < settings.minimumLength) {
|
||||||
|
if (searchString.hasStopWords) {
|
||||||
|
warnings.push(
|
||||||
|
`${textSearchNoResults}. ${textSearchCommonWordsIgnored}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
warnings.push(textSearchTooShort);
|
||||||
|
warnings.push(
|
||||||
|
settings.minimumLength === 1
|
||||||
|
? textSearchOneCharacterOrMore
|
||||||
|
: textSearchShouldBeXOrMore.replace(
|
||||||
|
"!min",
|
||||||
|
settings.minimumLength
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return { warnings, counter, results, start };
|
||||||
|
}
|
||||||
|
|
||||||
|
const found = getResults(
|
||||||
|
searchIndex,
|
||||||
|
searchString.searchFor,
|
||||||
|
searchString.isStandard
|
||||||
|
);
|
||||||
|
|
||||||
|
counter = found.length;
|
||||||
|
|
||||||
|
if (counter === 0) {
|
||||||
|
warnings.push(textSearchNoResults);
|
||||||
|
return { warnings, counter, results, start };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.showTitleCount) {
|
||||||
|
this.props.onTitleChange(`(${counter})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
results = found.filter(
|
||||||
|
(item, itemNumber) =>
|
||||||
|
itemNumber >= start && itemNumber < settings.show + start
|
||||||
|
);
|
||||||
|
|
||||||
|
return { warnings, counter, results, start };
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { settings } = this.props;
|
||||||
|
const { warnings, counter, results, start } = this.getResults();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="SearchResultsBackdrop" />
|
||||||
|
<div
|
||||||
|
className="SearchResults"
|
||||||
|
ref={el => (this.resultRef = el)}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="Search__field"
|
||||||
|
placeholder="Search..."
|
||||||
|
autoComplete="on"
|
||||||
|
autoSave="text_search"
|
||||||
|
type="search"
|
||||||
|
value={this.state.search}
|
||||||
|
onInput={this.handleChange}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
className="SearchResults__close"
|
||||||
|
onClick={this.handleClose}
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
<div className="SearchResults__count">
|
||||||
|
{counter === 1
|
||||||
|
? textSearchOneResult
|
||||||
|
: textSearchResults.replace("!count", counter)}
|
||||||
|
</div>
|
||||||
|
{warnings.map(warning => (
|
||||||
|
<div key={warning} className="SearchResults__warning">
|
||||||
|
{warning}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{results.map(result => (
|
||||||
|
<Result
|
||||||
|
key={result.title}
|
||||||
|
item={result}
|
||||||
|
settings={settings}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{counter > settings.show && (
|
||||||
|
<Pagination
|
||||||
|
counter={counter}
|
||||||
|
start={start}
|
||||||
|
settings={settings}
|
||||||
|
onPageSelect={this.handlePaginate}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
118
src/js/search/index.js
Normal file
118
src/js/search/index.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
import preact from "preact";
|
||||||
|
|
||||||
|
import Search from "./Search";
|
||||||
|
|
||||||
|
import { getURLP } from "./utils";
|
||||||
|
|
||||||
|
/** @jsx preact.h */
|
||||||
|
|
||||||
|
const originalTitle = document.title;
|
||||||
|
|
||||||
|
class SearchEngine {
|
||||||
|
constructor(options) {
|
||||||
|
this.settings = {
|
||||||
|
field: document.getElementById("search_input"),
|
||||||
|
show: 10,
|
||||||
|
showURL: true,
|
||||||
|
showTitleCount: true,
|
||||||
|
minimumLength: 3,
|
||||||
|
descriptiveWords: 25,
|
||||||
|
highlightTerms: true,
|
||||||
|
highlightEveryTerm: false,
|
||||||
|
contentLocation: "search/search_index.json",
|
||||||
|
debug: false,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
this.searchIndex = {
|
||||||
|
pages: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
if (!this.loadingPromise) {
|
||||||
|
this.loadingPromise = fetch(
|
||||||
|
this.settings.base_url + this.settings.contentLocation
|
||||||
|
)
|
||||||
|
.then(data => data.json())
|
||||||
|
.then(json => {
|
||||||
|
this.searchIndex = json;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.loadingPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if (getURLP("q")) {
|
||||||
|
this.settings.field.value = getURLP("q");
|
||||||
|
|
||||||
|
this.loadData().then(() => {
|
||||||
|
this.displaySearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings.field.addEventListener("keyup", event => {
|
||||||
|
// Start loading index once the user types text in the field, not before
|
||||||
|
this.loadData();
|
||||||
|
|
||||||
|
if (parseInt(event.keyCode, 10) === 13) {
|
||||||
|
this.loadData().then(() => {
|
||||||
|
this.displaySearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
keyUpHandler = e => {
|
||||||
|
if (e.which === 27) {
|
||||||
|
//escape
|
||||||
|
this.handleClose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClose = () => {
|
||||||
|
document.title = originalTitle;
|
||||||
|
|
||||||
|
document.removeEventListener("keyup", this.keyUpHandler);
|
||||||
|
|
||||||
|
document.body.classList.remove("with-search");
|
||||||
|
preact.render("", this.resultContainer, this.renderedElement);
|
||||||
|
this.resultContainer = null;
|
||||||
|
this.renderedElement = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
displaySearch() {
|
||||||
|
if (!this.resultContainer) {
|
||||||
|
this.resultContainer = document.createElement("div");
|
||||||
|
document.body.appendChild(this.resultContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("keyup", this.keyUpHandler);
|
||||||
|
|
||||||
|
this.renderedElement = preact.render(
|
||||||
|
<Search
|
||||||
|
searchIndex={this.searchIndex}
|
||||||
|
onClose={this.handleClose}
|
||||||
|
onTitleChange={title => {
|
||||||
|
document.title = `${title} ${originalTitle}`;
|
||||||
|
}}
|
||||||
|
settings={this.settings}
|
||||||
|
/>,
|
||||||
|
this.resultContainer
|
||||||
|
);
|
||||||
|
|
||||||
|
document.body.classList.add("with-search");
|
||||||
|
document.body.scrollTop = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main containers
|
||||||
|
|
||||||
|
function search(options) {
|
||||||
|
const instance = new SearchEngine(options);
|
||||||
|
instance.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare globally
|
||||||
|
window.search = search;
|
177
src/js/search/stopwords.js
Normal file
177
src/js/search/stopwords.js
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Stop words (list from http://www.ranks.nl/stopwords)
|
||||||
|
export default [
|
||||||
|
"a",
|
||||||
|
"about",
|
||||||
|
"above",
|
||||||
|
"after",
|
||||||
|
"again",
|
||||||
|
"against",
|
||||||
|
"all",
|
||||||
|
"am",
|
||||||
|
"an",
|
||||||
|
"and",
|
||||||
|
"any",
|
||||||
|
"are",
|
||||||
|
"aren't",
|
||||||
|
"as",
|
||||||
|
"at",
|
||||||
|
"be",
|
||||||
|
"because",
|
||||||
|
"been",
|
||||||
|
"before",
|
||||||
|
"being",
|
||||||
|
"below",
|
||||||
|
"between",
|
||||||
|
"both",
|
||||||
|
"but",
|
||||||
|
"by",
|
||||||
|
"can't",
|
||||||
|
"cannot",
|
||||||
|
"could",
|
||||||
|
"couldn't",
|
||||||
|
"did",
|
||||||
|
"didn't",
|
||||||
|
"do",
|
||||||
|
"does",
|
||||||
|
"doesn't",
|
||||||
|
"doing",
|
||||||
|
"don't",
|
||||||
|
"down",
|
||||||
|
"during",
|
||||||
|
"each",
|
||||||
|
"few",
|
||||||
|
"for",
|
||||||
|
"from",
|
||||||
|
"further",
|
||||||
|
"had",
|
||||||
|
"hadn't",
|
||||||
|
"has",
|
||||||
|
"hasn't",
|
||||||
|
"have",
|
||||||
|
"haven't",
|
||||||
|
"having",
|
||||||
|
"he",
|
||||||
|
"he'd",
|
||||||
|
"he'll",
|
||||||
|
"he's",
|
||||||
|
"her",
|
||||||
|
"here",
|
||||||
|
"here's",
|
||||||
|
"hers",
|
||||||
|
"herself",
|
||||||
|
"him",
|
||||||
|
"himself",
|
||||||
|
"his",
|
||||||
|
"how",
|
||||||
|
"how's",
|
||||||
|
"i",
|
||||||
|
"i'd",
|
||||||
|
"i'll",
|
||||||
|
"i'm",
|
||||||
|
"i've",
|
||||||
|
"if",
|
||||||
|
"in",
|
||||||
|
"into",
|
||||||
|
"is",
|
||||||
|
"isn't",
|
||||||
|
"it",
|
||||||
|
"it's",
|
||||||
|
"its",
|
||||||
|
"itself",
|
||||||
|
"let's",
|
||||||
|
"me",
|
||||||
|
"more",
|
||||||
|
"most",
|
||||||
|
"mustn't",
|
||||||
|
"my",
|
||||||
|
"myself",
|
||||||
|
"no",
|
||||||
|
"nor",
|
||||||
|
"not",
|
||||||
|
"of",
|
||||||
|
"off",
|
||||||
|
"on",
|
||||||
|
"once",
|
||||||
|
"only",
|
||||||
|
"or",
|
||||||
|
"other",
|
||||||
|
"ought",
|
||||||
|
"our",
|
||||||
|
"ours",
|
||||||
|
"ourselves",
|
||||||
|
"out",
|
||||||
|
"over",
|
||||||
|
"own",
|
||||||
|
"same",
|
||||||
|
"shan't",
|
||||||
|
"she",
|
||||||
|
"she'd",
|
||||||
|
"she'll",
|
||||||
|
"she's",
|
||||||
|
"should",
|
||||||
|
"shouldn't",
|
||||||
|
"so",
|
||||||
|
"some",
|
||||||
|
"such",
|
||||||
|
"than",
|
||||||
|
"that",
|
||||||
|
"that's",
|
||||||
|
"the",
|
||||||
|
"their",
|
||||||
|
"theirs",
|
||||||
|
"them",
|
||||||
|
"themselves",
|
||||||
|
"then",
|
||||||
|
"there",
|
||||||
|
"there's",
|
||||||
|
"these",
|
||||||
|
"they",
|
||||||
|
"they'd",
|
||||||
|
"they'll",
|
||||||
|
"they're",
|
||||||
|
"they've",
|
||||||
|
"this",
|
||||||
|
"those",
|
||||||
|
"through",
|
||||||
|
"to",
|
||||||
|
"too",
|
||||||
|
"under",
|
||||||
|
"until",
|
||||||
|
"up",
|
||||||
|
"very",
|
||||||
|
"was",
|
||||||
|
"wasn't",
|
||||||
|
"we",
|
||||||
|
"we'd",
|
||||||
|
"we'll",
|
||||||
|
"we're",
|
||||||
|
"we've",
|
||||||
|
"were",
|
||||||
|
"weren't",
|
||||||
|
"what",
|
||||||
|
"what's",
|
||||||
|
"when",
|
||||||
|
"when's",
|
||||||
|
"where",
|
||||||
|
"where's",
|
||||||
|
"which",
|
||||||
|
"while",
|
||||||
|
"who",
|
||||||
|
"who's",
|
||||||
|
"whom",
|
||||||
|
"why",
|
||||||
|
"why's",
|
||||||
|
"with",
|
||||||
|
"won't",
|
||||||
|
"would",
|
||||||
|
"wouldn't",
|
||||||
|
"you",
|
||||||
|
"you'd",
|
||||||
|
"you'll",
|
||||||
|
"you're",
|
||||||
|
"you've",
|
||||||
|
"your",
|
||||||
|
"yours",
|
||||||
|
"yourself",
|
||||||
|
"yourselves"
|
||||||
|
];
|
35
src/js/search/translation.js
Normal file
35
src/js/search/translation.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* eslint-disable camelcase */
|
||||||
|
const {
|
||||||
|
Link_previous,
|
||||||
|
Link_next,
|
||||||
|
Search_common_words_ignored,
|
||||||
|
Search_no_results,
|
||||||
|
Search_one_character_or_more,
|
||||||
|
Search_one_result,
|
||||||
|
Search_results,
|
||||||
|
Search_should_be_x_or_more,
|
||||||
|
Search_too_short
|
||||||
|
} = window.searchTranslation;
|
||||||
|
|
||||||
|
const textLinkPrevious = Link_previous;
|
||||||
|
const textLinkNext = Link_next;
|
||||||
|
const textSearchCommonWordsIgnored = Search_common_words_ignored;
|
||||||
|
const textSearchNoResults = Search_no_results;
|
||||||
|
const textSearchOneCharacterOrMore = Search_one_character_or_more;
|
||||||
|
const textSearchOneResult = Search_one_result;
|
||||||
|
const textSearchResults = Search_results;
|
||||||
|
const textSearchShouldBeXOrMore = Search_should_be_x_or_more;
|
||||||
|
const textSearchTooShort = Search_too_short;
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
export {
|
||||||
|
textLinkPrevious,
|
||||||
|
textLinkNext,
|
||||||
|
textSearchCommonWordsIgnored,
|
||||||
|
textSearchNoResults,
|
||||||
|
textSearchOneCharacterOrMore,
|
||||||
|
textSearchOneResult,
|
||||||
|
textSearchResults,
|
||||||
|
textSearchShouldBeXOrMore,
|
||||||
|
textSearchTooShort
|
||||||
|
};
|
120
src/js/search/utils.js
Normal file
120
src/js/search/utils.js
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import stopWords from "./stopwords";
|
||||||
|
|
||||||
|
export function getURLP(name) {
|
||||||
|
const elements = new RegExp(`[?|&]${name}=([^&;]+?)(&|#|;|$)`).exec(
|
||||||
|
window.location.search
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
decodeURIComponent(
|
||||||
|
((elements && elements[1]) || "").replace(/\+/g, "%20")
|
||||||
|
) || null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScore(searchFor, page) {
|
||||||
|
let score = 0;
|
||||||
|
const pat = new RegExp(searchFor, "gi");
|
||||||
|
|
||||||
|
if (page.title.search(pat) !== -1) {
|
||||||
|
score += 20 * page.title.match(pat).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.text.search(pat) !== -1) {
|
||||||
|
score += 20 * page.text.match(pat).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.tags.search(pat) !== -1) {
|
||||||
|
score += 10 * page.tags.match(pat).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page.url.search(pat) !== -1) {
|
||||||
|
score += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStandardScore(searchWords, page) {
|
||||||
|
let score = 0;
|
||||||
|
for (let f = 0; f < searchWords.length; f++) {
|
||||||
|
if (searchWords[f].match("^-")) {
|
||||||
|
const pat = new RegExp(searchWords[f].substring(1), "i");
|
||||||
|
if (
|
||||||
|
page.title.search(pat) !== -1 ||
|
||||||
|
page.text.search(pat) !== -1 ||
|
||||||
|
page.tags.search(pat) !== -1
|
||||||
|
) {
|
||||||
|
score = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
score += getScore(searchWords[f], page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSearchString(search) {
|
||||||
|
let isStandard = true;
|
||||||
|
let hasStopWords = false;
|
||||||
|
if (
|
||||||
|
(search.match('^"') && search.match('"$')) ||
|
||||||
|
(search.match("^'") && search.match("'$"))
|
||||||
|
) {
|
||||||
|
isStandard = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchFor;
|
||||||
|
if (isStandard) {
|
||||||
|
const searchWords = search.split(" ");
|
||||||
|
searchFor = searchWords
|
||||||
|
.filter(word => stopWords.indexOf(word) === -1)
|
||||||
|
.join(" ");
|
||||||
|
hasStopWords = search !== searchFor;
|
||||||
|
} else {
|
||||||
|
searchFor = searchFor.substring(1, searchFor.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
hasStopWords,
|
||||||
|
isStandard,
|
||||||
|
searchFor
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeResult(score, { title, url }, desc) {
|
||||||
|
return {
|
||||||
|
score,
|
||||||
|
title,
|
||||||
|
desc,
|
||||||
|
url
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getResults(index, searchFor, standard) {
|
||||||
|
const found = [];
|
||||||
|
|
||||||
|
let pages = index.pages;
|
||||||
|
|
||||||
|
// If a searchLanguage is set, filter out all other pages
|
||||||
|
if (window.searchLanguage) {
|
||||||
|
pages = pages.filter(
|
||||||
|
item => item.url.indexOf(`${window.searchLanguage}/`) === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchWords = searchFor.split(" ");
|
||||||
|
for (let i = 0; i < pages.length; i++) {
|
||||||
|
const score = standard
|
||||||
|
? getStandardScore(searchWords, pages[i])
|
||||||
|
: getScore(searchFor, pages[i]);
|
||||||
|
if (score !== 0) {
|
||||||
|
found.push(makeResult(score, pages[i], pages[i].text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
found.sort((a, b) => b.score - a.score);
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
@ -40,8 +40,8 @@
|
|||||||
} ?>
|
} ?>
|
||||||
|
|
||||||
<?php if ($params['html']['search']) { ?>
|
<?php if ($params['html']['search']) { ?>
|
||||||
<!-- Tipue Search -->
|
<!-- Search -->
|
||||||
<link href="<?= $base_url; ?>tipuesearch/tipuesearch.css" rel="stylesheet">
|
<link href="<?= $base_url; ?>search/search.css" rel="stylesheet">
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</head>
|
</head>
|
||||||
<body class="<?= $params['html']['float'] ? 'with-float' : ''; ?> <?= $this->section('classes'); ?>">
|
<body class="<?= $params['html']['float'] ? 'with-float' : ''; ?> <?= $this->section('classes'); ?>">
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<svg class="Search__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 451 451">
|
<svg class="Search__icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 451 451">
|
||||||
<path d="M447.05 428l-109.6-109.6c29.4-33.8 47.2-77.9 47.2-126.1C384.65 86.2 298.35 0 192.35 0 86.25 0 .05 86.3.05 192.3s86.3 192.3 192.3 192.3c48.2 0 92.3-17.8 126.1-47.2L428.05 447c2.6 2.6 6.1 4 9.5 4s6.9-1.3 9.5-4c5.2-5.2 5.2-13.8 0-19zM26.95 192.3c0-91.2 74.2-165.3 165.3-165.3 91.2 0 165.3 74.2 165.3 165.3s-74.1 165.4-165.3 165.4c-91.1 0-165.3-74.2-165.3-165.4z"/>
|
<path d="M447.05 428l-109.6-109.6c29.4-33.8 47.2-77.9 47.2-126.1C384.65 86.2 298.35 0 192.35 0 86.25 0 .05 86.3.05 192.3s86.3 192.3 192.3 192.3c48.2 0 92.3-17.8 126.1-47.2L428.05 447c2.6 2.6 6.1 4 9.5 4s6.9-1.3 9.5-4c5.2-5.2 5.2-13.8 0-19zM26.95 192.3c0-91.2 74.2-165.3 165.3-165.3 91.2 0 165.3 74.2 165.3 165.3s-74.1 165.4-165.3 165.4c-91.1 0-165.3-74.2-165.3-165.4z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<input type="search" id="tipue_search_input" class="Search__field" placeholder="<?=$this->translate("Search_placeholder") ?>" autocomplete="on"
|
<input type="search" id="search_input" class="Search__field" placeholder="<?=$this->translate("Search_placeholder") ?>" autocomplete="on"
|
||||||
results=25 autosave=text_search>
|
results=25 autosave=text_search>
|
||||||
</div>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
|
@ -22,16 +22,10 @@
|
|||||||
window.searchTranslation = <?= json_encode($search_translations) ?>;
|
window.searchTranslation = <?= json_encode($search_translations) ?>;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Tipue Search -->
|
<!-- Search -->
|
||||||
<script type="text/javascript" src="<?php echo $base_url; ?>tipuesearch/jquery-1.11.3.min.js"></script>
|
<script type="text/javascript" src="<?php echo $base_url; ?>search/search.min.js"></script>
|
||||||
<script type="text/javascript" src="<?php echo $base_url; ?>tipuesearch/tipuesearch.js"></script>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.onunload = function(){}; // force $(document).ready to be called on back/forward navigation in firefox
|
window.search({'base_url': '<?php echo $base_url?>'})
|
||||||
$(function() {
|
|
||||||
tipuesearch({
|
|
||||||
'base_url': '<?php echo $base_url?>'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<?php } ?>
|
<?php } ?>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -144,6 +144,8 @@ Components
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line selector-class-pattern */
|
||||||
.no-js .CodeToggler {
|
.no-js .CodeToggler {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -199,6 +201,7 @@ Components
|
|||||||
.Nav .Nav {
|
.Nav .Nav {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
|
|
||||||
|
/* stylelint-disable-next-line selector-class-pattern */
|
||||||
html:not(.no-js) & {
|
html:not(.no-js) & {
|
||||||
height: 0;
|
height: 0;
|
||||||
transition: height 400ms ease-in-out;
|
transition: height 400ms ease-in-out;
|
||||||
|
@ -43,6 +43,7 @@ body {
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
// mobile friendly sub-nav
|
// mobile friendly sub-nav
|
||||||
|
/* stylelint-disable-next-line selector-class-pattern */
|
||||||
html:not(.no-js) .Collapsible__content {
|
html:not(.no-js) .Collapsible__content {
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
5
tipuesearch/jquery-1.11.3.min.js
vendored
5
tipuesearch/jquery-1.11.3.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,374 +0,0 @@
|
|||||||
/*
|
|
||||||
Tipue Search 5.0
|
|
||||||
Copyright (c) 2015 Tipue
|
|
||||||
Tipue Search is released under the MIT License
|
|
||||||
http://www.tipue.com/search
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
(function ($) {
|
|
||||||
|
|
||||||
var originalTitle = document.title;
|
|
||||||
|
|
||||||
// Stop words (list from http://www.ranks.nl/stopwords)
|
|
||||||
var tipuesearch_stop_words = ["a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours", "ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves"];
|
|
||||||
|
|
||||||
// Internal strings
|
|
||||||
var tipuesearch_string_one_result = window.searchTranslation.Search_one_result;
|
|
||||||
var tipuesearch_string_results = window.searchTranslation.Search_results;
|
|
||||||
var tipuesearch_string_prev = window.searchTranslation.Link_previous;
|
|
||||||
var tipuesearch_string_next = window.searchTranslation.Link_next;
|
|
||||||
var tipuesearch_string_no_results = window.searchTranslation.Search_no_results;
|
|
||||||
var tipuesearch_string_common_words_ignored = window.searchTranslation.Search_common_words_ignored;
|
|
||||||
var tipuesearch_string_too_short = window.searchTranslation.Search_too_short;
|
|
||||||
var tipuesearch_string_one_character_or_more = window.searchTranslation.Search_one_character_or_more;
|
|
||||||
var tipuesearch_string_should_be_x_or_more = window.searchTranslation.Search_should_be_x_or_more;
|
|
||||||
|
|
||||||
// Main containers
|
|
||||||
var tipue_container, tipue_backdrop;
|
|
||||||
|
|
||||||
function getURLP(name) {
|
|
||||||
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [, ""])[1].replace(/\+/g, '%20')) || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeSearch() {
|
|
||||||
document.title = originalTitle;
|
|
||||||
|
|
||||||
$(document).off("keyup", keyUpHandler);
|
|
||||||
|
|
||||||
$("body").removeClass("with-search");
|
|
||||||
tipue_container.hide();
|
|
||||||
tipue_backdrop.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
function keyUpHandler(e) {
|
|
||||||
if (e.which == 27) { //escape
|
|
||||||
closeSearch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSearchString(searchFor) {
|
|
||||||
var standard = true;
|
|
||||||
var hasStopWords = false;
|
|
||||||
if ((searchFor.match("^\"") && searchFor.match("\"$")) || (searchFor.match("^'") && searchFor.match("'$"))) {
|
|
||||||
standard = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (standard) {
|
|
||||||
var d_w = searchFor.split(' ');
|
|
||||||
searchFor = '';
|
|
||||||
for (var i = 0; i < d_w.length; i++) {
|
|
||||||
var isStopWord = false;
|
|
||||||
for (var f = 0; f < tipuesearch_stop_words.length; f++) {
|
|
||||||
if (d_w[i] == tipuesearch_stop_words[f]) {
|
|
||||||
isStopWord = true;
|
|
||||||
hasStopWords = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isStopWord) {
|
|
||||||
searchFor = searchFor + ' ' + d_w[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
searchFor = $.trim(searchFor);
|
|
||||||
} else {
|
|
||||||
searchFor = searchFor.substring(1, searchFor.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
hasStopWords: hasStopWords,
|
|
||||||
isStandard: standard,
|
|
||||||
searchFor: searchFor
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getScore(searchFor, page) {
|
|
||||||
var score = 0;
|
|
||||||
var pat = new RegExp(searchFor, 'gi');
|
|
||||||
|
|
||||||
if (page.title.search(pat) != -1) {
|
|
||||||
score += (20 * page.title.match(pat).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.text.search(pat) != -1) {
|
|
||||||
score += (20 * page.text.match(pat).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.tags.search(pat) != -1) {
|
|
||||||
score += (10 * page.tags.match(pat).length);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page.url.search(pat) != -1) {
|
|
||||||
score += 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeResult(score, page, text) {
|
|
||||||
return {
|
|
||||||
"score": score,
|
|
||||||
"title": page.title,
|
|
||||||
"desc": text,
|
|
||||||
"url": page.url
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.tipuesearch = function (options) {
|
|
||||||
var settings = $.extend(
|
|
||||||
{
|
|
||||||
'field': $('#tipue_search_input'),
|
|
||||||
'show': 10,
|
|
||||||
'showURL': true,
|
|
||||||
'showTitleCount': true,
|
|
||||||
'minimumLength': 3,
|
|
||||||
'descriptiveWords': 25,
|
|
||||||
'highlightTerms': true,
|
|
||||||
'highlightEveryTerm': false,
|
|
||||||
'contentLocation': 'tipuesearch/tipuesearch_content.json',
|
|
||||||
'debug': false
|
|
||||||
}, options);
|
|
||||||
|
|
||||||
var tipuesearch_in = {
|
|
||||||
pages: []
|
|
||||||
};
|
|
||||||
|
|
||||||
$.ajax(
|
|
||||||
{
|
|
||||||
dataType: "json",
|
|
||||||
url: settings.base_url + settings.contentLocation,
|
|
||||||
async: false
|
|
||||||
})
|
|
||||||
.done(
|
|
||||||
function (json) {
|
|
||||||
tipuesearch_in = $.extend({}, json);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
if (getURLP('q')) {
|
|
||||||
settings.field.val(getURLP('q'));
|
|
||||||
getTipueSearch(0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.field.keyup(
|
|
||||||
function (event) {
|
|
||||||
if (event.keyCode == '13') {
|
|
||||||
getTipueSearch(0, true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function highlightText(search, text) {
|
|
||||||
if (settings.highlightTerms) {
|
|
||||||
var pattern = new RegExp('(' + search + ')', settings.highlightEveryTerm ? 'gi' : 'i');
|
|
||||||
text = text.replace(pattern, "<span class=\"SearchResults__highlight\">$1</span>");
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getResults(searchFor, standard) {
|
|
||||||
var found = [];
|
|
||||||
|
|
||||||
var pages = tipuesearch_in.pages;
|
|
||||||
|
|
||||||
// If a searchLanguage is set, filter out all other pages
|
|
||||||
if (window.searchLanguage) {
|
|
||||||
pages = pages.filter(function(item) {
|
|
||||||
return item.url.indexOf(window.searchLanguage + "/") === 0;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (standard) {
|
|
||||||
var d_w = searchFor.split(' ');
|
|
||||||
for (var i = 0; i < pages.length; i++) {
|
|
||||||
var score = 0;
|
|
||||||
var text = pages[i].text;
|
|
||||||
for (var f = 0; f < d_w.length; f++) {
|
|
||||||
if (d_w[f].match('^-')) {
|
|
||||||
var pat = new RegExp(d_w[f].substring(1), 'i');
|
|
||||||
if (pages[i].title.search(pat) != -1 || pages[i].text.search(pat) != -1 || pages[i].tags.search(pat) != -1) {
|
|
||||||
score = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
score += getScore(d_w[f], pages[i]);
|
|
||||||
text = highlightText(d_w[f], text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score != 0) {
|
|
||||||
found.push(makeResult(score, pages[i], text));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (var i = 0; i < pages.length; i++) {
|
|
||||||
var score = getScore(searchFor, pages[i]);
|
|
||||||
if (score != 0) {
|
|
||||||
found.push(makeResult(score, pages[i], highlightText(searchFor, pages[i].text)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
found.sort(function (a, b) {
|
|
||||||
return b.score - a.score
|
|
||||||
});
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTipueSearch(start, replace) {
|
|
||||||
|
|
||||||
if (!tipue_container) {
|
|
||||||
tipue_container = $(document.createElement("div"));
|
|
||||||
tipue_container.addClass('SearchResults');
|
|
||||||
document.body.appendChild(tipue_container.get(0));
|
|
||||||
|
|
||||||
tipue_backdrop = $(document.createElement("div"));
|
|
||||||
tipue_backdrop.addClass("SearchResultsBackdrop");
|
|
||||||
document.body.appendChild(tipue_backdrop.get(0));
|
|
||||||
|
|
||||||
tipue_container.on('click', '.SearchResults__close', closeSearch);
|
|
||||||
tipue_container.on('click', '.SearchResults__footer__link', function () {
|
|
||||||
var id_v = $(this).attr('id');
|
|
||||||
var id_a = id_v.split('_');
|
|
||||||
|
|
||||||
getTipueSearch(parseInt(id_a[0]), id_a[1]);
|
|
||||||
|
|
||||||
tipue_container.scrollTop(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
tipue_container.on('keyup paste', '.Search__field', function(event) {
|
|
||||||
settings.field.val($(this).val());
|
|
||||||
|
|
||||||
if (event.keyCode == '13') {
|
|
||||||
getTipueSearch(0, true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).keyup(keyUpHandler);
|
|
||||||
|
|
||||||
var output = '<input class="Search__field" placeholder="Search..." autocomplete="on" autosave="text_search" type="search" value="'+ settings.field.val() +'"><button class=SearchResults__close>×</button>';
|
|
||||||
|
|
||||||
var search = getSearchString($.trim(settings.field.val().toLowerCase()));
|
|
||||||
var searchFor = search.searchFor;
|
|
||||||
|
|
||||||
if (searchFor.length >= settings.minimumLength) {
|
|
||||||
var found = getResults(search.searchFor, search.isStandard);
|
|
||||||
var counter = found.length;
|
|
||||||
|
|
||||||
|
|
||||||
if (counter == 0) {
|
|
||||||
output += '<div class=SearchResults__warning>' + tipuesearch_string_no_results + '</div>';
|
|
||||||
} else {
|
|
||||||
if (settings.showTitleCount) {
|
|
||||||
document.title = '(' + counter + ') ' + originalTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter == 1) {
|
|
||||||
output += '<div class="SearchResults__count">' + tipuesearch_string_one_result + '</div>';
|
|
||||||
} else {
|
|
||||||
output += '<div class="SearchResults__count">' + tipuesearch_string_results.replace('!count', counter) + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
var l_o = 0;
|
|
||||||
for (var i = 0; i < found.length; i++) {
|
|
||||||
if (l_o >= start && l_o < settings.show + start) {
|
|
||||||
output += '<div class="SearchResults__title"><a href="' + settings.base_url + found[i].url + '"' + '>' + found[i].title + '</a></div>';
|
|
||||||
|
|
||||||
if (settings.debug) {
|
|
||||||
output += '<div class="SearchResults__debug">Score: ' + found[i].score + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.showURL) {
|
|
||||||
var s_u = found[i].url.toLowerCase();
|
|
||||||
if (s_u.indexOf('http://') == 0) {
|
|
||||||
s_u = s_u.slice(7);
|
|
||||||
}
|
|
||||||
output += '<div class="SearchResults__url"><a href="' + settings.base_url + found[i].url + '"' + '>' + s_u + '</a></div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found[i].desc) {
|
|
||||||
var t = found[i].desc;
|
|
||||||
var t_d = '';
|
|
||||||
var t_w = t.split(' ');
|
|
||||||
if (t_w.length < settings.descriptiveWords) {
|
|
||||||
t_d = t;
|
|
||||||
} else {
|
|
||||||
for (var f = 0; f < settings.descriptiveWords; f++) {
|
|
||||||
t_d += t_w[f] + ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t_d = $.trim(t_d);
|
|
||||||
if (t_d.charAt(t_d.length - 1) != '.') {
|
|
||||||
t_d += ' ...';
|
|
||||||
}
|
|
||||||
output += '<div class="SearchResults__text">' + t_d + '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l_o++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter > settings.show) {
|
|
||||||
var pages = Math.ceil(counter / settings.show);
|
|
||||||
var page = (start / settings.show);
|
|
||||||
output += '<div class="SearchResults__footer"><ul class="SearchResults__footer__links Pager">';
|
|
||||||
|
|
||||||
if (start > 0) {
|
|
||||||
output += '<li class="Pager--prev"><a class="SearchResults__footer__link" id="' + (start - settings.show) + '_' + replace + '">' + tipuesearch_string_prev + '</a></li>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page <= 2) {
|
|
||||||
var p_b = pages;
|
|
||||||
if (pages > 3) {
|
|
||||||
p_b = 3;
|
|
||||||
}
|
|
||||||
for (var f = 0; f < p_b; f++) {
|
|
||||||
if (f == page) {
|
|
||||||
output += '<li class="current">' + (f + 1) + '</li>';
|
|
||||||
} else {
|
|
||||||
output += '<li><a class="SearchResults__footer__link" id="' + (f * settings.show) + '_' + replace + '">' + (f + 1) + '</a></li>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var p_b = page + 2;
|
|
||||||
if (p_b > pages) {
|
|
||||||
p_b = pages;
|
|
||||||
}
|
|
||||||
for (var f = page - 1; f < p_b; f++) {
|
|
||||||
if (f == page) {
|
|
||||||
output += '<li class="current">' + (f + 1) + '</li>';
|
|
||||||
} else {
|
|
||||||
output += '<li><a class="SearchResults__footer__link" id="' + (f * settings.show) + '_' + replace + '">' + (f + 1) + '</a></li>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (page + 1 != pages) {
|
|
||||||
output += '<li class="Pager--next"><a class="SearchResults__footer__link" id="' + (start + settings.show) + '_' + replace + '">' + tipuesearch_string_next + '</a></li>';
|
|
||||||
}
|
|
||||||
|
|
||||||
output += '</ul></div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (search.hasStopWords) {
|
|
||||||
output += '<div class=SearchResults__warning>' + tipuesearch_string_no_results + '. ' + tipuesearch_string_common_words_ignored + '</div>';
|
|
||||||
} else {
|
|
||||||
output += '<div class=SearchResults__warning>' + tipuesearch_string_too_short + '</div>';
|
|
||||||
if (settings.minimumLength == 1) {
|
|
||||||
output += '<div class=SearchResults__warning>' + tipuesearch_string_one_character_or_more + '</div>';
|
|
||||||
} else {
|
|
||||||
output += '<div class=SearchResults__warning>' + tipuesearch_string_should_be_x_or_more.replace("!min", settings.minimumLength) + '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$("body").addClass("with-search").scrollTop(0);
|
|
||||||
tipue_backdrop.show();
|
|
||||||
tipue_container.scrollTop(0);
|
|
||||||
tipue_container.show().html(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})(jQuery);
|
|
Loading…
Reference in New Issue
Block a user