...
 
Commits (2)
export default class SIBLink extends HTMLElement {
constructor() {
super();
this.addEventListener('click', event => this.trigger());
}
trigger() {
const route = this.getAttribute('next')
const resource = this.dataset.src && { '@id': this.dataset.src }
if(!route && !resource) return;
this.dispatchEvent(
new CustomEvent('requestNavigation', {
bubbles: true,
detail: { route, resource },
}),
);
}
constructor() {
super();
this.addEventListener('click', event => this.trigger());
}
trigger() {
const route = this.getAttribute('next');
const resource = this.dataset.src && { '@id': this.dataset.src };
if (!route && !resource) return;
this.dispatchEvent(
new CustomEvent('requestNavigation', {
bubbles: true,
detail: { route, resource },
}),
);
}
}
customElements.define('sib-link', SIBLink);
export default class SIBRoute extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.addEventListener('click', event => this.router.navigate(this.name));
}
constructor() {
super();
}
connectedCallback() {
this.addEventListener('click', event => this.router.navigate(this.name));
}
get name() {
return this.getAttribute('name');
}
set name(name) {
this.setAttribute('name', name);
}
get view(){
const view = document.getElementById(this.name);
if(!view) {
throw new Error(`view "#${this.name}" is not in document`);
}
this.view = view;
return view;
}
set view(value) {
Object.defineProperty(this, 'view', {value});
}
get router() {
return this.closest('sib-router');
}
get resourceId() {
const id = this.router.currentURL.split('/')[1];
if(!id || !id.startsWith('@')) return '';
return this.decodeId(id);
get name() {
return this.getAttribute('name');
}
set name(name) {
this.setAttribute('name', name);
}
get view() {
const view = document.getElementById(this.name);
if (!view) {
throw new Error(`view "#${this.name}" is not in document`);
}
this.view = view;
return view;
}
set view(value) {
Object.defineProperty(this, 'view', { value });
}
get router() {
return this.closest('sib-router');
}
get resourceId() {
const id = this.router.currentURL.split('/')[1];
if (!id || !id.startsWith('@')) return '';
return this.decodeId(id);
}
encodeId(id) {
if (!id) return '';
return '@' + encodeURIComponent(id);
}
encodeId(id) {
if (!id) return '';
return '@' + encodeURIComponent(id);
}
decodeId(id) {
if(!id) return '';
return decodeURIComponent(id.replace(/^@/, ''));
}
decodeId(id) {
if (!id) return '';
return decodeURIComponent(id.replace(/^@/, ''));
}
getPath(prefix, name, id = '') {
const encodedId = this.encodeId(id);
return [prefix, name, encodedId].filter(s => s).join('/');
}
updateResource() {
if(this.resourceId) {
if(this.view.hasAttribute('bind-resources')) {
this.view.setAttribute('data-src', this.resourceId);
}
for(let element of this.view.querySelectorAll('[bind-resources]')) {
element.setAttribute('data-src', this.resourceId);
}
}
getPath(prefix, name, id = '') {
const encodedId = this.encodeId(id);
return [prefix, name, encodedId].filter(s => s).join('/');
}
updateResource() {
if (this.resourceId) {
if (this.view.hasAttribute('bind-resources')) {
this.view.setAttribute('data-src', this.resourceId);
}
for (let element of this.view.querySelectorAll('[bind-resources]')) {
element.setAttribute('data-src', this.resourceId);
}
}
updateSubRouters() {
for(let router of this.view.querySelectorAll('sib-router')){
const routePrefix = this.getPath(this.router.prefix, this.name, this.resourceId);
router.setAttribute('route-prefix', routePrefix);
router.display();
}
}
updateSubRouters() {
for (let router of this.view.querySelectorAll('sib-router')) {
const routePrefix = this.getPath(
this.router.prefix,
this.name,
this.resourceId,
);
router.setAttribute('route-prefix', routePrefix);
router.display();
}
activate() {
if ('HTMLDialogElement' in window && this.view instanceof window.HTMLDialogElement){
if(!this.view.hasAttribute('open')) {
this.view.showModal();
}
} else {
this.view.removeAttribute('hidden');
}
this.setAttribute('active', '');
this.updateResource();
this.updateSubRouters();
}
activate() {
if (
'HTMLDialogElement' in window &&
this.view instanceof window.HTMLDialogElement
) {
if (!this.view.hasAttribute('open')) {
this.view.showModal();
}
} else {
this.view.removeAttribute('hidden');
}
hide() {
if ('HTMLDialogElement' in window && this.view instanceof window.HTMLDialogElement){
this.view.close()
} else {
this.view.setAttribute('hidden', '');
}
this.removeAttribute('active');
this.setAttribute('active', '');
this.updateResource();
this.updateSubRouters();
}
hide() {
if (
'HTMLDialogElement' in window &&
this.view instanceof window.HTMLDialogElement
) {
this.view.close();
} else {
this.view.setAttribute('hidden', '');
}
this.removeAttribute('active');
}
}
customElements.define('sib-route', SIBRoute);
export default class SIBRouter extends HTMLElement {
constructor() {
super();
window.addEventListener('popstate', () => this.display());
window.addEventListener('requestNavigation', ({detail}) =>
this.navigate(detail.route, detail.resource, detail.keepURL)
);
window.addEventListener('DOMContentLoaded', () => {
for (const route of this.routes) {
route.updateSubRouters();
}
this.display();
});
}
get useHash() {
return this.hasAttribute('use-hash');
}
get prefix() {
let prefix = this.getAttribute('route-prefix') || '';
if(prefix.slice(-1) == '/')
prefix = prefix.slice(0, -1);
return prefix;
}
get defaultRoute() {
return this.getAttribute('default-route') || '';
}
get currentURL() {
let url = this.useHash ? location.hash : location.pathname;
url = url.slice(1);
if(url.startsWith(this.prefix)) {
url = url.slice(this.prefix.length);
}
return stripSlashes(url);
constructor() {
super();
window.addEventListener('popstate', () => this.display());
window.addEventListener('requestNavigation', ({ detail }) =>
this.navigate(detail.route, detail.resource, detail.keepURL),
);
window.addEventListener('DOMContentLoaded', () => {
for (const route of this.routes) {
route.updateSubRouters();
}
this.display();
});
}
get useHash() {
return this.hasAttribute('use-hash');
}
get prefix() {
let prefix = this.getAttribute('route-prefix') || '';
if (prefix.slice(-1) == '/') prefix = prefix.slice(0, -1);
return prefix;
}
get defaultRoute() {
return this.getAttribute('default-route') || '';
}
get currentURL() {
let url = this.useHash ? location.hash : location.pathname;
url = url.slice(1);
if (url.startsWith(this.prefix)) {
url = url.slice(this.prefix.length);
}
get currentRouteName() {
return this.currentURL.split('/')[0] || this.defaultRoute;
return stripSlashes(url);
}
get currentRouteName() {
return this.currentURL.split('/')[0] || this.defaultRoute;
}
get routes() {
return this.querySelectorAll('sib-route');
}
display(routeName = this.currentRouteName, resource) {
for (let route of this.routes) {
if (routeName === route.name) {
this.dispatchEvent(
new CustomEvent('navigate', {
detail: { route: routeName, resource },
}),
);
route.activate();
} else {
route.hide();
}
}
get routes() {
return this.querySelectorAll('sib-route');
}
navigate(routeName = '', resource, keepURL) {
let route;
if (routeName) {
route = this.querySelector('sib-route[name="' + routeName + '"]');
} else if (!routeName && resource && resource['@type']) {
route = this.querySelector(
'sib-route[rdf-type="' + resource['@type'] + '"]',
);
routeName = route ? route.attributes.name.value : null;
}
display(routeName = this.currentRouteName, resource) {
for(let route of this.routes) {
if(routeName === route.name) {
this.dispatchEvent(new CustomEvent('navigate', {detail: {route: routeName, resource}}));
route.activate();
} else {
route.hide();
}
}
if (!route) return; //this route is not for me!
if (this.prefix) {
const route = this.prefix.split('/').pop();
setTimeout(() => {
this.dispatchEvent(
new CustomEvent('requestNavigation', {
detail: { route: route, keepURL: true },
bubbles: true,
}),
);
});
}
navigate(routeName = '', resource, keepURL) {
let route
if(routeName) {
route = this.querySelector('sib-route[name="'+routeName+'"]');
} else if (!routeName && resource && resource['@type']) {
route = this.querySelector('sib-route[rdf-type="'+resource['@type']+'"]');
routeName = route ? route.attributes.name.value : null
}
if(!route) return; //this route is not for me!
if(this.prefix) {
const route = this.prefix.split('/').pop();
setTimeout(() => {
this.dispatchEvent(new CustomEvent('requestNavigation', {
detail: {route: route, keepURL: true},
bubbles: true,
}))
});
}
const id = resource ? resource['@id'] : null;
const path = route.getPath(this.prefix, routeName, id);
if(!keepURL && !this.hasAttribute('keep-url')) {
//update current URL
const prefix = this.useHash ? '#' : '/';
history.pushState({}, routeName, prefix + path);
}
this.display(routeName, resource);
const id = resource ? resource['@id'] : null;
const path = route.getPath(this.prefix, routeName, id);
if (!keepURL && !this.hasAttribute('keep-url')) {
//update current URL
const prefix = this.useHash ? '#' : '/';
history.pushState({}, routeName, prefix + path);
}
this.display(routeName, resource);
}
}
customElements.define('sib-router', SIBRouter);
function stripSlashes(str) {
return str.replace(/^\/+|\/+$/g, '');
return str.replace(/^\/+|\/+$/g, '');
}