Compare commits
9 commits
Author | SHA1 | Date | |
---|---|---|---|
|
9151b9eb32 | ||
|
0e7fb74acf | ||
|
ac9d112c13 | ||
|
377c9d5b8b | ||
|
abf143eeab | ||
|
9109c93e6a | ||
|
e635581228 | ||
|
117dead94a | ||
|
cd513bd796 |
26 changed files with 934 additions and 1269 deletions
|
@ -4,6 +4,9 @@
|
|||
|
||||
This repository contains the webapp files to be used with [BaseX](https://basex.org/) running as a webapp, plus some scripts to convert between different XML representations.
|
||||
|
||||
This is a rewrite that dispenses with most JS code, apart from some basic logic that is directly encoded in attributes.
|
||||
This is achieved by using HTMX.
|
||||
|
||||
Original code written by Oleg Belyaev in 2024–2025.
|
||||
|
||||
A public version of this database is currently available [here](https://abaev.belyaev.io/dict).
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
var md5 = (function() {
|
||||
var MD5 = function (d) {
|
||||
return M(V(Y(X(d), 8 * d.length)))
|
||||
}
|
||||
function M (d) {
|
||||
for (var _, m = '0123456789abcdef', f = '', r = 0; r < d.length; r++) {
|
||||
_ = d.charCodeAt(r)
|
||||
f += m.charAt(_ >>> 4 & 15) + m.charAt(15 & _)
|
||||
}
|
||||
return f
|
||||
}
|
||||
function X (d) {
|
||||
for (var _ = Array(d.length >> 2), m = 0; m < _.length; m++) {
|
||||
_[m] = 0
|
||||
}
|
||||
for (m = 0; m < 8 * d.length; m += 8) {
|
||||
_[m >> 5] |= (255 & d.charCodeAt(m / 8)) << m % 32
|
||||
}
|
||||
return _
|
||||
}
|
||||
function V (d) {
|
||||
for (var _ = '', m = 0; m < 32 * d.length; m += 8) _ += String.fromCharCode(d[m >> 5] >>> m % 32 & 255)
|
||||
return _
|
||||
}
|
||||
function Y (d, _) {
|
||||
d[_ >> 5] |= 128 << _ % 32
|
||||
d[14 + (_ + 64 >>> 9 << 4)] = _
|
||||
for (var m = 1732584193, f = -271733879, r = -1732584194, i = 271733878, n = 0; n < d.length; n += 16) {
|
||||
var h = m
|
||||
var t = f
|
||||
var g = r
|
||||
var e = i
|
||||
f = md5ii(f = md5ii(f = md5ii(f = md5ii(f = md5hh(f = md5hh(f = md5hh(f = md5hh(f = md5gg(f = md5gg(f = md5gg(f = md5gg(f = md5ff(f = md5ff(f = md5ff(f = md5ff(f, r = md5ff(r, i = md5ff(i, m = md5ff(m, f, r, i, d[n + 0], 7, -680876936), f, r, d[n + 1], 12, -389564586), m, f, d[n + 2], 17, 606105819), i, m, d[n + 3], 22, -1044525330), r = md5ff(r, i = md5ff(i, m = md5ff(m, f, r, i, d[n + 4], 7, -176418897), f, r, d[n + 5], 12, 1200080426), m, f, d[n + 6], 17, -1473231341), i, m, d[n + 7], 22, -45705983), r = md5ff(r, i = md5ff(i, m = md5ff(m, f, r, i, d[n + 8], 7, 1770035416), f, r, d[n + 9], 12, -1958414417), m, f, d[n + 10], 17, -42063), i, m, d[n + 11], 22, -1990404162), r = md5ff(r, i = md5ff(i, m = md5ff(m, f, r, i, d[n + 12], 7, 1804603682), f, r, d[n + 13], 12, -40341101), m, f, d[n + 14], 17, -1502002290), i, m, d[n + 15], 22, 1236535329), r = md5gg(r, i = md5gg(i, m = md5gg(m, f, r, i, d[n + 1], 5, -165796510), f, r, d[n + 6], 9, -1069501632), m, f, d[n + 11], 14, 643717713), i, m, d[n + 0], 20, -373897302), r = md5gg(r, i = md5gg(i, m = md5gg(m, f, r, i, d[n + 5], 5, -701558691), f, r, d[n + 10], 9, 38016083), m, f, d[n + 15], 14, -660478335), i, m, d[n + 4], 20, -405537848), r = md5gg(r, i = md5gg(i, m = md5gg(m, f, r, i, d[n + 9], 5, 568446438), f, r, d[n + 14], 9, -1019803690), m, f, d[n + 3], 14, -187363961), i, m, d[n + 8], 20, 1163531501), r = md5gg(r, i = md5gg(i, m = md5gg(m, f, r, i, d[n + 13], 5, -1444681467), f, r, d[n + 2], 9, -51403784), m, f, d[n + 7], 14, 1735328473), i, m, d[n + 12], 20, -1926607734), r = md5hh(r, i = md5hh(i, m = md5hh(m, f, r, i, d[n + 5], 4, -378558), f, r, d[n + 8], 11, -2022574463), m, f, d[n + 11], 16, 1839030562), i, m, d[n + 14], 23, -35309556), r = md5hh(r, i = md5hh(i, m = md5hh(m, f, r, i, d[n + 1], 4, -1530992060), f, r, d[n + 4], 11, 1272893353), m, f, d[n + 7], 16, -155497632), i, m, d[n + 10], 23, -1094730640), r = md5hh(r, i = md5hh(i, m = md5hh(m, f, r, i, d[n + 13], 4, 681279174), f, r, d[n + 0], 11, -358537222), m, f, d[n + 3], 16, -722521979), i, m, d[n + 6], 23, 76029189), r = md5hh(r, i = md5hh(i, m = md5hh(m, f, r, i, d[n + 9], 4, -640364487), f, r, d[n + 12], 11, -421815835), m, f, d[n + 15], 16, 530742520), i, m, d[n + 2], 23, -995338651), r = md5ii(r, i = md5ii(i, m = md5ii(m, f, r, i, d[n + 0], 6, -198630844), f, r, d[n + 7], 10, 1126891415), m, f, d[n + 14], 15, -1416354905), i, m, d[n + 5], 21, -57434055), r = md5ii(r, i = md5ii(i, m = md5ii(m, f, r, i, d[n + 12], 6, 1700485571), f, r, d[n + 3], 10, -1894986606), m, f, d[n + 10], 15, -1051523), i, m, d[n + 1], 21, -2054922799), r = md5ii(r, i = md5ii(i, m = md5ii(m, f, r, i, d[n + 8], 6, 1873313359), f, r, d[n + 15], 10, -30611744), m, f, d[n + 6], 15, -1560198380), i, m, d[n + 13], 21, 1309151649), r = md5ii(r, i = md5ii(i, m = md5ii(m, f, r, i, d[n + 4], 6, -145523070), f, r, d[n + 11], 10, -1120210379), m, f, d[n + 2], 15, 718787259), i, m, d[n + 9], 21, -343485551)
|
||||
m = safeadd(m, h)
|
||||
f = safeadd(f, t)
|
||||
r = safeadd(r, g)
|
||||
i = safeadd(i, e)
|
||||
}
|
||||
return [m, f, r, i]
|
||||
}
|
||||
function md5cmn (d, _, m, f, r, i) {
|
||||
return safeadd(bitrol(safeadd(safeadd(_, d), safeadd(f, i)), r), m)
|
||||
}
|
||||
function md5ff (d, _, m, f, r, i, n) {
|
||||
return md5cmn(_ & m | ~_ & f, d, _, r, i, n)
|
||||
}
|
||||
function md5gg (d, _, m, f, r, i, n) {
|
||||
return md5cmn(_ & f | m & ~f, d, _, r, i, n)
|
||||
}
|
||||
function md5hh (d, _, m, f, r, i, n) {
|
||||
return md5cmn(_ ^ m ^ f, d, _, r, i, n)
|
||||
}
|
||||
function md5ii (d, _, m, f, r, i, n) {
|
||||
return md5cmn(m ^ (_ | ~f), d, _, r, i, n)
|
||||
}
|
||||
function safeadd (d, _) {
|
||||
var m = (65535 & d) + (65535 & _)
|
||||
return (d >> 16) + (_ >> 16) + (m >> 16) << 16 | 65535 & m
|
||||
}
|
||||
function bitrol (d, _) {
|
||||
return d << _ | d >>> 32 - _
|
||||
}
|
||||
function MD5Unicode(buffer){
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
buffer = new TextEncoder().encode(typeof buffer==='string' ? buffer : JSON.stringify(buffer));
|
||||
}
|
||||
var binary = [];
|
||||
var bytes = new Uint8Array(buffer);
|
||||
for (var i = 0, il = bytes.byteLength; i < il; i++) {
|
||||
binary.push(String.fromCharCode(bytes[i]));
|
||||
}
|
||||
return MD5(binary.join(''));
|
||||
}
|
||||
|
||||
return MD5Unicode;
|
||||
})();
|
||||
|
||||
$( document ).ready(function() {
|
||||
// GLOBALS;
|
||||
|
||||
// Link elements that do not have href out of the box
|
||||
$('.link').on("click", function () {
|
||||
window.location=$(this)[0].dataset.href+window.location.search+window.location.hash;
|
||||
});
|
||||
|
||||
$('#abv-search').on("submit", function () {
|
||||
$('#abv-search-icon').replaceWith('<div class="loader"></div>');
|
||||
});
|
||||
|
||||
// SEARCH RESULTS MODAL
|
||||
$('#abv-btn-searchResults').on("click", function () {
|
||||
$('#modal_searchResults')[0].showModal();
|
||||
});
|
||||
|
||||
// Close search modal
|
||||
$('.abv-close-search').on("click", function () {
|
||||
$('#modal_searchResults')[0].close();
|
||||
});
|
||||
|
||||
$('#modal_searchResults').on("toggle", async function () {
|
||||
// let html = "";
|
||||
async function getResults() {
|
||||
let url = "./api/search/current";
|
||||
const response = await fetch(url);
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
async function getEntryForm(id) {
|
||||
let url = `./api/entries/${md5(id).toUpperCase()}`;
|
||||
const response = await fetch(url);
|
||||
const result = await response.json();
|
||||
return result.form;
|
||||
}
|
||||
searchResults = await getResults();
|
||||
for (result of searchResults) {
|
||||
let pos = searchResults.indexOf(result);
|
||||
// let form = await getEntryForm(result.entry);
|
||||
let html = `
|
||||
<tr>
|
||||
<td>${pos+1}</td>
|
||||
<td>
|
||||
<a href="./search/position?p=${pos+1}">${result.entryForm}</a>
|
||||
</td>
|
||||
<td>
|
||||
${result.fragment}
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
$(this).find('tbody').append($(html));
|
||||
}
|
||||
// $(this).find('tbody').append($(html));
|
||||
});
|
||||
});
|
|
@ -14,7 +14,7 @@ Root
|
|||
|
||||
:root {}
|
||||
html {}
|
||||
body {}
|
||||
body {height: 100vh;}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
|
@ -30,8 +30,7 @@ h5 {}
|
|||
h6 {}
|
||||
|
||||
body > header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
max-height: 10vh;
|
||||
background-color: #FCF9F5;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
|
@ -39,8 +38,8 @@ body > header {
|
|||
max-width: 100vw;
|
||||
/* border-bottom-width: thin;
|
||||
border-bottom-style: solid;*/
|
||||
margin-bottom: 10px !important;
|
||||
padding-bottom: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
padding-top: 0px !important;
|
||||
/* padding-right: 1em !important;
|
||||
padding-left: 1em !important;*/
|
||||
|
@ -69,6 +68,10 @@ body > header {
|
|||
body>main {
|
||||
max-width:950px
|
||||
}
|
||||
article.abv-entry {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(10%,15%) minmax(0,5%) minmax(75%,90%);
|
||||
}
|
||||
}
|
||||
@media (min-width:1280px) {
|
||||
body>footer>nav,
|
||||
|
@ -87,7 +90,9 @@ body > header {
|
|||
|
||||
body>header>nav,
|
||||
body>footer>nav {
|
||||
margin: auto;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
max-height: 10vh;
|
||||
--pico-primary: #8D0B02 !important;
|
||||
--pico-primary-background: #8D0B02 !important;
|
||||
--pico-primary-border: #8D0B02 !important;
|
||||
|
@ -124,12 +129,10 @@ nav li.abv-menu-re {
|
|||
filter: gray;
|
||||
}
|
||||
|
||||
main {}
|
||||
|
||||
article.abv-entry {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0px,15%) 5% minmax(50%,80%);
|
||||
align-items: baseline;
|
||||
main {
|
||||
height: 85vh;
|
||||
overflow-y: scroll;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
section {}
|
||||
|
@ -292,6 +295,13 @@ table {}
|
|||
caption {}
|
||||
col:first-child {}
|
||||
thead {}
|
||||
|
||||
table#abv-search-results thead {
|
||||
padding-top: 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
tbody {}
|
||||
tfoot {}
|
||||
tr {}
|
||||
|
@ -309,19 +319,17 @@ legend {}
|
|||
|
||||
form {}
|
||||
|
||||
header form {
|
||||
margin-bottom: 0 !important;
|
||||
/*table#abv-search-results {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header form button {
|
||||
padding-left: 1em !important;
|
||||
padding-right: 1em !important;
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
table#abv-search-results tbody {
|
||||
height:100%;
|
||||
overflow-y: scroll;
|
||||
}*/
|
||||
|
||||
header form select {
|
||||
max-width:35%;
|
||||
form#abv-search select {
|
||||
max-width:15%;
|
||||
}
|
||||
|
||||
label {}
|
||||
|
@ -395,6 +403,10 @@ div.index-submit {
|
|||
float: right;
|
||||
}
|
||||
|
||||
div.notes {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
optgroup {}
|
||||
option {}
|
||||
option:checked {}
|
||||
|
@ -413,7 +425,6 @@ button#open-leftbar{
|
|||
left: 0;
|
||||
/*height: 1ex;*/
|
||||
/*line-height: 1ex;*/
|
||||
/*width: 1em;*/
|
||||
}
|
||||
|
||||
button#close-leftbar{
|
||||
|
@ -423,6 +434,23 @@ button#close-leftbar{
|
|||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
button#open-leftbar {
|
||||
width: 1em;
|
||||
height: 10ex;
|
||||
margin: 0 auto;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
button#close-leftbar {
|
||||
width: 1em;
|
||||
height: 10ex;
|
||||
margin: 0 auto;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* This matches forms or form elements that are invalid *and* have been
|
||||
interacted with. Note that:
|
||||
1) You may need to add the `interacted` class to forms yourself
|
||||
|
@ -466,28 +494,6 @@ dialog > article {
|
|||
max-height: 85vh !important;
|
||||
}
|
||||
|
||||
dialog#modal_searchResults > article {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
dialog#modal_searchResults > article > table > thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1055;
|
||||
}
|
||||
|
||||
dialog#modal_searchResults > article > header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1055;
|
||||
}
|
||||
dialog#modal_searchResults > article > footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 1055;
|
||||
}
|
||||
|
||||
/*dialog#modal_searchResults > article > p > table > tbody {
|
||||
height:100%;
|
||||
overflow-y: scroll;
|
||||
|
@ -534,30 +540,11 @@ span.abv-text-ul {
|
|||
span.abv-text-sc {
|
||||
font-variant-caps: small-caps;
|
||||
}
|
||||
span.abv-name {
|
||||
font-variant-caps: small-caps;
|
||||
}
|
||||
|
||||
/*
|
||||
This file evolved from Natural Selection:
|
||||
https://github.com/frontaid/natural-selection
|
||||
*/
|
||||
|
||||
.icons{
|
||||
text-align: center;
|
||||
width: 1.5em;
|
||||
float: left;
|
||||
padding-right: 0.5vw;
|
||||
}
|
||||
|
||||
.loader {
|
||||
border: 2px solid #f3f3f3;
|
||||
border-radius: 50%;
|
||||
border-top: 2px solid #3498db;
|
||||
width: 2ex;
|
||||
height: 2ex;
|
||||
-webkit-animation: spin 2s linear infinite; /* Safari */
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
$( document ).ready(function() {
|
||||
$('#abv-select-lang').on('input', function() {
|
||||
let $selected = $(this).children('option:selected');
|
||||
let url = `./api/languages/${$selected.attr('value')}`;
|
||||
|
||||
fetch(url)
|
||||
.then(res => res.json())
|
||||
.then(out => {
|
||||
let $select_word = $('#abv-select-word');
|
||||
let $select_entry = $('#abv-select-entry');
|
||||
$select_word.empty();
|
||||
$select_entry.empty();
|
||||
$('#btn-index-submit').prop('hidden',true);
|
||||
for (w of out.words) {
|
||||
$select_word.append(new Option(w.text,w.id))
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
});
|
||||
|
||||
$('#abv-select-word').on('input', function() {
|
||||
let $selected = $(this).children('option:selected');
|
||||
let wordid = $selected.attr('value');
|
||||
let lang = $('#abv-select-lang').children('option:selected').attr('value');
|
||||
let url = `./api/languages/${lang}/words/${wordid}`;
|
||||
|
||||
fetch(url)
|
||||
.then(res => res.json())
|
||||
.then(out => {
|
||||
let $select_entry = $('#abv-select-entry');
|
||||
$select_entry.empty();
|
||||
$('#btn-index-submit').prop('hidden',true);
|
||||
for (e of out.entries) {
|
||||
url_entry = `./api/entries/${e.id}`;
|
||||
fetch(url_entry)
|
||||
.then(res => res.json())
|
||||
.then(out => {
|
||||
let entrystring = '';
|
||||
let glosses = [];
|
||||
for (r of e.refs) {
|
||||
if (r.glosses) {
|
||||
for(g of r.glosses){
|
||||
glosses.push(`‘${g}’`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(glosses.length > 0) {
|
||||
entrystring = `${glosses.join('; ')}: `
|
||||
}
|
||||
opt = $(`<option value="${out.xmlid}" data-abv-xpath="${e.refs[0].path}">${entrystring}<i>${out.form}</i>
|
||||
${out.glosses.map(g => `‘${g}’`).join(', ')}</option>`);
|
||||
$select_entry.append(opt)
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
}
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
});
|
||||
|
||||
$('#abv-select-entry').on("input", function () {
|
||||
$('#btn-index-submit').removeAttr('hidden');
|
||||
})
|
||||
|
||||
function goToEntry() {
|
||||
let $selected = $('#abv-select-entry').children('option:selected');
|
||||
let entry = $selected.attr('value');
|
||||
let path = $selected.attr('data-abv-xpath');
|
||||
window.location.replace('./dict/' + entry + `?entry=${entry}&xpath=${path}`);
|
||||
}
|
||||
|
||||
$('#abv-select-entry').on("dblclick", goToEntry);
|
||||
$('#btn-index-submit').on("click", goToEntry);
|
||||
});
|
186
static/abaev.js
186
static/abaev.js
|
@ -1,186 +0,0 @@
|
|||
$( document ).ready(function() {
|
||||
// GLOBALS;
|
||||
|
||||
// Link elements that do not have href out of the box
|
||||
$('.link').on("click", function () {
|
||||
window.location=$(this)[0].dataset.href+window.location.search+window.location.hash;
|
||||
});
|
||||
|
||||
$('#abv-search').on("submit", function () {
|
||||
$('#abv-search-icon').replaceWith('<div class="loader"></div>');
|
||||
});
|
||||
|
||||
// INFINITE SCROLL
|
||||
|
||||
// init Infinite Scroll
|
||||
$('main').infiniteScroll({
|
||||
path: '.pagination__next',
|
||||
append: 'div.abv-lex',
|
||||
//status: '.scroller-status',
|
||||
//hideNav: '.pagination',
|
||||
});
|
||||
|
||||
// MAP MODAL
|
||||
// This event binding is done on page load and also whenever new entries are loaded.
|
||||
bindMapOpen = function () {
|
||||
let url = `./dict/map-info/${$(this).data('abv-entry')}`;
|
||||
|
||||
fetch(url)
|
||||
.then(res => res.json())
|
||||
.then(out => {
|
||||
$('#modal_map').data('map',out);
|
||||
$('#modal_map')[0].showModal();
|
||||
})
|
||||
.catch(err => console.log(err));
|
||||
};
|
||||
|
||||
// Event to open map modal.
|
||||
$('.abv-map').on("click", bindMapOpen);
|
||||
|
||||
$('main').on( 'append.infiniteScroll', function( event, body, path, response ) {
|
||||
$('.abv-map').on("click", bindMapOpen);
|
||||
});
|
||||
|
||||
// Close map modal
|
||||
$('.abv-close-map').on("click", function () {
|
||||
$('#modal_map')[0].close();
|
||||
});
|
||||
|
||||
// Load map data when dialog opens
|
||||
$('#modal_map').on("toggle", function () {
|
||||
map = document.getElementById('map_display');
|
||||
var layout = {
|
||||
// title: {
|
||||
// text: 'Canadian cities',
|
||||
// font: {
|
||||
// family: 'Droid Serif, serif',
|
||||
// size: 16
|
||||
// }
|
||||
// },
|
||||
geo: {
|
||||
scope: 'world',
|
||||
resolution: 50,
|
||||
fitbounds: 'locations',
|
||||
showrivers: true,
|
||||
rivercolor: '#fff',
|
||||
showlakes: true,
|
||||
lakecolor: '#fff',
|
||||
showland: true,
|
||||
landcolor: '#EAEAAE',
|
||||
showcountries: false,
|
||||
subunitcolor: '#d3d3d3'
|
||||
}
|
||||
};
|
||||
Plotly.newPlot(map, [$(this).data('map')], layout);
|
||||
});
|
||||
|
||||
// HASH NAVIGATION
|
||||
// If the hash is on the page, scroll a little bit above to account for top nav height
|
||||
if ( $(decodeURI(document.location.hash)).length > 0) {
|
||||
window.scrollBy(0,-$('header').height());
|
||||
}
|
||||
|
||||
// If the hash is not on the page, go to the right page by using the ID interface
|
||||
$( window ).on('hashchange', function() {
|
||||
const hash = decodeURI(document.location.hash);
|
||||
|
||||
if ( hash.startsWith('#entry_') ) {
|
||||
if ( $(hash).length == 0) {
|
||||
window.location.replace(document.location.pathname + '/' + document.location.hash.substring(1));
|
||||
}
|
||||
else {
|
||||
window.scrollBy(0,-$('header').height());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ENTRY LIST SIDE PANEL
|
||||
|
||||
// Function to produce HTML for the entry list
|
||||
function filterEntryList(list, filter, showSubentries) {
|
||||
let listHtml = "";
|
||||
for (entry of list) {
|
||||
let entryID = entry.xmlid;
|
||||
let entryForm = entry.form;
|
||||
if (entryForm.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").startsWith(filter)
|
||||
&& (!entry.subentry || showSubentries)
|
||||
) {
|
||||
listHtml += `<li id="link_${entryID}"${entry.subentry ? ' class="abv-menu-re"' : ''}"><a href="./dict/${entryID}">${entryForm}</a></li>`
|
||||
}
|
||||
}
|
||||
listHtml = $(`<ul>${listHtml}</ul>`);
|
||||
$("#entrylist").append(listHtml);
|
||||
}
|
||||
|
||||
// Fetch entries from the server when page loads
|
||||
var entries = [];
|
||||
|
||||
fetch('./api/entries')
|
||||
.then(res => res.json())
|
||||
.then(out => {
|
||||
entries = out;
|
||||
filterEntryList(entries, '', false);
|
||||
})
|
||||
.then(err => console.log(err));
|
||||
|
||||
// Function to scroll the entry list to the selected entry OR to the first entry displayed on the current page if there is no hash in the URL
|
||||
function scrollView() {
|
||||
var $container = $('#entrylist')
|
||||
if (document.location.hash.length > 0) {
|
||||
var $scrollTo = $(`#link_${$(decodeURI(document.location.hash)).attr('id')}`);
|
||||
}
|
||||
else {
|
||||
var $scrollTo = $(`#link_${$('article:first').attr('id')}`);
|
||||
}
|
||||
$container.scrollTop(
|
||||
$scrollTo.offset().top - $container.offset().top + $container.scrollTop())
|
||||
}
|
||||
|
||||
// Click to open the offcanvas entry list
|
||||
$( '#open-leftbar' ).on('click', function() {
|
||||
$("#leftbar").show();
|
||||
$("main")[0].style.marginLeft = "250px";
|
||||
$("header nav")[0].style.marginLeft = "250px";
|
||||
$("footer nav")[0].style.marginLeft = "250px";
|
||||
$("#open-leftbar").hide();
|
||||
$("#close-leftbar").show();
|
||||
|
||||
scrollView();
|
||||
});
|
||||
|
||||
// Click to close the offcanvas entry list
|
||||
$( '#close-leftbar' ).on('click', function() {
|
||||
$("#leftbar").hide();
|
||||
// $("#leftbar")[0].style.paddingLeft = null;
|
||||
$("main")[0].style.marginLeft = null;
|
||||
$("header nav")[0].style.marginLeft = null;
|
||||
$("footer nav")[0].style.marginLeft = null;
|
||||
$("#open-leftbar").show();
|
||||
$("#close-leftbar").hide();
|
||||
})
|
||||
|
||||
// Quick filter functionality
|
||||
$('#filter-entries').on("keyup", function () {
|
||||
$("#entrylist > ul").remove();
|
||||
var value = $(this).val().toLowerCase();
|
||||
filterEntryList(entries, value, $('#show-re').prop('checked'))
|
||||
// var value = $(this).val().toLowerCase();
|
||||
// if (value.length > 0) {
|
||||
// $("#entrylist > ul > li:not([hidden])").filter(function () {
|
||||
// return $(this).toggle($(this).children('a').text().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").startsWith(value))
|
||||
// });
|
||||
// }
|
||||
// else {
|
||||
// $("#entrylist > ul > li").show();
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
});
|
||||
|
||||
// Event handler to show subentries
|
||||
$('#show-re').on("change", function () {
|
||||
$("#entrylist > ul").remove();
|
||||
filterEntryList(entries, $("#filter-entries").val().toLowerCase(), $(this).prop('checked'));
|
||||
scrollView();
|
||||
});
|
||||
});
|
1
static/htmx.min.js
vendored
Normal file
1
static/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
14
static/infinite-scroll.pkgd.min.js
vendored
14
static/infinite-scroll.pkgd.min.js
vendored
File diff suppressed because one or more lines are too long
2
static/jquery-3.7.1.min.js
vendored
2
static/jquery-3.7.1.min.js
vendored
File diff suppressed because one or more lines are too long
4
static/pico.classless.min.css
vendored
Normal file
4
static/pico.classless.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -3,6 +3,24 @@ module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod';
|
|||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
declare namespace abv = "http://ossetic-studies.org/ns/abaevdict";
|
||||
|
||||
(: HELPER FUNCTIONS :)
|
||||
|
||||
declare function abv-m:lookups($include-subentries as xs:boolean := false()) {
|
||||
if (not($include-subentries)) then
|
||||
doc('abaevdict_index/lookup.xml')/tei:table/tei:entry
|
||||
else
|
||||
doc('abaevdict_index/lookup.xml')/tei:table/*
|
||||
};
|
||||
|
||||
declare function abv-m:collection($lang as xs:string,
|
||||
$method as xs:string) {
|
||||
collection(`abaevdict_{$lang}/{$method}`)
|
||||
};
|
||||
|
||||
declare function abv-m:invert-lang($lang as xs:string) as xs:string {
|
||||
if($lang = 'en') then 'ru' else 'en'
|
||||
};
|
||||
|
||||
declare function abv-m:normalize-str($str as xs:string?)
|
||||
as xs:string? {
|
||||
$str => translate('
','') => normalize-space()
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
module namespace dict = 'http://ossetic-studies.org/ns/abaevdict-site/dictionary';
|
||||
|
||||
import module namespace shared = 'http://ossetic-studies.org/ns/abaevdict-site/shared' at '../shared/shared.xq';
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
(: Sidebar with entry list. Requires abaev.js to be loaded. :)
|
||||
|
||||
declare function dict:sidebar($lang as xs:string) {
|
||||
(
|
||||
<button id="open-leftbar">
|
||||
{html:doc('../../../static/chevron-right.svg')}
|
||||
</button>,
|
||||
<button id="close-leftbar">
|
||||
{html:doc('../../../static/chevron-left.svg')}
|
||||
</button>,
|
||||
<aside id="leftbar">
|
||||
<fieldset>
|
||||
<input type="text" id="filter-entries"
|
||||
placeholder="{if ($lang = 'ru') then 'Быстрый фильтр' else 'Quick filter…'}"/>
|
||||
<label>
|
||||
<input id="show-re" type="checkbox" role="switch"></input>
|
||||
{if ($lang = 'ru') then 'Производные' else 'Show subentries'}
|
||||
</label>
|
||||
</fieldset>
|
||||
<nav id="entrylist"></nav> <!-- To be filled dynamically in js -->
|
||||
</aside>
|
||||
)
|
||||
};
|
||||
|
||||
declare function dict:main-view($lang as xs:string,
|
||||
$p as xs:integer,
|
||||
$xpath as xs:string,
|
||||
$entry as xs:string) {
|
||||
(dict:sidebar($lang),
|
||||
<main>
|
||||
{for $doc at $i in $shared:sorted
|
||||
where $i > ($p - 1) * $shared:items-per-page and $i <= $p * $shared:items-per-page
|
||||
let $html := if ($xpath != '' and $entry = $doc/@xml:id)
|
||||
then abv-m:mark-element(
|
||||
doc(`abaevdict_{$lang}/xml/{$doc/@xml:id}.xml`),
|
||||
$xpath) => abv-m:make-html($lang)
|
||||
else
|
||||
let $sd := session:get('searchData')
|
||||
let $sn := session:get('searchN')
|
||||
return if (exists($sd) and $sd($sn)('entry') = $doc/@xml:id)
|
||||
then abv-m:make-html($sd($sn)('tei'), $lang)
|
||||
else doc(`abaevdict_{$lang}/html/{$doc/@xml:id}.html`)
|
||||
return <div class="abv-lex">{(
|
||||
(: Block with icons to the left of entry (floating) :)
|
||||
<div class="icons">
|
||||
{
|
||||
<a href="dict/{$doc/@xml:id}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-link-45deg" viewBox="0 0 16 16">
|
||||
<path d="M4.715 6.542 3.343 7.914a3 3 0 1 0 4.243 4.243l1.828-1.829A3 3 0 0 0 8.586 5.5L8 6.086a1 1 0 0 0-.154.199 2 2 0 0 1 .861 3.337L6.88 11.45a2 2 0 1 1-2.83-2.83l.793-.792a4 4 0 0 1-.128-1.287z"/>
|
||||
<path d="M6.586 4.672A3 3 0 0 0 7.414 9.5l.775-.776a2 2 0 0 1-.896-3.346L9.12 3.55a2 2 0 1 1 2.83 2.83l-.793.792c.112.42.155.855.128 1.287l1.372-1.372a3 3 0 1 0-4.243-4.243z"/>
|
||||
</svg>
|
||||
</a>,
|
||||
if (doc('abaevdict_index/mentioned_en.xml')/lang-index
|
||||
/lang[@id != 'os' and
|
||||
not(starts-with(@id,'os-'))]/word/entry[@id=string($doc/@xml:id)])
|
||||
then <a class="abv-map" data-abv-entry="{$doc/@xml:id}">
|
||||
<img src="/static/map.png"></img>
|
||||
</a>}</div>,
|
||||
$html)}</div>
|
||||
}
|
||||
</main>,
|
||||
<footer>
|
||||
<nav class="pagination">
|
||||
<ul>
|
||||
<li>
|
||||
{if ($p > 1) then <a href="?page={$p - 1}">Previous page</a> else ()}
|
||||
</li>
|
||||
</ul>
|
||||
<!-- <ul><li>Page {$p} of {$page:total}</li></ul> -->
|
||||
<ul class="abv-nextpage">
|
||||
<li>
|
||||
{if ($p < $shared:total) then
|
||||
<a class="pagination__next" href="?page={$p + 1}">Next page</a> else ()}
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</footer>,
|
||||
<dialog id="modal_map">
|
||||
<article>
|
||||
<header>
|
||||
<button class="abv-close-map" aria-label="Close" rel="prev">
|
||||
</button>
|
||||
<p>{if ($lang = 'ru') then 'Карта' else 'Map'}</p>
|
||||
</header>
|
||||
<p>
|
||||
<div id="map_display" style="width:100%;height:80%;"></div>
|
||||
</p>
|
||||
<footer><button class="abv-close-map">{if ($lang = 'ru') then 'Закрыть' else 'Close'}</button></footer>
|
||||
</article>
|
||||
</dialog>)
|
||||
};
|
184
xq/site/components/entries.xqm
Normal file
184
xq/site/components/entries.xqm
Normal file
|
@ -0,0 +1,184 @@
|
|||
module namespace entries = 'http://ossetic-studies.org/ns/abaevdict-site/entries';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
(: MAIN DICTIONARY VIEW COMPONENT :)
|
||||
|
||||
(: GET INDIVIDUAL ENTRY :)
|
||||
|
||||
declare %rest:path("{$lang}/entries/{$id}")
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entry($lang as xs:string, $id as xs:string, $query as xs:string?) {
|
||||
if (count($query) = 0) then
|
||||
doc(`abaevdict_{$lang}/html/{$id}.html`)
|
||||
(: abv-m:make-html(doc(`abaevdict_{$lang}/xml/{$id}.xml`),$lang) :)
|
||||
else
|
||||
let $xml := doc(`abaevdict_{$lang}/xml/{$id}.xml`)/tei:entry[1]
|
||||
let $marked := ft:mark(
|
||||
xquery:eval(
|
||||
`declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
declare namespace abv = "http://ossetic-studies.org/ns/abaevdict";
|
||||
.[{$query}]`, {'': $xml})
|
||||
)
|
||||
return abv-m:make-html($marked,$lang)
|
||||
};
|
||||
|
||||
declare %rest:path("/entries/{$id}")
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entry($id as xs:string, $query as xs:string?) {
|
||||
entries:entry('en', $id, $query)
|
||||
};
|
||||
|
||||
(: GET ENTRIES CATALOG :)
|
||||
|
||||
declare %rest:path("{$lang}/entries")
|
||||
%rest:query-param("per-chunk", "{$per-chunk}", 5)
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entries($lang as xs:string,
|
||||
$per-chunk as xs:integer,
|
||||
$query as xs:string?) {
|
||||
let $docs := abv-m:collection($lang,'xml')/tei:entry
|
||||
let $chosen := if($query) then
|
||||
xquery:eval(`declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
declare namespace abv = "http://ossetic-studies.org/ns/abaevdict";
|
||||
.[{$query}]`,
|
||||
{'': $docs})/string(@xml:id)
|
||||
for tumbling window $w in abv-m:lookups()
|
||||
start at $s when true()
|
||||
only end at $e when $e - $s eq ($per-chunk - 1)
|
||||
return
|
||||
<div class="abv-chunk">
|
||||
{
|
||||
for $entry in $w
|
||||
let $id := $entry/@xml:id
|
||||
return
|
||||
<div class="abv-lex"
|
||||
hx-get="/{$lang}/entries/{$id}"
|
||||
hx-target="find article"
|
||||
hx-trigger="intersect once from:closest .abv-chunk"
|
||||
hx-swap="outerHTML focus-scroll:true">
|
||||
{if ($id = $chosen) then
|
||||
attribute hx-vals {
|
||||
`{'{'}"query": "{$query}"{'}'}`
|
||||
}}
|
||||
{
|
||||
<article id="{$id}"
|
||||
class="abv-entry">
|
||||
</article>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
};
|
||||
|
||||
declare %rest:path("/entries")
|
||||
%rest:query-param("per-chunk", "{$per-chunk}", 5)
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entries($per-chunk as xs:integer,
|
||||
$query as xs:string?) {
|
||||
entries:entries('en', $per-chunk, $query)
|
||||
};
|
||||
|
||||
(: LIST OF ENTRIES FOR MENU :)
|
||||
|
||||
declare %rest:path("{$lang}/entrylist")
|
||||
%rest:query-param("filter", "{$filter}")
|
||||
%rest:query-param("subentries", "{$subentries}", false())
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entrylist($lang as xs:string,
|
||||
$filter as xs:string?,
|
||||
$subentries as xs:boolean?) {
|
||||
<nav id="entrylist">
|
||||
<ul>
|
||||
{for $e in abv-m:lookups($subentries)
|
||||
let $txt := $e/text()
|
||||
let $id := if (name($e) = 'entry') then $e/@xml:id else $e/@corresp
|
||||
return
|
||||
if ($txt contains text {`{$filter}.*`} using wildcards at start) then
|
||||
<li>
|
||||
{
|
||||
if (name($e) = 're') then
|
||||
attribute class {'abv-menu-re'} else (),
|
||||
<a href="#{$id}">
|
||||
{$e/text()}
|
||||
</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
};
|
||||
|
||||
declare %rest:path("entrylist")
|
||||
%rest:query-param("filter", "{$filter}")
|
||||
%rest:query-param("subentries", "{$subentries}", false())
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entries:entrylist($filter as xs:string?,
|
||||
$subentries as xs:boolean?) {
|
||||
entries:entrylist('en',$filter,$subentries)
|
||||
};
|
||||
|
||||
(: QUICK ENTRY LIST SIDEBAR :)
|
||||
|
||||
declare function entries:sidebar($lang as xs:string) {
|
||||
(<button id="close-leftbar"
|
||||
hx-on-click="document.getElementById('leftbar').style.display = 'none';
|
||||
document.getElementById('open-leftbar').style.display = 'block';
|
||||
document.getElementById('close-leftbar').style.display = 'none'">
|
||||
{html:doc('../../../static/chevron-left.svg')}
|
||||
</button>,
|
||||
<button id="open-leftbar"
|
||||
hx-on-click="document.getElementById('leftbar').style.display = 'block';
|
||||
document.getElementById('open-leftbar').style.display = 'none';
|
||||
document.getElementById('close-leftbar').style.display = 'block'">
|
||||
{html:doc('../../../static/chevron-right.svg')}
|
||||
</button>,
|
||||
<aside id="leftbar">
|
||||
<fieldset>
|
||||
<input type="text" id="filter-entries" name="filter"
|
||||
hx-get="./entrylist" hx-target="#entrylist"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="input changed"
|
||||
placeholder="{if ($lang = 'ru') then 'Быстрый фильтр' else 'Quick filter…'}"/>
|
||||
<label>
|
||||
<input id="show-re"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
name="subentries" value="1"
|
||||
hx-get="./entrylist" hx-target="#entrylist"
|
||||
hx-trigger="change"
|
||||
hx-swap="outerHTML"/>
|
||||
{if ($lang = 'ru') then 'Производные' else 'Show subentries'}
|
||||
</label>
|
||||
</fieldset>
|
||||
<nav id="entrylist"
|
||||
hx-get="./entrylist"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="load">
|
||||
</nav>
|
||||
</aside>)
|
||||
};
|
|
@ -1,49 +0,0 @@
|
|||
module namespace index = 'http://ossetic-studies.org/ns/abaevdict-site/index';
|
||||
|
||||
import module namespace shared = 'http://ossetic-studies.org/ns/abaevdict-site/shared' at '../shared/shared.xq';
|
||||
|
||||
declare function index:content($lang as xs:string) {
|
||||
let $mlangs := doc(`abaevdict_index/langnames.xml`)/csv[1]/record
|
||||
return
|
||||
<html>
|
||||
{shared:head('HEDO – Index', <script src="/static/abaev-index.js"></script>)}
|
||||
<body>
|
||||
{shared:header($lang, `../{shared:invert-lang($lang)}/index`)}
|
||||
<main>
|
||||
<div class="index">
|
||||
<div class="langs">
|
||||
<label>{if ($lang = 'ru') then 'Языки' else 'Languages'}</label>
|
||||
<select id="abv-select-lang" size="99999">
|
||||
{
|
||||
for $mlang in $mlangs
|
||||
let $mlang-id := $mlang/code/text()
|
||||
let $mlang-name := if ($lang = 'ru') then $mlang/ru/text() else $mlang/en/text()
|
||||
where $mlang-name != ''
|
||||
order by $mlang-name
|
||||
return
|
||||
<option value="{$mlang-id}">
|
||||
{
|
||||
$mlang-name
|
||||
}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="forms">
|
||||
<Label>{if ($lang = 'ru') then 'Формы' else 'Forms'}</Label>
|
||||
<select id="abv-select-word" size="99999">
|
||||
</select>
|
||||
</div>
|
||||
<div class="entries">
|
||||
<Label>{if ($lang = 'ru') then 'Лексемы' else 'Lexemes'}</Label>
|
||||
<select id="abv-select-entry" size="99999">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="index-submit">
|
||||
<button id="btn-index-submit" hidden="1">{if ($lang = 'ru') then 'К форме' else 'Go to form'}</button>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
};
|
149
xq/site/components/index.xqm
Normal file
149
xq/site/components/index.xqm
Normal file
|
@ -0,0 +1,149 @@
|
|||
module namespace index = 'http://ossetic-studies.org/ns/abaevdict-site/index';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
(: GET LANGUAGE LIST :)
|
||||
|
||||
declare %rest:path("{$lang}/index/languages")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:languages($lang as xs:string) {
|
||||
let $mlangs := doc(`abaevdict_index/langnames.xml`)/csv[1]/record
|
||||
for $mlang in $mlangs
|
||||
let $mlang-id := $mlang/code/text()
|
||||
let $mlang-name := if ($lang = 'ru') then $mlang/ru/text() else $mlang/en/text()
|
||||
where $mlang-name != ''
|
||||
order by $mlang-name
|
||||
return
|
||||
<option value="{$mlang-id}">
|
||||
{
|
||||
$mlang-name
|
||||
}
|
||||
</option>
|
||||
};
|
||||
|
||||
declare %rest:path("/index/languages")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:languages() {
|
||||
index:languages('en')
|
||||
};
|
||||
|
||||
(: FORM LIST :)
|
||||
|
||||
declare %rest:path("{$lang}/index/forms")
|
||||
%rest:query-param("flang", "{$flang}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:forms($lang as xs:string, $flang as xs:string) {
|
||||
let $ments := doc(`abaevdict_index/mentioned_{$lang}.xml`)
|
||||
/lang-index[1]/lang[@id=$flang]/word
|
||||
for $w in $ments
|
||||
let $txt := string($w/@text)
|
||||
return <option value="{$txt}">{$txt}</option>
|
||||
};
|
||||
|
||||
declare %rest:path("index/forms")
|
||||
%rest:query-param("flang", "{$flang}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:forms($flang as xs:string) {
|
||||
index:forms('en',$flang)
|
||||
};
|
||||
(: RELATED ENTRY LIST :)
|
||||
|
||||
declare %rest:path("{$lang}/index/entries")
|
||||
%rest:query-param("flang", "{$flang}")
|
||||
%rest:query-param("word", "{$word}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:entries($lang as xs:string,
|
||||
$flang as xs:string,
|
||||
$word as xs:string) {
|
||||
let $entries := doc(`abaevdict_index/mentioned_{$lang}.xml`)
|
||||
/lang-index[1]/lang[@id=$flang]/word[@text=$word]/entry
|
||||
for $e in $entries
|
||||
let $id := $e/@id
|
||||
let $txt := abv-m:entry-form-by-id($id)
|
||||
let $query := `./@xml:id = '{$id}' and .//text() contains
|
||||
text '{$word}' using diacritics sensitive`
|
||||
return <tr>
|
||||
<td>{string-join(
|
||||
distinct-values($e/ref/gloss/string(@text)),', ')}</td>
|
||||
<td>
|
||||
<a href="./dictionary?query={$query}#{$id}">{$txt}</a>
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
string-join(data(doc(`abaevdict_{$lang}/xml/{$id}.xml`)
|
||||
/tei:entry[1]/tei:sense/(tei:sense|tei:sense/tei:sense|.)
|
||||
/tei:cit/tei:quote),', ')
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
};
|
||||
|
||||
declare %rest:path("index/entries")
|
||||
%rest:query-param("flang", "{$flang}")
|
||||
%rest:query-param("word", "{$word}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function index:entries($flang as xs:string,
|
||||
$word as xs:string) {
|
||||
index:entries('en',$flang,$word)
|
||||
};
|
||||
|
||||
(: THE MAIN INDEX VIEW :)
|
||||
|
||||
declare function index:content($lang as xs:string) {
|
||||
<html>
|
||||
<body>
|
||||
<main>
|
||||
<div class="index">
|
||||
<div class="langs">
|
||||
<label>{if ($lang = 'ru') then 'Языки' else 'Languages'}</label>
|
||||
<select id="abv-select-lang" size="99999" name="flang"
|
||||
hx-get='./index/forms' hx-target="#abv-select-word" hx-trigger="input">
|
||||
{index:languages($lang)}
|
||||
</select>
|
||||
</div>
|
||||
<div class="forms">
|
||||
<label>{if ($lang = 'ru') then 'Формы' else 'Forms'}</label>
|
||||
<select id="abv-select-word" size="99999" name="word"
|
||||
hx-get="./index/entries" hx-target="#abv-select-entry" hx-trigger="input"
|
||||
hx-include="#abv-select-lang">
|
||||
</select>
|
||||
</div>
|
||||
<div class="entries">
|
||||
<label>{if ($lang = 'ru') then 'Лексемы' else 'Lexemes'}</label>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Глоссы' else 'Gloss'}</th>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Лексема' else 'Lemma'}</th>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Перевод' else 'Translation'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="abv-select-entry">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
};
|
71
xq/site/components/map.xqm
Normal file
71
xq/site/components/map.xqm
Normal file
|
@ -0,0 +1,71 @@
|
|||
module namespace map = 'http://ossetic-studies.org/ns/abaevdict-site/map';
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
declare %rest:path("{$lang}/map")
|
||||
%rest:query-param("entry", "{$entry}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function map:map($lang as xs:string, $entry as xs:string) {
|
||||
let $mapdata := abv-m:make-geomap(
|
||||
doc(`abaevdict_{$lang}/xml/{$entry}.xml`),
|
||||
$lang)
|
||||
return
|
||||
(<article>
|
||||
<header>
|
||||
<button class="abv-close-map" aria-label="Close" rel="prev">
|
||||
</button>
|
||||
<p>{if ($lang = 'ru') then 'Карта' else 'Map'}</p>
|
||||
</header>
|
||||
<p>
|
||||
<div id="map-display" style="width:100%;height:80%;"></div>
|
||||
</p>
|
||||
<footer>
|
||||
<button class="abv-close-map"
|
||||
hx-on-click="document.getElementById('modal_map').close()">
|
||||
{if ($lang = 'ru') then 'Закрыть' else 'Close'}
|
||||
</button>
|
||||
</footer>
|
||||
</article>,
|
||||
<script defer="1">
|
||||
{concat(`var mapdata = {json:serialize($mapdata)}`,
|
||||
"
|
||||
map = document.getElementById('map-display');
|
||||
var layout = {
|
||||
// title: {
|
||||
// text: 'Canadian cities',
|
||||
// font: {
|
||||
// family: 'Droid Serif, serif',
|
||||
// size: 16
|
||||
// }
|
||||
// },
|
||||
geo: {
|
||||
scope: 'world',
|
||||
resolution: 50,
|
||||
fitbounds: 'locations',
|
||||
showrivers: true,
|
||||
rivercolor: '#fff',
|
||||
showlakes: true,
|
||||
lakecolor: '#fff',
|
||||
showland: true,
|
||||
landcolor: '#EAEAAE',
|
||||
showcountries: false,
|
||||
subunitcolor: '#d3d3d3'
|
||||
}
|
||||
};
|
||||
Plotly.newPlot(map, [mapdata], layout);
|
||||
")}
|
||||
</script>)
|
||||
};
|
||||
|
||||
declare %rest:path("map")
|
||||
%rest:query-param("entry", "{$entry}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function map:map($entry as xs:string) {
|
||||
map:map('en', $entry)
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
module namespace search = 'http://ossetic-studies.org/ns/abaevdict-site/search-modal';
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
(: Modal that appears on all pages, with search results :)
|
||||
declare function search:content($lang as xs:string) {
|
||||
if (exists(session:get('searchData'))) then
|
||||
<dialog id="modal_searchResults">
|
||||
<article>
|
||||
<header>
|
||||
<button class="abv-close-search" aria-label="Close" rel="prev"/>
|
||||
<p>{if ($lang = 'ru') then 'Результаты поиска'
|
||||
else 'Search results'}</p>
|
||||
</header>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{if ($lang = 'ru') then '№' else 'No.'}</td>
|
||||
<td>{if ($lang = 'ru') then 'Лемма' else 'Lemma'}</td>
|
||||
<td>{if ($lang = 'ru') then 'Фрагмент' else 'Fragment'}</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<footer>
|
||||
<button class="abv-close-search">{if ($lang = 'ru') then 'Закрыть' else 'Close'}</button>
|
||||
</footer>
|
||||
</article>
|
||||
</dialog>
|
||||
};
|
134
xq/site/components/search.xqm
Normal file
134
xq/site/components/search.xqm
Normal file
|
@ -0,0 +1,134 @@
|
|||
module namespace search = 'http://ossetic-studies.org/ns/abaevdict-site/search';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../../abv-mod.xqm';
|
||||
|
||||
declare variable $search:types :=
|
||||
(
|
||||
{
|
||||
'query': ".//text() contains text {'$'}",
|
||||
'ru': 'Везде',
|
||||
'en': 'All'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:form/tei:orth//text() contains text {'$'}",
|
||||
'ru': 'Формы',
|
||||
'en': 'Forms'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:cit[@type='translationEquivalent']//text() contains text {'$'}",
|
||||
'ru': 'Значения',
|
||||
'en': 'Meanings'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:cit[@type='example']/tei:quote//text() contains text {'$'}",
|
||||
'ru': 'Примеры',
|
||||
'en': 'Examples'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:cit[@type='translation']//text() contains text {'$'}",
|
||||
'ru': 'Переводы',
|
||||
'en': 'Translations'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:mentioned/(tei:m|tei:w|tei:phr|tei:s)/text() contains text {'$'}",
|
||||
'ru': 'Цит. формы',
|
||||
'en': 'Mentioned'
|
||||
},
|
||||
{
|
||||
'query': ".//tei:gloss//text() contains text {'$'}",
|
||||
'ru': 'Глоссы',
|
||||
'en': 'Glosses'
|
||||
},
|
||||
{
|
||||
'query': "./tei:etym[1]//text() contains text {'$'}",
|
||||
'ru': 'Этимологии',
|
||||
'en': 'Etymology'
|
||||
},
|
||||
{
|
||||
'query': "$",
|
||||
'ru': 'XQuery',
|
||||
'en': 'XQuery'
|
||||
}
|
||||
);
|
||||
|
||||
(: GET SEARCH RESULTS :)
|
||||
|
||||
declare function search:content($lang as xs:string,
|
||||
$pattern as xs:string? := (),
|
||||
$text as xs:string? := ()) {
|
||||
<form role="search" id="abv-search"
|
||||
hx-post=""
|
||||
hx-select="main"
|
||||
hx-target="main"
|
||||
hx-swap="outerHTML"
|
||||
hx-push-url="true">
|
||||
<select name="pattern" required="1">
|
||||
{
|
||||
for $opt in $search:types
|
||||
return
|
||||
<option value="{$opt('query')}">
|
||||
{
|
||||
if ($pattern = $opt('query')) then
|
||||
attribute selected {'1'},
|
||||
if ($lang = 'ru') then $opt('ru')
|
||||
else $opt('en')
|
||||
}
|
||||
</option>
|
||||
}
|
||||
</select>
|
||||
<input name="text" required="1"
|
||||
placeholder="{if ($lang = 'ru') then 'Искать' else 'Search'}"
|
||||
value="{$text}"
|
||||
aria-label="Search">
|
||||
{if (exists(session:get('searchQuery'))) then
|
||||
attribute disabled {"1"} }
|
||||
</input>
|
||||
<input type="submit" value="Submit"/>
|
||||
</form>,
|
||||
<table hx-boost="true" id="abv-search-results">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Лексема' else 'Lemma'}</th>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Перевод' else 'Translation'}</th>
|
||||
<th scope="col">{if ($lang = 'ru') then 'Контекст' else 'Context'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{if (exists($pattern) and exists($text)) then
|
||||
let $entries := abv-m:collection($lang,'xml')/tei:entry
|
||||
let $query := replace($pattern, "\$", $text)
|
||||
let $hits :=
|
||||
ft:mark(xquery:eval(`declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
declare namespace abv = "http://ossetic-studies.org/ns/abaevdict";
|
||||
.[{$query}]`, {'': $entries}))
|
||||
for $hit in $hits
|
||||
order by abv-m:sortKey($hit/tei:form[1]/tei:orth[1]/text())
|
||||
return
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/{$lang}/dictionary?query={$query}#{$hit/@xml:id}">
|
||||
{$hit/tei:form[1]/tei:orth[1]}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
string-join(
|
||||
data($hit/tei:sense
|
||||
/(tei:sense|tei:sense/tei:sense|.)/tei:cit/tei:quote),
|
||||
', '
|
||||
)
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
{
|
||||
if (count($hit//*[tei:mark]) > 0) then
|
||||
util:strip-namespaces(($hit//*[tei:mark])[1])/child::node()
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
};
|
|
@ -1,210 +0,0 @@
|
|||
declare namespace api = 'http://ossetic-studies.org/ns/abaevdict-api';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../abv-mod.xqm';
|
||||
|
||||
(: ======================================================= :)
|
||||
(: ==================== API STUFF ======================== :)
|
||||
(: ======================================================= :)
|
||||
|
||||
declare option output:method 'json';
|
||||
declare option output:json "format=xquery";
|
||||
|
||||
declare variable $langs-index := doc('abaevdict_index/langnames.xml');
|
||||
declare variable $lookup := doc('abaevdict_index/lookup.xml');
|
||||
|
||||
(: Get the mentioned index for the right metalanguage :)
|
||||
declare function api:ment-index($db-lang as xs:string) {
|
||||
doc(`abaevdict_index/mentioned_{$db-lang}.xml`)
|
||||
};
|
||||
|
||||
(: Short entry info (only forms for now). Hash is used because URLs don't handle
|
||||
some Unicode characters very well in this system :)
|
||||
declare function api:entry-info-short($n as node())
|
||||
as map(xs:string, item()) {
|
||||
let $id := if (name($n) = 're') then string($n/@corresp) else string($n/@xml:id)
|
||||
return map{'id': hash($id),
|
||||
'xmlid': $id,
|
||||
'form': $n/text(),
|
||||
'subentry': if (name($n) = 're') then true() else false()
|
||||
}
|
||||
};
|
||||
|
||||
declare function api:entry-info-long($n as node(), $db-lang as xs:string)
|
||||
as map(xs:string, item()) {
|
||||
map:merge(
|
||||
(
|
||||
api:entry-info-short($n),
|
||||
map{'glosses': array:build(data(doc(`abaevdict_{$db-lang}/xml/{$n/@xml:id}.xml`)
|
||||
/tei:entry[1]/tei:sense/(tei:sense|tei:sense/tei:sense|.)/tei:cit/tei:quote))}
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
(: Short language info -- for the language list :)
|
||||
declare function api:lang-info-short($n as node(), $db-lang as xs:string) {
|
||||
map{
|
||||
'id': $n/code/text(),
|
||||
'glottolog': $n/glottolog/text(),
|
||||
'name': map{'full': if ($db-lang = 'ru') then $n/ru/text() else $n/en/text(),
|
||||
'abbr': if ($db-lang = 'ru') then $n/ru/text() else $n/en_abbr/text()}
|
||||
}
|
||||
};
|
||||
|
||||
(: Longer info -- for individual languages :)
|
||||
declare function api:lang-info-full($n as node(), $db-lang as xs:string) {
|
||||
map:merge(
|
||||
(api:lang-info-short($n, $db-lang),
|
||||
map{'words':
|
||||
array:build(api:ment-index($db-lang)/lang-index[1]/lang[@id=$n/code]/word,
|
||||
api:word-info-short#1)
|
||||
}
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
declare function api:word-info-short($n as node()) {
|
||||
let $text := xs:string($n/@text)
|
||||
return map{
|
||||
(: I am using a hash function as ID, because the words are uniquely
|
||||
identified within a language by their text. And the mentioned index is not
|
||||
persistent enough (at least yet) to use persistent ids :)
|
||||
'id': string(hash($text)),
|
||||
'text': $text
|
||||
}
|
||||
};
|
||||
|
||||
declare function api:word-info-long($n as node()) {
|
||||
map:merge(
|
||||
(
|
||||
api:word-info-short($n),
|
||||
map{
|
||||
'entries': array:build($n/entry,
|
||||
fn {
|
||||
map{'id': string(hash(./@id)),
|
||||
'xmlid': string(./@id),
|
||||
'refs': array:build(./ref,
|
||||
fn {
|
||||
map:merge((
|
||||
map:entry('path', string(./@path)),
|
||||
if(./gloss) then
|
||||
{'glosses': array:build(distinct-values(./gloss/@text), string#1)}
|
||||
else ()
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
(: 'nodes': array:build(string($n/ref/@node-id)),
|
||||
'glosses': array:build(string($n/gloss/@text)) :)
|
||||
}
|
||||
)
|
||||
)
|
||||
};
|
||||
|
||||
(: RETURN JSON FOR PLOTLY MAP :)
|
||||
(: English as default language :)
|
||||
declare %rest:path("dict/map-info/{$entry}")
|
||||
%rest:GET
|
||||
function api:map-info($entry) {
|
||||
(: In this case we do not redirect, but simply give the JSON via
|
||||
another function :)
|
||||
api:map-info('en', $entry)
|
||||
};
|
||||
|
||||
(: This has to be remade to use the /api path. But keep it like this for now :)
|
||||
declare %rest:path("{$lang}/dict/map-info/{$entry}")
|
||||
%rest:GET
|
||||
function api:map-info($lang, $entry) {
|
||||
let $ments := abv-m:make-geomap(doc(`abaevdict_{$lang}/xml/{$entry}.xml`),
|
||||
$lang)
|
||||
return $ments
|
||||
};
|
||||
|
||||
(: API FOR THE MENTIONED INDEX :)
|
||||
(: Default to English :)
|
||||
declare %rest:path("/api/{$path=.+}")
|
||||
%rest:GET
|
||||
function api:default($path as xs:string) {
|
||||
web:forward(`/en/api/{$path}`)
|
||||
};
|
||||
|
||||
(: Get languages :)
|
||||
declare %rest:path("{$db-lang}/api/languages")
|
||||
%rest:GET
|
||||
function api:langs($db-lang as xs:string := 'en') {
|
||||
array:build(
|
||||
$langs-index/csv[1]/record,
|
||||
api:lang-info-short(?,$db-lang)
|
||||
)
|
||||
};
|
||||
|
||||
(: Get one language :)
|
||||
declare %rest:path("{$db-lang}/api/languages/{$lang}")
|
||||
%rest:GET
|
||||
function api:langs-lang($db-lang as xs:string, $lang as xs:string) {
|
||||
api:lang-info-full(
|
||||
$langs-index/csv[1]/record[code=$lang], $db-lang
|
||||
)
|
||||
};
|
||||
|
||||
(: Get words for a language :)
|
||||
declare %rest:path("{$db-lang}/api/languages/{$lang}/words")
|
||||
%rest:GET
|
||||
function api:langs-lang-words($db-lang as xs:string, $lang as xs:string) {
|
||||
array:build(api:ment-index($db-lang)/lang-index[1]/lang[@id=$lang]/word,
|
||||
api:word-info-short#1)
|
||||
};
|
||||
|
||||
(: Get info on a particular word.
|
||||
Db-lang is a placeholder, it does nothing here, used only for consistency :)
|
||||
declare %rest:path("{$db-lang}/api/languages/{$lang}/words/{$word-id}")
|
||||
%rest:GET
|
||||
function api:langs-lang-words-word($db-lang as xs:string,
|
||||
$lang as xs:string,
|
||||
$word-id as xs:string) {
|
||||
api:word-info-long(
|
||||
api:ment-index($db-lang)/lang-index[1]/lang[@id=$lang]/word[string(hash(@text))=$word-id]
|
||||
)
|
||||
};
|
||||
|
||||
(: Entry info :)
|
||||
declare %rest:path("{$db-lang}/api/entries")
|
||||
%rest:GET
|
||||
function api:entries($db-lang as xs:string) {
|
||||
array:build(
|
||||
$lookup/tei:table[1]/*,
|
||||
api:entry-info-short#1
|
||||
)
|
||||
};
|
||||
|
||||
declare %rest:path("{$db-lang}/api/entries/{$entry-id}")
|
||||
%rest:GET
|
||||
function api:entries($db-lang as xs:string,
|
||||
$entry-id as xs:string) {
|
||||
api:entry-info-long(
|
||||
$lookup/tei:table[1]/*[string(hash(@xml:id))=$entry-id],
|
||||
$db-lang)
|
||||
};
|
||||
|
||||
(: Search API :)
|
||||
declare %rest:path("{$db-lang}/api/search")
|
||||
%rest:query-param("type","{$type}",'full')
|
||||
%rest:query-param("query","{$query}")
|
||||
%rest:GET
|
||||
function api:search($db-lang as xs:string,
|
||||
$type as xs:string,
|
||||
$query as xs:string) {
|
||||
abv-m:search($db-lang,$type,$query)
|
||||
};
|
||||
|
||||
(: Get current search results :)
|
||||
declare %rest:path("{$db-lang}/api/search/current")
|
||||
%rest:GET
|
||||
function api:search-current($db-lang as xs:string) {
|
||||
session:get('searchData')
|
||||
};
|
||||
|
||||
0
|
|
@ -1,123 +0,0 @@
|
|||
module namespace page = 'http://ossetic-studies.org/ns/abaevdict-site';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../abv-mod.xqm';
|
||||
import module namespace shared = 'http://ossetic-studies.org/ns/abaevdict-site/shared' at './shared/shared.xq';
|
||||
|
||||
(: COMPONENTS :)
|
||||
(: To make the codebase more manageable, components are split into separate
|
||||
files included as modules. :)
|
||||
import module namespace home = 'http://ossetic-studies.org/ns/abaevdict-site/home' at './components/home.xq';
|
||||
import module namespace dict = 'http://ossetic-studies.org/ns/abaevdict-site/dictionary' at './components/dictionary.xq';
|
||||
import module namespace index = 'http://ossetic-studies.org/ns/abaevdict-site/index' at './components/index.xq';
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ============================ FAVICON ========================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare %rest:path("/favicon.ico")
|
||||
%rest:GET
|
||||
function page:favicon() {
|
||||
fetch:binary('../../static/favicon.ico')
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= HOMEPAGE ============================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare %rest:path("{$lang}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:zero($lang) {
|
||||
page:home($lang)
|
||||
};
|
||||
|
||||
declare %rest:path("/")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:zero() {
|
||||
page:zero('en')
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/home")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:home($lang) {
|
||||
<html>
|
||||
{shared:head(if ($lang='ru') then 'ИЭСОЯ — Главная' else 'Abaevdict — Home',())}
|
||||
|
||||
<body>
|
||||
{shared:header($lang,`../{shared:invert-lang($lang)}/home`)}
|
||||
|
||||
<main>
|
||||
{
|
||||
home:content($lang)
|
||||
}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
|
||||
declare %rest:path("home")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:home() {
|
||||
page:home('en')
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= THE DICTIONARY ======================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
(: If no language defined, default to English :)
|
||||
declare %rest:path("dict")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:dict() {
|
||||
web:forward('/en/dict')
|
||||
};
|
||||
|
||||
(: The main dictionary view :)
|
||||
declare %rest:path("{$lang}/dict")
|
||||
%rest:query-param("page","{$p}", 1)
|
||||
%rest:query-param("xpath","{$xpath}", '')
|
||||
%rest:query-param("entry","{$entry}", '')
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:dict($lang, $p, $xpath, $entry) {
|
||||
<html>
|
||||
{shared:head('HEDO – Dictionary',
|
||||
(<script src="/static/infinite-scroll.pkgd.min.js">
|
||||
</script>,
|
||||
<script src="/static/plotly-3.0.1.min.js" charset="utf-8"></script>,
|
||||
<script src="/static/abaev.js"></script>))}
|
||||
|
||||
<body>
|
||||
{(
|
||||
shared:header($lang,`../{shared:invert-lang($lang)}/dict`),
|
||||
dict:main-view($lang, $p, $xpath, $entry)
|
||||
)}
|
||||
</body>
|
||||
</html>
|
||||
};
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= THE INDEX ============================= :)
|
||||
(: =============================================================== :)
|
||||
|
||||
(: Mentioned index :)
|
||||
declare %rest:path("{$lang}/index")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:index($lang) {
|
||||
index:content($lang)
|
||||
};
|
||||
|
||||
(: Default to English :)
|
||||
declare %rest:path("index")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function page:index() {
|
||||
page:index('en')
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
module namespace entry = 'http://ossetic-studies.org/ns/abaevdict-site/entrypoints';
|
||||
|
||||
import module namespace shared = 'http://ossetic-studies.org/ns/abaevdict-site/shared' at './shared/shared.xq';
|
||||
|
||||
(: This module includes various ways of accessing dictionary resources by
|
||||
their identifiers. Right now, this only applies to entries, but may apply to
|
||||
other things as well :)
|
||||
|
||||
(: == ENTRY BY ID == :)
|
||||
(: English as default language :)
|
||||
declare %rest:path("dict/{$id}")
|
||||
%rest:query-param("xpath", "{$xpath}", '')
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entry:by-id($id, $xpath) {
|
||||
entry:by-id('en', $id, $xpath)
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/dict/{$id}")
|
||||
%rest:query-param("xpath", "{$xpath}", '')
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function entry:by-id($lang, $id, $xpath) {
|
||||
let $doc-index := index-where($shared:sorted, fn { ./@xml:id=$id })
|
||||
let $pagenum := ceiling($doc-index div $shared:items-per-page)
|
||||
return web:redirect('../dict',
|
||||
map:merge(({'page': $pagenum,
|
||||
'entry': web:decode-url($id)},
|
||||
if ($xpath != '') then {'xpath': $xpath})),
|
||||
web:decode-url($id))
|
||||
};
|
254
xq/site/restx_main.xq
Normal file
254
xq/site/restx_main.xq
Normal file
|
@ -0,0 +1,254 @@
|
|||
declare namespace site = 'http://ossetic-studies.org/ns/abaevdict-site';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../abv-mod.xqm';
|
||||
import module namespace home = 'http://ossetic-studies.org/ns/abaevdict-site/home' at './components/home.xqm';
|
||||
import module namespace entries = 'http://ossetic-studies.org/ns/abaevdict-site/entries' at './components/entries.xqm';
|
||||
import module namespace index = 'http://ossetic-studies.org/ns/abaevdict-site/index' at './components/index.xqm';
|
||||
import module namespace search = 'http://ossetic-studies.org/ns/abaevdict-site/search' at './components/search.xqm';
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ============================ FAVICON ========================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare %rest:path("/favicon.ico")
|
||||
%rest:GET
|
||||
function site:favicon() {
|
||||
fetch:binary('../../static/favicon.ico')
|
||||
};
|
||||
|
||||
(: HTML HEAD :)
|
||||
|
||||
declare function site:head($title as xs:string) {
|
||||
<head>
|
||||
<title>{$title}</title>
|
||||
<script src="/static/htmx.min.js"></script>
|
||||
<script src="/static/plotly-3.0.1.min.js" charset="utf-8"></script>
|
||||
|
||||
<link rel="stylesheet" href="/static/pico.classless.min.css"/>
|
||||
|
||||
<link rel="stylesheet" href="/static/abaev-html.css"/>
|
||||
</head>
|
||||
};
|
||||
|
||||
(: HEADER :)
|
||||
|
||||
declare function site:header($lang as xs:string, $href-other as xs:string) {
|
||||
<header id="abv-header">
|
||||
<nav>
|
||||
<ul class="abv-brand">
|
||||
<li><img class="abv-logo" src="/static/Abaev_logo.png" alt="Abaevdict logo"></img>
|
||||
<strong>{
|
||||
switch($lang)
|
||||
case 'ru' return 'ИЭСОЯ'
|
||||
default return `Abaevdict`
|
||||
}</strong><i> β</i></li>
|
||||
<li>{
|
||||
(element {if ($lang = 'ru') then 'mark' else 'a'}{
|
||||
attribute hx-on-click {"this.href = this.href + window.location.hash;"},
|
||||
attribute class {'abv-lang-link'},
|
||||
if ($lang != 'ru') then
|
||||
(attribute href {`{$href-other}`}) else (),
|
||||
'ru'
|
||||
}, ' / ',
|
||||
element {if ($lang = 'ru') then 'a' else 'mark'}{
|
||||
attribute hx-on-click {"this.href = this.href + window.location.hash;"},
|
||||
attribute class {'abv-lang-link'},
|
||||
if ($lang = 'ru') then
|
||||
(attribute href {`{$href-other}`}) else (),
|
||||
'en'
|
||||
})
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="./home">{
|
||||
switch($lang)
|
||||
case 'ru' return 'Главная'
|
||||
default return 'Home'
|
||||
}
|
||||
</a></li>
|
||||
<li><a href="./dictionary">{switch($lang)
|
||||
case 'ru' return 'Просмотр'
|
||||
default return 'Browse'
|
||||
}
|
||||
</a></li>
|
||||
<li><a href="./search">{switch($lang)
|
||||
case 'ru' return 'Поиск'
|
||||
default return 'Search'
|
||||
}
|
||||
</a></li>
|
||||
<li><a href="./index">{switch($lang)
|
||||
case 'ru' return 'Указатель'
|
||||
default return 'Index'
|
||||
}</a></li>
|
||||
<li><a href="#">{switch($lang)
|
||||
case 'ru' return 'Литература'
|
||||
default return 'References'
|
||||
}</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
};
|
||||
|
||||
(: PATHS :)
|
||||
|
||||
(: ZERO PATH :)
|
||||
|
||||
declare %rest:path("{$lang}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:null($lang as xs:string) {
|
||||
site:home($lang)
|
||||
};
|
||||
|
||||
declare %rest:path("/")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:null() {
|
||||
site:null('en')
|
||||
};
|
||||
|
||||
(: HOME PAGE :)
|
||||
|
||||
declare %rest:path("{$lang}/home")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:home($lang as xs:string) {
|
||||
<html>
|
||||
{
|
||||
site:head(if ($lang = 'ru') then 'ИЭСОЯ В.И. Абаева — Главная'
|
||||
else 'Abaevdict — Main'),
|
||||
<body>
|
||||
{site:header($lang,`/{abv-m:invert-lang($lang)}/home`)}
|
||||
<main>
|
||||
{home:content($lang)}
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
</html>
|
||||
};
|
||||
|
||||
declare %rest:path("/home")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:home() {
|
||||
site:home('en')
|
||||
};
|
||||
|
||||
(: DICTIONARY INTERFACE :)
|
||||
|
||||
declare %rest:path("{$lang}/dictionary")
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:dictionary($lang as xs:string, $query as xs:string?) {
|
||||
<html>
|
||||
{
|
||||
site:head(if($lang = 'ru') then 'ИЭСОЯ В.И. Абаева — Просмотр'
|
||||
else 'Abaevdict — Browse'),
|
||||
<body>
|
||||
{site:header($lang,`/{abv-m:invert-lang($lang)}/dictionary`)}
|
||||
{entries:sidebar($lang)}
|
||||
<dialog id="modal_map"/>
|
||||
<main id="abv-dict">
|
||||
{entries:entries($lang,20,$query)}
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
</html>
|
||||
};
|
||||
|
||||
declare %rest:path("dictionary")
|
||||
%rest:query-param("query", "{$query}")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:dictionary($query as xs:string?) {
|
||||
site:dictionary('en', $query)
|
||||
};
|
||||
|
||||
(: INDEX INTERFACE :)
|
||||
|
||||
declare %rest:path("{$lang}/index")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:index($lang as xs:string) {
|
||||
<html>
|
||||
{
|
||||
site:head(if ($lang = 'ru') then 'ИЭСОЯ В.И. Абаева — Указатель'
|
||||
else 'Abaevdict — Index'),
|
||||
<body>
|
||||
{site:header($lang,`/{abv-m:invert-lang($lang)}/index`)}
|
||||
<main>
|
||||
{index:content($lang)}
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
</html>
|
||||
};
|
||||
|
||||
declare %rest:path("index")
|
||||
%rest:GET
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:index() {
|
||||
site:index('en')
|
||||
};
|
||||
|
||||
(: SEARCH INTERFACE :)
|
||||
|
||||
declare %rest:path("{$lang}/search")
|
||||
%rest:form-param("pattern", "{$pattern}")
|
||||
%rest:form-param("text", "{$text}")
|
||||
%rest:GET
|
||||
%rest:POST
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:search($lang as xs:string,
|
||||
$pattern as xs:string?,
|
||||
$text as xs:string?) {
|
||||
<html>
|
||||
{
|
||||
site:head(if ($lang = 'ru') then 'ИЭСОЯ В.И. Абаева — Поиск'
|
||||
else 'Abaevdict — Search'),
|
||||
<body>
|
||||
{site:header($lang,`/{abv-m:invert-lang($lang)}/search`)}
|
||||
<main>
|
||||
{search:content($lang,$pattern,$text)}
|
||||
</main>
|
||||
</body>
|
||||
}
|
||||
</html>
|
||||
};
|
||||
|
||||
declare %rest:path("search")
|
||||
%rest:form-param("pattern", "{$pattern}")
|
||||
%rest:form-param("text", "{$text}")
|
||||
%rest:GET
|
||||
%rest:POST
|
||||
%rest:produces("text/html")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function site:search($pattern as xs:string?,
|
||||
$text as xs:string?) {
|
||||
site:search('en',$pattern,$text)
|
||||
};
|
||||
|
||||
0
|
|
@ -1,75 +0,0 @@
|
|||
module namespace search = 'http://ossetic-studies.org/ns/abaevdict-site/search';
|
||||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at '../abv-mod.xqm';
|
||||
import module namespace entry = 'http://ossetic-studies.org/ns/abaevdict-site/entrypoints' at './restx_entrypoints.xq';
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= SEARCH ENTRYPOINT ===================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare %rest:path("/search/{$path=.+}")
|
||||
%rest:GET
|
||||
function search:search-default($path as xs:string) {
|
||||
web:forward(`/en/search/{$path}`)
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/new")
|
||||
%rest:query-param("searchType", "{$searchType}", 'full')
|
||||
%rest:query-param("searchQuery", "{$searchQuery}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function search:search($lang, $searchType, $searchQuery) {
|
||||
let $sd := abv-m:search($lang,$searchType,$searchQuery)
|
||||
let $r1 := session:set('searchType', $searchType)
|
||||
let $r2 := session:set('searchQuery', $searchQuery)
|
||||
let $r3 := session:set('searchN', 1)
|
||||
let $r4 := session:set('searchData', $sd)
|
||||
return
|
||||
if(array:size($sd) > 0) then
|
||||
entry:by-id($lang,
|
||||
$sd(1)('entry'),
|
||||
'')
|
||||
else
|
||||
web:redirect('../search/clear')
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/next")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function search:search-next($lang) {
|
||||
let $n as xs:integer := session:get('searchN')+1
|
||||
let $r1 := session:set('searchN', $n)
|
||||
let $sd := session:get('searchData')
|
||||
return entry:by-id($lang, $sd($n)('entry'), ())
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/prev")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function search:search-prev($lang) {
|
||||
let $n as xs:integer := session:get('searchN')-1
|
||||
let $r1 := session:set('searchN', $n)
|
||||
let $sd := session:get('searchData')
|
||||
return entry:by-id($lang, $sd($n)('entry'), ())
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/position")
|
||||
%rest:query-param("p", "{$p}")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function search:search-position($lang, $p as xs:integer) {
|
||||
let $r1 := session:set('searchN', $p)
|
||||
let $sd := session:get('searchData')
|
||||
return entry:by-id($lang, $sd($p)('entry'), ())
|
||||
};
|
||||
|
||||
declare %rest:path("{$lang}/search/clear")
|
||||
%output:method("html")
|
||||
%output:html-version('5')
|
||||
function search:clearSearch($lang) {
|
||||
let $r1 := session:delete('searchType')
|
||||
let $r2 := session:delete('searchQuery')
|
||||
let $r3 := session:delete('searchN')
|
||||
let $r4 := session:delete('searchData')
|
||||
return web:redirect('../dict')
|
||||
};
|
|
@ -1,165 +0,0 @@
|
|||
module namespace shared = 'http://ossetic-studies.org/ns/abaevdict-site/shared';
|
||||
|
||||
declare namespace tei = "http://www.tei-c.org/ns/1.0";
|
||||
import module namespace search = 'http://ossetic-studies.org/ns/abaevdict-site/search-modal' at '../components/search.xq';
|
||||
|
||||
(: =========================================================== :)
|
||||
(: ================== GLOBAL VARIABLES ======================= :)
|
||||
(: =========================================================== :)
|
||||
|
||||
(: Number of items per page :)
|
||||
declare variable $shared:items-per-page as xs:integer := 20;
|
||||
|
||||
(: These variables are declared in module scope because they are needed in
|
||||
different places, but they are still dynamically evaluated :)
|
||||
declare variable $shared:lookup := doc('abaevdict_index/lookup.xml')/tei:table/tei:entry;
|
||||
declare variable $shared:lookup-all := doc('abaevdict_index/lookup.xml')/tei:table/*;
|
||||
|
||||
declare variable $shared:total := ceiling(count($shared:lookup) div $shared:items-per-page);
|
||||
(: This is still called sorted, because I haven't changed the code.
|
||||
But it's now equal to lookup, because the lookup itself is now sorted. :)
|
||||
declare variable $shared:sorted := $shared:lookup;
|
||||
|
||||
(: =============================================================== :)
|
||||
(: ======================= COMMON STUFF ========================== :)
|
||||
(: =============================================================== :)
|
||||
|
||||
declare function shared:head($title as xs:string, $script as node()* := ()) {
|
||||
<head>
|
||||
<script src="/static/jquery-3.7.1.min.js">
|
||||
</script>
|
||||
<script src="/static/abaev-global.js">
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"/>
|
||||
|
||||
<link rel="stylesheet" href="{web:create-url('/static/abaev-html.css')}"/>
|
||||
|
||||
{$script}
|
||||
|
||||
<title>{$title}</title>
|
||||
</head>
|
||||
};
|
||||
|
||||
(: Navigation header and search dialog :)
|
||||
declare function shared:header($lang as xs:string, $href-other as xs:string) {
|
||||
(
|
||||
<header>
|
||||
<nav>
|
||||
<ul class="abv-brand">
|
||||
<li><img class="abv-logo" src="/static/Abaev_logo.png" alt="Abaevdict logo"></img>
|
||||
<strong>{
|
||||
switch($lang)
|
||||
case 'ru' return 'ИЭСОЯ'
|
||||
default return `Abaevdict`
|
||||
}</strong><i> β</i></li>
|
||||
<li>{
|
||||
(element {if ($lang = 'ru') then 'mark' else 'a'}{
|
||||
if ($lang != 'ru') then
|
||||
(attribute class {'link'},
|
||||
attribute data-href {`{$href-other}`}) else (),
|
||||
'ru'
|
||||
}, ' / ',
|
||||
element {if ($lang = 'ru') then 'a' else 'mark'}{
|
||||
if ($lang = 'ru') then
|
||||
(attribute class {'link'},
|
||||
attribute data-href {`{$href-other}`}) else (),
|
||||
'en'
|
||||
})
|
||||
}
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<form role="search" action="./search/new" id="abv-search">
|
||||
{if (session:get('searchQuery')) then
|
||||
<button class="link" data-href="./search/clear" type="button">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||
</svg>
|
||||
</button>
|
||||
else
|
||||
<select name="searchType" required="1">
|
||||
<option selected="1" value="full">{if ($lang = 'ru')
|
||||
then 'Везде' else 'All'}</option>
|
||||
<option value="form">{if ($lang = 'ru')
|
||||
then 'Формы' else 'Forms'}</option>
|
||||
<option value="sense">{if ($lang = 'ru')
|
||||
then 'Значения' else 'Senses'}</option>
|
||||
<option value="example">{if ($lang = 'ru')
|
||||
then 'Примеры' else 'Examples'}</option>
|
||||
<option value="translation">{if ($lang = 'ru')
|
||||
then 'Переводы' else 'Translations'}</option>
|
||||
<option value="mentioned">{if ($lang = 'ru')
|
||||
then 'Цит. формы' else 'Mentioned'}</option>
|
||||
<option value="gloss">{if ($lang = 'ru')
|
||||
then 'Глоссы' else 'Glosses'}</option>
|
||||
<option value="etym">{if ($lang = 'ru')
|
||||
then 'Этимологии' else 'Etymology'}</option>
|
||||
</select>
|
||||
}
|
||||
<input name="searchQuery"
|
||||
placeholder="{if ($lang = 'ru') then 'Искать' else 'Search'}"
|
||||
value="{session:get('searchQuery')}"
|
||||
aria-label="Search">
|
||||
{if (exists(session:get('searchQuery'))) then
|
||||
attribute disabled {"1"} }
|
||||
</input>
|
||||
{if (not(session:get('searchQuery'))) then
|
||||
<button type="submit">
|
||||
<svg id="abv-search-icon" xmlns="http://www.w3.org/2000/svg"
|
||||
width="16" height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="currentColor" class="bi bi-search">
|
||||
<path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
else
|
||||
(
|
||||
if (session:get('searchN') > 1) then
|
||||
<button type="button" class="link" data-href="./search/prev">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M15 8a.5.5 0 0 0-.5-.5H2.707l3.147-3.146a.5.5 0 1 0-.708-.708l-4 4a.5.5 0 0 0 0 .708l4 4a.5.5 0 0 0 .708-.708L2.707 8.5H14.5A.5.5 0 0 0 15 8"/>
|
||||
</svg>
|
||||
</button>,
|
||||
<button type="button" id="abv-btn-searchResults">{session:get('searchN')}/{array:size(session:get('searchData'))}</button>
|
||||
,
|
||||
if (session:get('searchN') and session:get('searchN') < array:size(session:get('searchData'))) then
|
||||
<button type="button" class="link" data-href="./search/next">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-right" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M1 8a.5.5 0 0 1 .5-.5h11.793l-3.147-3.146a.5.5 0 0 1 .708-.708l4 4a.5.5 0 0 1 0 .708l-4 4a.5.5 0 0 1-.708-.708L13.293 8.5H1.5A.5.5 0 0 1 1 8"/>
|
||||
</svg>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
</form>
|
||||
</li>
|
||||
<li><a href="./home">{
|
||||
switch($lang)
|
||||
case 'ru' return 'Главная'
|
||||
default return 'Home'
|
||||
}
|
||||
</a></li>
|
||||
<li><a href="./dict">{switch($lang)
|
||||
case 'ru' return 'Словарь'
|
||||
default return 'Dictionary'
|
||||
}
|
||||
</a></li>
|
||||
<li><a href="./index">{switch($lang)
|
||||
case 'ru' return 'Указатель'
|
||||
default return 'Index'
|
||||
}</a></li>
|
||||
<li><a href="#">{switch($lang)
|
||||
case 'ru' return 'Литература'
|
||||
default return 'References'
|
||||
}</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>,
|
||||
search:content($lang)
|
||||
)
|
||||
};
|
||||
|
||||
declare function shared:invert-lang($lang as xs:string) as xs:string {
|
||||
if($lang = 'en') then 'ru' else 'en'
|
||||
};
|
11
xq/test.xq
11
xq/test.xq
|
@ -3,10 +3,13 @@ declare namespace abv = "http://ossetic-studies.org/ns/abaevdict";
|
|||
|
||||
import module namespace abv-m = 'http://ossetic-studies.org/ns/abaevdict-mod' at './abv-mod.xqm';
|
||||
|
||||
let $search := abv-m:search('en','full','friend')
|
||||
let $frag := $search(1)('fragment')
|
||||
return $frag
|
||||
(: return abv-m:mark-element(db:get('abaevdict_en',`xml/{$search(1)('entry')}.xml`), $search(1)('nodes')(1), 'friend') => abv-m:make-html('en') :)
|
||||
abv-m:make-html(doc('abaevdict_en/xml/entry_abūzyn.xml'),'en')
|
||||
|
||||
(: for $node in collection('abaevdict')//tei:ref/@target[.='#ref_Walde—Pok.']
|
||||
return replace value of node $node with '#ref_Walde-Pok.' :)
|
||||
|
||||
(: collection(`abaevdict_en/html`)/article[@id='entry_az'] :)
|
||||
(: html:doc('abaevdict_en/html/entry_az.html') :)
|
||||
|
||||
(: let $doc-tr := $doc transform with {
|
||||
replace node xquery:eval("/Q{http://www.tei-c.org/ns/1.0}entry[1]/Q{http://www.tei-c.org/ns/1.0}etym[1]/Q{http://www.tei-c.org/ns/1.0}mentioned[2]/Q{http://www.tei-c.org/ns/1.0}mentioned[18]", {'': .})
|
||||
|
|
|
@ -46,6 +46,23 @@
|
|||
<xsl:when test="current-grouping-key()">
|
||||
<h6 class="abv-headword">
|
||||
<xsl:apply-templates select="current-group()"/>
|
||||
<br/>
|
||||
<a href="#{$entry-id}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-hash" viewBox="0 0 16 16">
|
||||
<path d="M8.39 12.648a1 1 0 0 0-.015.18c0 .305.21.508.5.508.266 0 .492-.172.555-.477l.554-2.703h1.204c.421 0 .617-.234.617-.547 0-.312-.188-.53-.617-.53h-.985l.516-2.524h1.265c.43 0 .618-.227.618-.547 0-.313-.188-.524-.618-.524h-1.046l.476-2.304a1 1 0 0 0 .016-.164.51.51 0 0 0-.516-.516.54.54 0 0 0-.539.43l-.523 2.554H7.617l.477-2.304c.008-.04.015-.118.015-.164a.51.51 0 0 0-.523-.516.54.54 0 0 0-.531.43L6.53 5.484H5.414c-.43 0-.617.22-.617.532s.187.539.617.539h.906l-.515 2.523H4.609c-.421 0-.609.219-.609.531s.188.547.61.547h.976l-.516 2.492c-.008.04-.015.125-.015.18 0 .305.21.508.5.508.265 0 .492-.172.554-.477l.555-2.703h2.242zm-1-6.109h2.266l-.515 2.563H6.859l.532-2.563z"/>
|
||||
</svg>
|
||||
</a>
|
||||
<xsl:if test="//tei:mentioned[@xml:lang != 'os' and not(starts-with(@xml:lang,'os-'))]">
|
||||
<a class="abv-map"
|
||||
hx-get="./map"
|
||||
hx-target="#modal_map"
|
||||
hx-swap="innerHTML"
|
||||
hx-vals='{"{"}"entry": "{$entry-id}"{"}"}'
|
||||
hx-on-click='document.getElementById("modal_map").showModal()'
|
||||
>
|
||||
<img src="/static/map.png"></img>
|
||||
</a>
|
||||
</xsl:if>
|
||||
</h6>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
|
@ -70,12 +87,24 @@
|
|||
group-adjacent="not(self::tei:etym)">
|
||||
<xsl:choose>
|
||||
<xsl:when test="current-grouping-key()">
|
||||
<p class="abv-mainpart">
|
||||
<xsl:apply-templates select="current-group()"/>
|
||||
</p>
|
||||
<div class="abv-mainpart">
|
||||
<p>
|
||||
<xsl:apply-templates select="current-group()"/>
|
||||
</p>
|
||||
</div>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:apply-templates select="current-group()"/>
|
||||
<div class="abv-etym">
|
||||
<xsl:for-each-group select="node()"
|
||||
group-starting-with="tei:lb">
|
||||
<p>
|
||||
<xsl:apply-templates select="current-group()"/>
|
||||
</p>
|
||||
</xsl:for-each-group>
|
||||
</div>
|
||||
<div class="notes">
|
||||
<xsl:apply-templates select="//tei:note[@type='footnote']" mode="footnote"/>
|
||||
</div>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:for-each-group>
|
||||
|
@ -105,7 +134,7 @@
|
|||
<xsl:template match="tei:form/tei:orth">
|
||||
<xsl:variable name="elem">
|
||||
<xsl:choose>
|
||||
<xsl:when test="ancestor::tei:cit[@type='formGrp']">i</xsl:when>
|
||||
<xsl:when test="ancestor::tei:cit[@type='formGrp'] or ancestor::tei:re">i</xsl:when>
|
||||
<xsl:otherwise>b</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
</xsl:variable>
|
||||
|
@ -243,9 +272,7 @@
|
|||
|
||||
<!-- ETYMOLOGY -->
|
||||
<xsl:template match="tei:etym">
|
||||
<p class="abv-etym">
|
||||
<xsl:apply-templates />
|
||||
</p>
|
||||
<xsl:apply-templates />
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="tei:oRef">
|
||||
|
@ -267,7 +294,6 @@
|
|||
</xsl:template>
|
||||
|
||||
<xsl:template match="tei:lb">
|
||||
<xsl:element name="br"/>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="tei:hi">
|
||||
|
@ -312,8 +338,27 @@
|
|||
</td>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match=
|
||||
"text()">
|
||||
<!-- Footnotes -->
|
||||
<xsl:template match="tei:note[@type='footnote']">
|
||||
<xsl:variable name="no"><xsl:number level="single"/></xsl:variable>
|
||||
<a id="fn_{/tei:entry/@xml:id}_{$no}" href="#fntxt_{/tei:entry/@xml:id}_{$no}"><sup><xsl:value-of select="$no"/></sup></a>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="tei:note[@type='footnote']" mode='footnote'>
|
||||
<xsl:variable name="no"><xsl:number level="single"/></xsl:variable>
|
||||
<p>
|
||||
<a id="fntxt_{/tei:entry/@xml:id}_{$no}" href="#fn_{/tei:entry/@xml:id}_{$no}"><sup><xsl:value-of select="$no"/></sup></a>
|
||||
<xsl:apply-templates />
|
||||
</p>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Remove internal notes -->
|
||||
<xsl:template match="tei:note[@type='internal']">
|
||||
</xsl:template>
|
||||
|
||||
<!-- get rid of new lines -->
|
||||
|
||||
<xsl:template match="text()">
|
||||
<xsl:value-of select="replace(translate(., '
', ''), ' +', ' ')"/>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
Loading…
Add table
Reference in a new issue