asteroid/bibi/and/jo.js

193 lines
8.9 KiB
JavaScript
Raw Normal View History

2023-12-28 06:39:56 +00:00
(() => { 'use strict'; if(window['bibi:jo']) return;
const Jo = window['bibi:jo'] = { 'version': '____Bibi-Version____',
CSS: require('./jo.scss'),
Status: '',
Bibis: [],
TrustworthyOrigins: [location.origin],
Loaded: 0,
};
const BibiEventRE = /^bibi:[a-z][a-z:_\-]*$/;
Jo.Bibi = function() { return Jo.callBibi.apply(Jo, arguments); };
Jo.callBibi = (Love) => {
let Anchor = null, Frame = null, Receiver = null, ToReceive = [];
try {
if(!(Love instanceof HTMLElement)) {
if(Love && typeof Love == 'object') {
if(!Love['bibi-href']) return null;
Anchor = Jo.create('a', { href: Love['bibi-href'] });
Frame = Jo.create('iframe');
Receiver = Frame;
}
if(Love['bibi-receive'] instanceof Array) ToReceive = Love['bibi-receive'];
} else {
if(/^iframe$/i.test(Love.tagName)) {
const BibiHref = Love.getAttribute('data-bibi-href');
if(!BibiHref) return null;
Anchor = Love.parentNode.insertBefore(Jo.create('a', { href: BibiHref }), Love);
Frame = Love.parentNode.removeChild(Love);
} else if(/^a$/i.test(Love.tagName)) {
if(!Love.href) return null;
Anchor = Love;
Frame = Jo.create('iframe');
(BibiClass => BibiClass ? BibiClass.trim().replace(/\s+/, ' ').split(' ').forEach(CN => CN ? Frame.classList.add(CN) : false) : false)(Anchor.getAttribute('data-bibi-class'));
(BibiID => BibiID ? Frame.setAttribute('id', BibiID ) : false)(Anchor.getAttribute('data-bibi-id' ));
(BibiStyle => BibiStyle ? Frame.setAttribute('style', BibiStyle) : false)(Anchor.getAttribute('data-bibi-style'));
}
Receiver = Love;
let BibiReceive = Receiver.getAttribute('data-bibi-receive');
if(BibiReceive && (BibiReceive = BibiReceive.replace(/\s+/, ''))) ToReceive = BibiReceive.split(',');
}
} catch(Err) { return null; } if(!Anchor || !Frame || !Receiver) return null;
const Bibi = Anchor.Bibi = Frame.Bibi = { Jo: Jo, Anchor: Anchor, Frame: Frame, Receiver: Receiver, Index: Jo.Bibis.length, Status: '' };
Bibi.listen = (EN, fun) => !BibiEventRE.test(EN) ? false : Receiver.addEventListener(EN, Eve => fun.call(Receiver, Eve.detail), false);
Bibi.dispatch = (EN, Det = Bibi) => !BibiEventRE.test(EN) ? false : Receiver.dispatchEvent(new CustomEvent(EN, { detail: Det }));
Bibi.receive = (EN) => !BibiEventRE.test(EN) ? false : Frame.contentWindow.E.add(EN, Det => Bibi.dispatch(EN, Det));
Bibi.post = (EN, V) => !BibiEventRE.test(EN) ? false : Frame.contentWindow.postMessage(`{ "${ EN }" : "${ V }" }`, Anchor.origin);
Bibi.listen('bibi:initialized', (Status) => Bibi.Status = Bibi.Initialized = Status); if(ToReceive.length) Bibi.listen('bibi:initialized', () => ToReceive.forEach(EN => Bibi.receive('' + EN.trim())));
Bibi.listen('bibi:readied', (Status) => Bibi.Status = Bibi.Readied = Status);
Bibi.listen('bibi:prepared', (Status) => Bibi.Status = Bibi.Prepared = Status);
Bibi.listen('bibi:opened', (Status) => Bibi.Status = Bibi.Opened = Status);
Bibi.listen('bibi:opened', () => {
Bibi.move = (Distance) => Bibi.post('bibi:commands:move', Distance);
Bibi.focus = (Target) => Bibi.post('bibi:commands:focus', Target);
Bibi.changeView = (RVM) => Bibi.post('bibi:commands:change-view', RVM);
Bibi.togglePanel = () => Bibi.post('bibi:commands:toggle-panel', '');
});
Anchor.style.display = 'none';
if(!Jo.TrustworthyOrigins.includes(Anchor.origin)) Jo.TrustworthyOrigins.push(Anchor.origin); // It is NOT reflected to S['trustworthy-origins'].
Anchor.href += (/#/.test(Anchor.href) ? '&' : '#') + (() => {
const Fragments = new Jo.Fragments();
Fragments.add('parent-bibi-index', Bibi.Index);
[
'autostart-embedded', 'autostart',
'p',
'fix-reader-view-mode', 'fix-view', 'view-unchangeable',
'full-breadth-layout-in-scroll',
'iipp',
'nav',
'reader-view-mode', 'rvm', 'view',
'start-embedded-in-new-window', 'start-in-new-window'
].forEach(K => { let V = '' + (Love.ownerDocument ? Love.getAttribute('data-bibi-' + K) || '' : Love['bibi-' + K]);
if(V && (() => { switch(K) {
case 'p': return /^(\d*\.)?\d+$/;
case 'iipp': return /^(\d*\.)?\d+$/;
case 'nav': return /^[1-9][0-9]*$/;
case 'rvm': case 'view': K = 'reader-view-mode';
case 'reader-view-mode': return /^(paged|horizontal|vertical)$/;
case 'autostart': case 'start-in-new-window': K = K.replace('start', 'start-embedded'); break;
case 'view-unchangeable': K = 'fix-reader-view-mode'; break;
} return /^(true|false|1|0|yes|no|mobile|desktop)$/; })().test(V)) Fragments.add(K, V);
});
return Fragments.make();
})();
Frame.classList.add('bibi-frame');
Frame.setAttribute('frameborder', '0');
Frame.setAttribute('scrolling', 'auto');
Frame.setAttribute('allowfullscreen', 'true');
Frame.src = Anchor.href;
Jo.Bibis.push(Bibi);
return Bibi;
};
Jo.embed = () => {
const BibisToBeLoaded = [], BibisLoaded = [];
Array.prototype.forEach.call(document.body.querySelectorAll('*[data-bibi]'), Bed => {
if(Bed.getAttribute('data-bibi-processed')) return;
Bed.setAttribute('data-bibi-processed', 'true');
const Bibi = new Jo.Bibi(Bed);
if(Bibi) BibisToBeLoaded.push(Bibi);
});
if(!BibisToBeLoaded.length) return;
//Jo.listen('bibi:jo:embedded', Bibis => console.log(`[Bibi:Jo] Embedded. - ${ Bibis.length } of ${ Jo.Bibis.length }`));
BibisToBeLoaded.forEach(Bibi => {
const Anchor = Bibi.Anchor, Frame = Bibi.Frame;
Bibi.listen('bibi:initialized', () => (BibisLoaded.push(Bibi) < BibisToBeLoaded.length) ? false : Jo.dispatch('bibi:jo:embedded', BibisLoaded));
Anchor.parentNode.insertBefore(Frame, Anchor);
});
};
document.addEventListener('DOMContentLoaded', Jo.embed), window.addEventListener('load', Jo.embed);
window.addEventListener('message', Eve => {
if(!Eve || !Jo.judge(Eve.data, Eve.origin)) return false; try {
Data = JSON.parse(Data);
if(typeof Data != 'object' || !Data) return false;
for(let EN in Data) Jo.dispatch(EN, Data[EN]);
return true; } catch(Err) {} return false;
}, false);
// Utility
Jo.create = (TagName, Properties) => {
const Ele = document.createElement(TagName);
for(let Attribute in Properties) Ele[Attribute] = Properties[Attribute];
return Ele;
};
Jo.encode = (Str) => encodeURIComponent(Str).replace('(', '_BibiKakkoOpen_').replace(')', '_BibiKakkoClose_');
Jo.Fragments = function() { // constructor
this.FragmentKeys = [];
this.FragmentKeysAndValues = {};
this.add = function(Key, Value) {
if(!this.FragmentKeys.includes(Key)) this.FragmentKeys.push(Key);
this.FragmentKeysAndValues[Key] = Value;
};
this.make = function() {
if(!this.FragmentKeys.length) return '';
const Fragments = [];
for(let l = this.FragmentKeys.length, i = 0; i < l; i++) Fragments.push(`${ this.FragmentKeys[i] }=${ Jo.encode(this.FragmentKeysAndValues[this.FragmentKeys[i]]) }`);
return `jo(${ Fragments.join('&') })`;
};
return this;
};
Jo.judge = (Msg, Origin) => (Msg && typeof Msg == 'string' && Origin && typeof Origin == 'string' && Jo.TrustworthyOrigins.includes(Origin));
Jo.listen = (EN, fun) => !BibiEventRE.test(EN) ? false : document.addEventListener(EN, Eve => fun.call(document, Eve.detail));
Jo.dispatch = (EN, Det = Jo) => !BibiEventRE.test(EN) ? false : document.dispatchEvent(new CustomEvent(EN, { detail: Det }));
// Polyfill
if(!Array.prototype.includes) Array.prototype.includes = function(I) { for(let l = this.length, i = 0; i < l; i++) if(this[i] == I) return true; return false; };
if(!window.CustomEvent || (typeof window.CustomEvent !== 'function') && (window.CustomEvent.toString().indexOf('CustomEventConstructor') === -1)) {
window.CustomEvent = function(EventName, Arguments) { // constructor
Arguments = Arguments || { bubbles: false, cancelable: false, detail: undefined };
const Eve = document.createEvent('CustomEvent');
Eve.initCustomEvent(EventName, Arguments.bubbles, Arguments.cancelable, Arguments.detail);
return Eve;
};
window.CustomEvent.prototype = window.Event.prototype;
}
})();