fc2
16 removals
552 lines
15 additions
552 lines
<div id=deck deck_name="{{Deck}}"></div>
<!-- FC2 CONFIGURATION BEGIN -->
<!-- FC2 CONFIGURATION BEGIN -->
<script type="application/javascript">
<script type="application/javascript">
var config = {
var config = {
prompt: '[...]', // Prompt when no hint
prompt: '[...]', // Prompt when no hint
hint: '[%h]', // %h is replaced with hint text
hint: '[%h]', // %h is replaced with hint text
expose: {
expose: {
char: '!', // Char to mark exposed cloze
char: '!', // Char to mark exposed cloze
pos: 'begin', // Char pos: `pre`, `begin`, `end` or `post`
pos: 'begin', // Char pos: `pre`, `begin`, `end` or `post`
reverse: '{{Invert cloze hiding}}'.toString() // If true exposed clozes are hidden, others shown
reverse: '{{Invert cloze hiding}}'.toString() // If true exposed clozes are hidden, others shown
},
},
scroll: { // Valid values: `none`, `min`, `center`, `context`, `context-top` or `context-bottom`
scroll: { // Valid values: `none`, `min`, `center`, `context`, `context-top` or `context-bottom`
initial: 'center', // Scoll on initial show
initial: 'center', // Scoll on initial show
click: 'min', // Scroll on cloze click
click: 'min', // Scroll on cloze click
iterate: 'min' // Scroll on iteration
iterate: 'min' // Scroll on iteration
},
},
iteration: {
iteration: {
top: false, // Always start iteration from top
top: false, // Always start iteration from top
loop: true, // Restart from top/bottom from end
loop: true, // Restart from top/bottom from end
hide: false // Hide cloze iterated away from
hide: false // Hide cloze iterated away from
},
},
shortcuts: {
shortcuts: {
next: 'j', // Iterate to next cloze
next: 'j', // Iterate to next cloze
previous: 'h', // Iterate to previous cloze
previous: 'h', // Iterate to previous cloze
toggle_all: 'k' // Toggle all clozes and fields
toggle_all: 'k' // Toggle all clozes and fields
},
},
show: { // `false` means initially collapsed/hidden
show: { // `false` means initially collapsed/hidden
inactive: false, // Inactive clozes
inactive: false, // Inactive clozes
additional: false // Additional fields (Note, Mnemonics etc.)
additional: true // Additional fields (Note, Mnemonics etc.)
},
},
fields: {
fields: {
title: true, // Title area
title: true, // Title area
legends: [ ],
legends: [ ],
show_all_button: true, // Optional "show all" button at bottom of page
show_all_button: true, // Optional "show all" button at bottom of page
log: false // Debug information level (`false`, `'error'` or `true`)
log: false // Debug information level (`false`, `'error'` or `true`)
}
}
}
}
</script>
</script>
<!-- FC2 CONFIGURATION END -->
<!-- FC2 CONFIGURATION END -->
<!-- Title field at top of page -->
<!-- Title field at top of page -->
{{#Title}}<div id="fc2-title">{{Title}}</div>{{/Title}}
{{#Title}}<div id="fc2-title">{{Title}}</div>{{/Title}}
<!-- Scrollable area of the screen, required but can be styled/layouted differently -->
<!-- Scrollable area of the screen, required but can be styled/layouted differently -->
<div id="fc2-viewport">
<div id="fc2-viewport">
<!-- Main content field, required but can be styled/layouted differently -->
<!-- Main content field, required but can be styled/layouted differently -->
<div id="fc2-content" class="linkRender">{{cloze:Text}}<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br></div>
<div id="fc2-content" class="linkRender">{{cloze:Text}}</div>
<div id="fc2-content-placeholder">This note type requires <code>nested cloze support</code> and <code>increased meta data</code> features from the Anki 2.1.56+ backend (Anki desktop 2.1.56+, AnkiDroid 2.16alpha93+ with `Use new backend` enabled and AnkiMobile 2.0.88+).</div>
<div id="fc2-content-placeholder">This note type requires <code>nested cloze support</code> and <code>increased meta data</code> features from the Anki 2.1.56+ backend (Anki desktop 2.1.56+, AnkiDroid 2.16alpha93+ with `Use new backend` enabled and AnkiMobile 2.0.88+).</div>
<!-- Additional fields, delete to remove from the cards -->
<!-- Additional fields, delete to remove from the cards -->
<div id="fc2-additional">
{{#Extra}}
<div id="fc2-extra-header" class="fc2-additional-header"></div>
<div id="fc2-extra-content" class="fc2-additional-content">{{Extra}}</div>
{{/Extra}}
{{#Source}}
<gr><br>Source: {{Source}} </gr>
{{/Source}}
<p class=footer>{{clickable:Tags}}</p>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
</div>
</div>
</div>
<!-- FC2 FUNCTIONALITY - DO NOT EDIT BELOW THIS POINT -->
<!-- FC2 FUNCTIONALITY - DO NOT EDIT BELOW THIS POINT -->
<!-- nav bars -->
<!-- nav bars -->
<div id="fc2-nav-toggle-all"></div>
<div id="fc2-nav-toggle-all"></div>
<div id="fc2-nav-prev-cloze"></div>
<div id="fc2-nav-prev-cloze"></div>
<div id="fc2-nav-next-cloze"></div>
<div id="fc2-nav-next-cloze"></div>
<!-- card meta data -->
<!-- card meta data -->
<div id="fc2-meta-type">{{Type}}</div>
<div id="fc2-meta-type">{{Type}}</div>
<div id="fc2-meta-card">{{Card}}</div>
<div id="fc2-meta-card">{{Card}}</div>
<div id="fc2-meta-tags">{{Tags}}</div>
<div id="fc2-meta-tags">{{Tags}}</div>
<div id="fc2-meta-deck">{{Deck}}</div>
<div id="fc2-meta-deck">{{Deck}}</div>
<div id="fc2-meta-subdeck">{{Subdeck}}</div>
<div id="fc2-meta-subdeck">{{Subdeck}}</div>
<div id="fc2-meta-flag">{{CardFlag}}</div>
<div id="fc2-meta-flag">{{CardFlag}}</div>
<script type="application/javascript">
<script type="application/javascript">
var fc2
var fc2
(function () {
(function () {
'use strict';
'use strict';
function logger(element, lvl) {
function logger(element, lvl) {
let log = ((_) => { });
let log = ((_) => { });
if (lvl && element) {
if (lvl && element) {
if (lvl === true) {
if (lvl === true) {
log = ((str, args) => {
log = ((str, args) => {
if (log.element.hidden)
if (log.element.hidden)
log.element.hidden = false;
log.element.hidden = false;
let msg = str;
let msg = str;
if (typeof (args) === 'object') {
if (typeof (args) === 'object') {
for (let i = 0; i < args.length; i++) {
for (let i = 0; i < args.length; i++) {
msg += i ? ', ' : ': ';
msg += i ? ', ' : ': ';
if (typeof (args[i]) == 'object')
if (typeof (args[i]) == 'object')
msg += JSON.stringify(args[i]);
msg += JSON.stringify(args[i]);
else if (typeof (args[i]) == 'string')
else if (typeof (args[i]) == 'string')
msg += `"${args[i]}"`;
msg += `"${args[i]}"`;
else
else
msg += args[i];
msg += args[i];
}
}
}
}
else if (args)
else if (args)
msg += `: ${args}`;
msg += `: ${args}`;
log.element.innerText += `${msg}\n`;
log.element.innerText += `${msg}\n`;
log.element.scrollTop = log.element.scrollHeight;
log.element.scrollTop = log.element.scrollHeight;
});
});
}
}
window.onerror = (emsg, _src, _ln, _col, err) => {
window.onerror = (emsg, _src, _ln, _col, err) => {
if (log.element.hidden)
if (log.element.hidden)
log.element.hidden = false;
log.element.hidden = false;
log.element.innerText += `error ${emsg}:\n${err.stack}\n`;
log.element.innerText += `error ${emsg}:\n${err.stack}\n`;
log.element.scrollTop = log.element.scrollHeight;
log.element.scrollTop = log.element.scrollHeight;
return true;
return true;
};
};
log.element = element;
log.element = element;
log.element.hidden = true;
log.element.hidden = true;
}
}
return log;
return log;
}
}
class Searcher {
class Searcher {
constructor(element, prefix, logger = (() => { })) {
constructor(element, prefix, logger = (() => { })) {
logger('searcher.constructor()');
logger('searcher.constructor()');
this.element = element, this.log = logger;
this.element = element, this.log = logger;
this.matches = [], this.index = -1, this.sstr = '';
this.matches = [], this.index = -1, this.sstr = '';
this.class_ = {
this.class_ = {
match: prefix + 'search-match',
match: prefix + 'search-match',
matches: prefix + 'search-matches'
matches: prefix + 'search-matches'
};
};
const panel = document.createElement('div');
const panel = document.createElement('div');
panel.id = `${prefix}search-panel`;
panel.id = `${prefix}search-panel`;
panel.hidden = true;
panel.hidden = true;
panel.innerHTML = `<input type="text" id="${prefix}search-field" placeholder="Search for text"/><div id="${prefix}search-btn" tabindex="0">Search</div>`;
panel.innerHTML = `<input type="text" id="${prefix}search-field" placeholder="Search for text"/><div id="${prefix}search-btn" tabindex="0">Search</div>`;
this.panel = element.parentElement.insertBefore(panel, this.element.nextElementSibling);
this.panel = element.parentElement.insertBefore(panel, this.element.nextElementSibling);
this.field = document.getElementById(`${prefix}search-field`);
this.field = document.getElementById(`${prefix}search-field`);
this.field.addEventListener('keydown', (evt) => {
this.field.addEventListener('keydown', (evt) => {
if (evt.key === 'Enter') {
if (evt.key === 'Enter') {
this.search();
this.search();
this.button.focus();
this.button.focus();
if (!document.documentElement.classList.contains('mobile'))
if (!document.documentElement.classList.contains('mobile'))
this.field.focus();
this.field.focus();
}
}
else if (evt.key === 'Escape')
else if (evt.key === 'Escape')
return;
return;
evt.stopPropagation();
evt.stopPropagation();
});
});
this.button = document.getElementById(`${prefix}search-btn`);
this.button = document.getElementById(`${prefix}search-btn`);
this.button.onclick = (evt) => { this.search(); };
this.button.onclick = (evt) => { this.search(); };
this.field.onfocus = (evt) => { this.field.select(); };
this.field.onfocus = (evt) => { this.field.select(); };
}
}
search() {
search() {
this.log('searcher.search');
this.log('searcher.search');
if (!this.field?.value) {
if (!this.field?.value) {
this.clear();
this.clear();
return;
return;
}
}
if (this.field.value !== this.sstr) {
if (this.field.value !== this.sstr) {
this.clear();
this.clear();
this.sstr = this.field.value;
this.sstr = this.field.value;
this.highlight(RegExp(this.sstr.replace(/[.*+?^${}()|[\]\\]/g, "\\/*--###JS###--*/"), 'gi'));
this.highlight(RegExp(this.sstr.replace(/[.*+?^${}()|[\]\\]/g, "\\/*--###JS###--*/"), 'gi'));
}
}
if (this.matches?.length) {
if (this.matches?.length) {
if (this.index >= 0)
if (this.index >= 0)
this.matches[this.index].classList.replace(this.class_.match, this.class_.matches);
this.matches[this.index].classList.replace(this.class_.match, this.class_.matches);
this.index = this.index < this.matches.length - 1
this.index = this.index < this.matches.length - 1
? this.index + 1
? this.index + 1
: 0;
: 0;
this.matches[this.index].classList.replace(this.class_.matches, this.class_.match);
this.matches[this.index].classList.replace(this.class_.matches, this.class_.match);
this.matches[this.index].scrollIntoView({ behavior: 'auto', block: 'center', inline: 'nearest' });
this.matches[this.index].scrollIntoView({ behavior: 'auto', block: 'center', inline: 'nearest' });
}
}
}
}
highlight(re) {
highlight(re) {
this.log('searcher.highlight');
this.log('searcher.highlight');
const txt = this.element.textContent;
const txt = this.element.textContent;
const rct = this.element.getBoundingClientRect();
const rct = this.element.getBoundingClientRect();
const stl = getComputedStyle(this.element);
const stl = getComputedStyle(this.element);
const offset = {
const offset = {
top: this.element.scrollTop - rct.top - parseFloat(stl.marginTop),
top: this.element.scrollTop - rct.top - parseFloat(stl.marginTop),
left: this.element.scrollLeft - rct.left - parseFloat(stl.marginLeft)
left: this.element.scrollLeft - rct.left - parseFloat(stl.marginLeft)
};
};
const sel = window.getSelection();
const sel = window.getSelection();
sel.removeAllRanges();
sel.removeAllRanges();
let match, sstr = this.field.value;
let match, sstr = this.field.value;
while (match = re.exec(txt)) {
while (match = re.exec(txt)) {
const itr = nd_itr(this.element);
const itr = nd_itr(this.element);
let index = 0;
let index = 0;
let res = itr.next();
let res = itr.next();
while (!res.done) {
while (!res.done) {
let rng;
let rng;
if (match.index >= index && match.index < index + res.value.length) {
if (match.index >= index && match.index < index + res.value.length) {
rng = new Range();
rng = new Range();
rng.setStart(res.value, match.index - index);
rng.setStart(res.value, match.index - index);
}
}
if (match.index + sstr.length >= index &&
if (match.index + sstr.length >= index &&
match.index + sstr.length <= index + res.value.length &&
match.index + sstr.length <= index + res.value.length &&
rng) {
rng) {
rng.setEnd(res.value, match.index + sstr.length - index);
rng.setEnd(res.value, match.index + sstr.length - index);
sel.addRange(rng);
sel.addRange(rng);
for (const rect of rng.getClientRects()) {
for (const rect of rng.getClientRects()) {
const light = document.createElement('DIV');
const light = document.createElement('DIV');
this.element.appendChild(light);
this.element.appendChild(light);
light.classList.add(this.class_.matches);
light.classList.add(this.class_.matches);
light.style.top = rect.y + offset.top + 'px';
light.style.top = rect.y + offset.top + 'px';
light.style.left = rect.x + offset.left + 'px';
light.style.left = rect.x + offset.left + 'px';
light.style.height = rect.height + 'px';
light.style.height = rect.height + 'px';
light.style.width = rect.width + 'px';
light.style.width = rect.width + 'px';
this.matches.push(light);
this.matches.push(light);
}
}
}
}
index += res.value.length;
index += res.value.length;
res = itr.next();
res = itr.next();
}
}
}
}
sel.removeAllRanges();
sel.removeAllRanges();
function* nd_itr(nd) {
function* nd_itr(nd) {
for (const cnd of nd.childNodes) {
for (const cnd of nd.childNodes) {
if (cnd.nodeType === Node.TEXT_NODE)
if (cnd.nodeType === Node.TEXT_NODE)
yield cnd;
yield cnd;
else
else
yield* nd_itr(cnd);
yield* nd_itr(cnd);
}
}
}
}
}
}
clear() {
clear() {
this.log('searcher.clear');
this.log('searcher.clear');
for (const el of this.matches)
for (const el of this.matches)
el.remove();
el.remove();
this.index = -1, this.sstr = '', this.matches = [];
this.index = -1, this.sstr = '', this.matches = [];
}
}
focus() {
focus() {
this.log('searcher.focus');
this.log('searcher.focus');
this.hidden = false;
this.hidden = false;
this.field.focus();
this.field.focus();
}
}
get hidden() { return this.panel.hidden; }
get hidden() { return this.panel.hidden; }
set hidden(hide) { if (this.panel.hidden = hide)
set hidden(hide) { if (this.panel.hidden = hide)
this.clear(); }
this.clear(); }
}
}
function ancestor(descendant, selector) {
function ancestor(descendant, selector) {
while (descendant && !descendant.matches(selector))
while (descendant && !descendant.matches(selector))
descendant = descendant.parentElement;
descendant = descendant.parentElement;
return descendant;
return descendant;
}
}
class FC2 {
class FC2 {
constructor() {
constructor() {
this.listeners = false;
this.listeners = false;
}
}
load(config, side) {
load(config, side) {
this.viewport = document.getElementById('fc2-viewport');
this.viewport = document.getElementById('fc2-viewport');
let elm = document.getElementById('fc2-log-panel');
let elm = document.getElementById('fc2-log-panel');
if (!elm && config.fields?.log) {
if (!elm && config.fields?.log) {
elm = document.createElement('pre');
elm = document.createElement('pre');
elm.id = 'fc2-log-panel';
elm.id = 'fc2-log-panel';
elm.hidden = true;
elm.hidden = true;
elm = this.viewport.parentElement.appendChild(elm);
elm = this.viewport.parentElement.appendChild(elm);
}
}
this.log = logger(elm, config.fields?.log);
this.log = logger(elm, config.fields?.log);
this.cfg = config;
this.cfg = config;
this.cfg.front = side === 'front';
this.cfg.front = side === 'front';
this.content = document.getElementById('fc2-content');
this.content = document.getElementById('fc2-content');
this.current = this.content.querySelector('.cloze');
this.current = this.content.querySelector('.cloze');
if (this.current.dataset.ordinal === undefined)
if (this.current.dataset.ordinal === undefined)
return;
return;
this.search = new Searcher(this.viewport, 'fc2-', this.log);
this.search = new Searcher(this.viewport, 'fc2-', this.log);
this.ordinal || (this.ordinal = parseInt(this.current.dataset.ordinal));
this.ordinal || (this.ordinal = parseInt(this.current.dataset.ordinal));
this.content.parentElement.classList.remove(this.cfg.front ? 'back' : 'front');
this.content.parentElement.classList.remove(this.cfg.front ? 'back' : 'front');
this.content.parentElement.classList.add(this.cfg.front ? 'front' : 'back');
this.content.parentElement.classList.add(this.cfg.front ? 'front' : 'back');
const tags = document.getElementById('fc2-meta-tags').innerText.split(' ');
const tags = document.getElementById('fc2-meta-tags').innerText.split(' ');
for (const tag of tags) {
for (const tag of tags) {
if (!tag.startsWith('fc2.'))
if (!tag.startsWith('fc2.'))
continue;
continue;
const parts = tag.slice(4).split('.');
const parts = tag.slice(4).split('.');
const tag_side = ['front', 'back'].includes(parts[0]) ? parts.shift() : undefined;
const tag_side = ['front', 'back'].includes(parts[0]) ? parts.shift() : undefined;
if (tag_side && tag_side !== side || this.cfg[parts[0]]?.[parts[1]] === undefined)
if (tag_side && tag_side !== side || this.cfg[parts[0]]?.[parts[1]] === undefined)
continue;
continue;
this.cfg[parts[0]][parts[1]] = typeof (this.cfg[parts[0]][parts[1]]) === 'boolean'
this.cfg[parts[0]][parts[1]] = typeof (this.cfg[parts[0]][parts[1]]) === 'boolean'
? parts[2] === 'true'
? parts[2] === 'true'
: parts.slice(2);
: parts.slice(2);
}
}
let title = document.getElementById('fc2-title');
let title = document.getElementById('fc2-title');
if (!title && this.cfg.fields?.title) {
if (!title && this.cfg.fields?.title) {
let titles;
let titles;
const h1 = this.content.querySelector('h1');
const h1 = this.content.querySelector('h1');
if (h1) {
if (h1) {
titles = h1.innerText;
titles = h1.innerText;
h1.remove();
h1.remove();
}
}
else
else
titles = document.getElementById('fc2-meta-subdeck')?.innerText;
titles = document.getElementById('fc2-meta-subdeck')?.innerText;
if (titles) {
if (titles) {
title = document.createElement('div');
title = document.createElement('div');
title.id = 'fc2-title';
title.id = 'fc2-title';
title.innerText = titles;
title.innerText = titles;
this.viewport.insertAdjacentElement('beforebegin', title);
this.viewport.insertAdjacentElement('beforebegin', title);
}
}
}
}
const expose = this.generate_expose();
const expose = this.generate_expose();
let active_seen = false;
let active_seen = false;
this.content.querySelectorAll('.cloze-inactive, .cloze').forEach(((cloze) => {
this.content.querySelectorAll('.cloze-inactive, .cloze').forEach(((cloze) => {
const active = cloze.classList.contains('cloze');
const active = cloze.classList.contains('cloze');
active_seen || (active_seen = active);
active_seen || (active_seen = active);
const exposed = expose(cloze);
const exposed = expose(cloze);
if (!active && (exposed || cloze.querySelector('.cloze'))) {
if (!active && (exposed || cloze.querySelector('.cloze'))) {
cloze.classList.remove('cloze-inactive');
cloze.classList.remove('cloze-inactive');
return;
return;
}
}
cloze.dataset.hint = this.cfg.front && active && cloze.innerHTML !== '[...]'
cloze.dataset.hint = this.cfg.front && active && cloze.innerHTML !== '[...]'
? this.cfg.hint.replace('%h', cloze.innerHTML.slice(1, cloze.innerHTML.length - 1)) || ""
? this.cfg.hint.replace('%h', cloze.innerHTML.slice(1, cloze.innerHTML.length - 1)) || ""
: this.cfg.prompt;
: this.cfg.prompt;
if (active && this.cfg.front ||
if (active && this.cfg.front ||
!active && (!this.cfg.show.inactive ||
!active && (!this.cfg.show.inactive ||
this.cfg.show.inactive === 'preceding' && active_seen))
this.cfg.show.inactive === 'preceding' && active_seen))
this.hide(cloze);
this.hide(cloze);
}));
}));
if (!this.cfg.show.additional)
if (!this.cfg.show.additional)
this.viewport.querySelectorAll('.fc2-additional-content')
this.viewport.querySelectorAll('.fc2-additional-content')
.forEach(nd => nd.hidden = true);
.forEach(nd => nd.hidden = true);
if (this.cfg.fields?.legends?.length) {
if (this.cfg.fields?.legends?.length) {
const footer = document.createElement('div');
const footer = document.createElement('div');
footer.id = 'fc2-footer';
footer.id = 'fc2-footer';
this.viewport.insertAdjacentElement('afterend', footer);
this.viewport.insertAdjacentElement('afterend', footer);
for (const legend of this.cfg.fields.legends) {
for (const legend of this.cfg.fields.legends) {
const row = document.createElement('div');
const row = document.createElement('div');
row.className = 'fc2-legends';
row.className = 'fc2-legends';
footer.appendChild(row);
footer.appendChild(row);
for (const itm of legend) {
for (const itm of legend) {
let cell = document.createElement('div');
let cell = document.createElement('div');
cell.innerHTML = itm;
cell.innerHTML = itm;
cell = cell.firstElementChild;
cell = cell.firstElementChild;
if (!cell)
if (!cell)
continue;
continue;
cell.className = 'fc2-legend';
cell.className = 'fc2-legend';
row.appendChild(cell);
row.appendChild(cell);
}
}
}
}
}
}
if (this.cfg.fields?.show_all_button) {
if (this.cfg.fields?.show_all_button) {
const btn = document.createElement('button');
const btn = document.createElement('button');
btn.id = "fc2-show-all-btn";
btn.id = "fc2-show-all-btn";
btn.innerText = "Show all";
btn.innerText = "Show all";
this.viewport.insertAdjacentElement('afterend', btn);
this.viewport.insertAdjacentElement('afterend', btn);
}
}
if (this.cfg.front)
if (this.cfg.front)
this.viewport.onscroll = (_evt) => sessionStorage.setItem('fc2_scroll_top', this.viewport.scrollTop.toString());
this.viewport.onscroll = (_evt) => sessionStorage.setItem('fc2_scroll_top', this.viewport.scrollTop.toString());
if (!this.listeners) {
if (!this.listeners) {
document.addEventListener("click", this.mouse.bind(this));
document.addEventListener("click", this.mouse.bind(this));
document.addEventListener("keydown", this.keyboard.bind(this));
document.addEventListener("keydown", this.keyboard.bind(this));
this.listeners = true;
this.listeners = true;
}
}
this.content.style.display = 'block';
this.content.style.display = 'block';
document.getElementById('fc2-content-placeholder').remove();
document.getElementById('fc2-content-placeholder').remove();
window.requestAnimationFrame(() => this.scroll_to({ scroll: this.cfg.scroll.initial }));
window.requestAnimationFrame(() => this.scroll_to({ scroll: this.cfg.scroll.initial }));
}
}
generate_expose() {
generate_expose() {
this.log('generate_expose');
this.log('generate_expose');
let expose_;
let expose_;
if (this.cfg.expose.pos === 'pre') {
if (this.cfg.expose.pos === 'pre') {
expose_ = (el) => {
expose_ = (el) => {
if (el.previousSibling?.data?.endsWith(this.cfg.expose.char)) {
if (el.previousSibling?.data?.endsWith(this.cfg.expose.char)) {
el.previousSibling.data = el.previousSibling.data.slice(0, -1);
el.previousSibling.data = el.previousSibling.data.slice(0, -1);
return true;
return true;
}
}
return false;
return false;
};
};
}
}
else if (this.cfg.expose.pos === 'post') {
else if (this.cfg.expose.pos === 'post') {
expose_ = (el) => {
expose_ = (el) => {
if (el.nextSibling?.data?.startsWith(this.cfg.expose.char)) {
if (el.nextSibling?.data?.startsWith(this.cfg.expose.char)) {
el.nextSibling.data = el.nextSibling.data.substring(1);
el.nextSibling.data = el.nextSibling.data.substring(1);
return true;
return true;
}
}
return false;
return false;
};
};
}
}
else if (this.cfg.expose.pos === 'end') {
else if (this.cfg.expose.pos === 'end') {
expose_ = (el) => {
expose_ = (el) => {
if (el.dataset.cloze?.endsWith(this.cfg.expose.char)) {
if (el.dataset.cloze?.endsWith(this.cfg.expose.char)) {
el.dataset.cloze = el.dataset.cloze.slice(0, -1);
el.dataset.cloze = el.dataset.cloze.slice(0, -1);
return true;
return true;
}
}
else if (el.lastChild?.data?.endsWith(this.cfg.expose.char)) {
else if (el.lastChild?.data?.endsWith(this.cfg.expose.char)) {
el.lastChild.data = el.lastChild.data.slice(0, -1);
el.lastChild.data = el.lastChild.data.slice(0, -1);
return true;
return true;
}
}
return false;
return false;
};
};
}
}
else {
else {
expose_ = (el) => {
expose_ = (el) => {
if (el.dataset.cloze?.startsWith(this.cfg.expose.char)) {
if (el.dataset.cloze?.startsWith(this.cfg.expose.char)) {
el.dataset.cloze = el.dataset.cloze.substring(1);
el.dataset.cloze = el.dataset.cloze.substring(1);
return true;
return true;
}
}
else if (el.firstChild?.data?.startsWith(this.cfg.expose.char)) {
else if (el.firstChild?.data?.startsWith(this.cfg.expose.char)) {
el.firstChild.data = el.firstChild.data.substring(1);
el.firstChild.data = el.firstChild.data.substring(1);
return true;
return true;
}
}
return false;
return false;
};
};
}
}
return this.cfg.expose.reverse ? (el) => { return !expose_(el); } : expose_;
return this.cfg.expose.reverse ? (el) => { return !expose_(el); } : expose_;
}
}
show(el) {
show(el) {
this.log('show', el.tagName);
this.log('show', el.tagName);
if (!el?.classList.contains('hide'))
if (!el?.classList.contains('hide'))
return;
return;
el.classList.remove('hide');
el.classList.remove('hide');
el.innerHTML = el.dataset.cloze;
el.innerHTML = el.dataset.cloze;
for (const child of el.querySelectorAll(':scope .cloze, :scope .cloze-inactive')) {
for (const child of el.querySelectorAll(':scope .cloze, :scope .cloze-inactive')) {
if (child.dataset.hint === undefined)
if (child.dataset.hint === undefined)
child.dataset.hint = '';
child.dataset.hint = '';
this.hide(child);
this.hide(child);
}
}
}
}
hide(el) {
hide(el) {
this.log('hide');
this.log('hide');
if (el?.classList.contains('hide'))
if (el?.classList.contains('hide'))
return;
return;
el.classList.add('hide');
el.classList.add('hide');
if (!this.search.hidden)
if (!this.search.hidden)
this.search.hidden = true;
this.search.hidden = true;
if (el.dataset.cloze === undefined)
if (el.dataset.cloze === undefined)
el.dataset.cloze = el.innerHTML;
el.dataset.cloze = el.innerHTML;
el.innerHTML = el.dataset.hint;
el.innerHTML = el.dataset.hint;
}
}
iter(fwd) {
iter(fwd) {
this.log('iter');
this.log('iter');
const els = this.content.querySelectorAll('.cloze');
const els = this.content.querySelectorAll('.cloze');
let nxt;
let nxt;
if (this.current?.classList.contains('hide'))
if (this.current?.classList.contains('hide'))
nxt = this.current;
nxt = this.current;
if (fwd && this.current === els[els.length - 1])
if (fwd && this.current === els[els.length - 1])
nxt = this.cfg.iteration.loop ? els[0] : this.current;
nxt = this.cfg.iteration.loop ? els[0] : this.current;
else if (!fwd && this.current === els[0])
else if (!fwd && this.current === els[0])
nxt = this.cfg.iteration.loop ? els[els.length - 1] : this.current;
nxt = this.cfg.iteration.loop ? els[els.length - 1] : this.current;
for (let i = 0; !nxt && i < els.length; i++) {
for (let i = 0; !nxt && i < els.length; i++) {
if (els[i] === this.current)
if (els[i] === this.current)
nxt = els[i + (fwd ? 1 : -1)];
nxt = els[i + (fwd ? 1 : -1)];
}
}
if (nxt !== this.current && this.cfg.iteration.hide)
if (nxt !== this.current && this.cfg.iteration.hide)
this.hide(this.current);
this.hide(this.current);
this.show(this.current = nxt);
this.show(this.current = nxt);
this.scroll_to({ scroll: this.cfg.scroll.iterate, cloze: this.current });
this.scroll_to({ scroll: this.cfg.scroll.iterate, cloze: this.current });
}
}
toggle_cloze(cloze) {
toggle_cloze(cloze) {
this.log('toggle_cloze');
this.log('toggle_cloze');
const show = cloze.classList.contains('hide');
const show = cloze.classList.contains('hide');
if (show)
if (show)
this.show(cloze);
this.show(cloze);
else
else
this.hide(cloze);
this.hide(cloze);
return show;
return show;
}
}
toggle_field(field) {
toggle_field(field) {
this.log('toggle_field');
this.log('toggle_field');
const fld = ancestor(field, '.fc2-additional-content') ||
const fld = ancestor(field, '.fc2-additional-content') ||
ancestor(field, '.fc2-additional-header').nextElementSibling;
ancestor(field, '.fc2-additional-header').nextElementSibling;
if (fld)
if (fld)
fld.hidden = !fld.hidden;
fld.hidden = !fld.hidden;
}
}
toggle_all(show = undefined) {
toggle_all(show = undefined) {
this.log('toggle_all');
this.log('toggle_all');
if (show === true || this.search.hidden ||
if (show === true || this.search.hidden ||
show === undefined &&
show === undefined &&
this.content.querySelector('.cloze.hide, .cloze-inactive.hide, .fc2-additional-content[hidden]')) {
this.content.querySelector('.cloze.hide, .cloze-inactive.hide, .fc2-additional-content[hidden]')) {
this.content.querySelectorAll('.cloze.hide, .cloze-inactive.hide')
this.content.querySelectorAll('.cloze.hide, .cloze-inactive.hide')
.forEach(el => { this.show(el); });
.forEach(el => { this.show(el); });
this.viewport.querySelectorAll('.fc2-additional-content[hidden]')
this.viewport.querySelectorAll('.fc2-additional-content[hidden]')
.forEach(el => { el.hidden = false; });
.forEach(el => { el.hidden = false; });
this.search.hidden = false;
this.search.hidden = false;
return true;
return true;
}
}
else {
else {
this.content.querySelectorAll('.cloze:not(.hide), .cloze-inactive:not(.hide)')
this.content.querySelectorAll('.cloze:not(.hide), .cloze-inactive:not(.hide)')
.forEach(el => { this.hide(el); });
.forEach(el => { this.hide(el); });
this.viewport.querySelectorAll('.fc2-additional-content:not([hidden])')
this.viewport.querySelectorAll('.fc2-additional-content:not([hidden])')
.forEach(el => { el.hidden = true; });
.forEach(el => { el.hidden = true; });
this.search.hidden = true;
this.search.hidden = true;
return false;
return false;
}
}
}
}
scroll_to(opts) {
scroll_to(opts) {
this.log('scroll_to');
this.log('scroll_to');
if (!this.cfg.front) {
if (!this.cfg.front) {
const scroll_top = parseFloat(sessionStorage.getItem('fc2_scroll_top'));
const scroll_top = parseFloat(sessionStorage.getItem('fc2_scroll_top'));
if (!isNaN(scroll_top)) {
if (!isNaN(scroll_top)) {
sessionStorage.removeItem('fc2_scroll_top');
sessionStorage.removeItem('fc2_scroll_top');
this.viewport.scrollTop = scroll_top;
this.viewport.scrollTop = scroll_top;
}
}
}
}
if (opts.scroll === 'none')
if (opts.scroll === 'none')
return;
return;
let first, last;
let first, last;
if (opts.cloze)
if (opts.cloze)
first = last = opts.cloze;
first = last = opts.cloze;
else {
else {
const active = this.content.querySelectorAll('.cloze');
const active = this.content.querySelectorAll('.cloze');
first = active[0];
first = active[0];
last = active[active.length - 1];
last = active[active.length - 1];
}
}
const offset = this.viewport.getBoundingClientRect().top;
const offset = this.viewport.getBoundingClientRect().top;
const line_height = (style) => {
const line_height = (style) => {
this.log(' line_height');
this.log(' line_height');
return parseInt(style.height) + parseInt(style.marginTop) + parseInt(style.marginBottom)
return parseInt(style.height) + parseInt(style.marginTop) + parseInt(style.marginBottom)
|| parseInt(style.lineHeight)
|| parseInt(style.lineHeight)
|| 20;
|| 20;
};
};
const vp_height = this.viewport.clientHeight;
const vp_height = this.viewport.clientHeight;
const cloze_top = (first.getBoundingClientRect().top - offset) - line_height(window.getComputedStyle(first?.previousElementSibling || first, ':before')) + 3;
const cloze_top = (first.getBoundingClientRect().top - offset) - line_height(window.getComputedStyle(first?.previousElementSibling || first, ':before')) + 3;
let top;
let top;
const bottom = (last.getBoundingClientRect().bottom - offset) + line_height(window.getComputedStyle(last?.nextElementSibling || last, ':after')) + 3;
const bottom = (last.getBoundingClientRect().bottom - offset) + line_height(window.getComputedStyle(last?.nextElementSibling || last, ':after')) + 3;
let y = 0;
let y = 0;
if (opts.scroll?.slice(0, 7) === 'context') {
if (opts.scroll?.slice(0, 7) === 'context') {
top = 0;
top = 0;
let section, section_seen, cloze_seen;
let section, section_seen, cloze_seen;
const sections = this.content.querySelectorAll('hr, h1, h2, h3, h4, h5, h6, .cloze');
const sections = this.content.querySelectorAll('hr, h1, h2, h3, h4, h5, h6, .cloze');
for (let i = 0; i < sections.length; i++) {
for (let i = 0; i < sections.length; i++) {
cloze_seen || (cloze_seen = sections[i].tagName === 'SPAN');
cloze_seen || (cloze_seen = sections[i].tagName === 'SPAN');
section_seen || (section_seen = sections[i].tagName !== 'SPAN');
section_seen || (section_seen = sections[i].tagName !== 'SPAN');
if (!cloze_seen)
if (!cloze_seen)
section = sections[i];
section = sections[i];
if (cloze_seen && (section || section_seen))
if (cloze_seen && (section || section_seen))
break;
break;
}
}
if (section) {
if (section) {
top = section.tagName === 'HR'
top = section.tagName === 'HR'
? (section.getBoundingClientRect().bottom - offset)
? (section.getBoundingClientRect().bottom - offset)
: (section.getBoundingClientRect().top - offset) - 5;
: (section.getBoundingClientRect().top - offset) - 5;
}
}
else if (!section_seen) {
else if (!section_seen) {
const all = this.content.querySelectorAll('.cloze, .cloze-inactive');
const all = this.content.querySelectorAll('.cloze, .cloze-inactive');
for (let i = 1; i < all.length; i++) {
for (let i = 1; i < all.length; i++) {
if (all[i] === this.current) {
if (all[i] === this.current) {
top = (all[i - 1].getBoundingClientRect().top - offset) - 5;
top = (all[i - 1].getBoundingClientRect().top - offset) - 5;
break;
break;
}
}
}
}
}
}
}
}
else
else
top = cloze_top;
top = cloze_top;
if (['center', 'context', 'context-bottom'].includes(opts.scroll)) {
if (['center', 'context', 'context-bottom'].includes(opts.scroll)) {
if (bottom - top <= vp_height)
if (bottom - top <= vp_height)
opts.scroll === 'context-bottom'
opts.scroll === 'context-bottom'
? y = bottom - vp_height
? y = bottom - vp_height
: y = top + (bottom - top) / 2 - vp_height / 2;
: y = top + (bottom - top) / 2 - vp_height / 2;
else if (this.cfg.front)
else if (this.cfg.front)
y = top;
else
y = bottom - cloze_top <= vp_height
? bottom - vp_height
: cloze_top;
}
else {
if (cloze_top < 0 || bottom - top >= vp_height)
y = cloze_top;
else if (bot