Merge pull request #224 from toasted-nutbread/display-jquery-optimizations
Remove jQuery usage from Display.js
This commit is contained in:
commit
64eed33e88
@ -19,20 +19,27 @@
|
|||||||
|
|
||||||
class DisplaySearch extends Display {
|
class DisplaySearch extends Display {
|
||||||
constructor() {
|
constructor() {
|
||||||
super($('#spinner'), $('#content'));
|
super(document.querySelector('#spinner'), document.querySelector('#content'));
|
||||||
|
|
||||||
this.optionsContext = {
|
this.optionsContext = {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
url: window.location.href
|
url: window.location.href
|
||||||
};
|
};
|
||||||
|
|
||||||
this.search = $('#search').click(this.onSearch.bind(this));
|
this.search = document.querySelector('#search');
|
||||||
this.query = $('#query').on('input', this.onSearchInput.bind(this));
|
this.query = document.querySelector('#query');
|
||||||
this.intro = $('#intro');
|
this.intro = document.querySelector('#intro');
|
||||||
|
this.introHidden = false;
|
||||||
|
|
||||||
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
||||||
|
|
||||||
window.wanakana.bind(this.query.get(0));
|
if (this.search !== null) {
|
||||||
|
this.search.addEventListener('click', (e) => this.onSearch(e), false);
|
||||||
|
}
|
||||||
|
if (this.query !== null) {
|
||||||
|
this.query.addEventListener('input', () => this.onSearchInput(), false);
|
||||||
|
window.wanakana.bind(this.query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
@ -40,23 +47,50 @@ class DisplaySearch extends Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onSearchClear() {
|
onSearchClear() {
|
||||||
this.query.focus().select();
|
if (this.query === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.query.focus();
|
||||||
|
this.query.select();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchInput() {
|
onSearchInput() {
|
||||||
this.search.prop('disabled', this.query.val().length === 0);
|
this.search.disabled = (this.query === null || this.query.value.length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onSearch(e) {
|
async onSearch(e) {
|
||||||
|
if (this.query === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.intro.slideUp();
|
this.hideIntro();
|
||||||
const {length, definitions} = await apiTermsFind(this.query.val(), this.optionsContext);
|
const {length, definitions} = await apiTermsFind(this.query.value, this.optionsContext);
|
||||||
super.termsShow(definitions, await apiOptionsGet(this.optionsContext));
|
super.termsShow(definitions, await apiOptionsGet(this.optionsContext));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hideIntro() {
|
||||||
|
if (this.introHidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.introHidden = true;
|
||||||
|
|
||||||
|
if (this.intro === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = this.intro.getBoundingClientRect();
|
||||||
|
this.intro.style.height = `${size.height}px`;
|
||||||
|
this.intro.style.transition = 'height 0.4s ease-in-out 0s';
|
||||||
|
window.getComputedStyle(this.intro).getPropertyValue('height'); // Commits height so next line can start animation
|
||||||
|
this.intro.style.height = '0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.yomichan_search = new DisplaySearch();
|
window.yomichan_search = new DisplaySearch();
|
||||||
|
@ -10,21 +10,19 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div id="intro">
|
<div id="intro" style="overflow: hidden;">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>Yomichan Search</h1>
|
<h1>Yomichan Search</h1>
|
||||||
</div>
|
</div>
|
||||||
<p>Search your installed dictionaries by entering a Japanese expression into the field below.</p>
|
<p style="margin-bottom: 0;">Search your installed dictionaries by entering a Japanese expression into the field below.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<form class="input-group" style="padding-top: 10px;">
|
||||||
<form class="input-group">
|
<input type="text" class="form-control" placeholder="Search for..." id="query" autofocus>
|
||||||
<input type="text" class="form-control" placeholder="Search for..." id="query" autofocus>
|
<span class="input-group-btn">
|
||||||
<span class="input-group-btn">
|
<input type="submit" class="btn btn-default form-control" id="search" value="Search" disabled>
|
||||||
<input type="submit" class="btn btn-default form-control" id="search" value="Search" disabled>
|
</span>
|
||||||
</span>
|
</form>
|
||||||
</form>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div id="spinner">
|
<div id="spinner">
|
||||||
<img src="/mixed/img/spinner.gif">
|
<img src="/mixed/img/spinner.gif">
|
||||||
@ -34,7 +32,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/mixed/lib/handlebars.min.js"></script>
|
<script src="/mixed/lib/handlebars.min.js"></script>
|
||||||
<script src="/mixed/lib/jquery.min.js"></script>
|
|
||||||
<script src="/mixed/lib/wanakana.min.js"></script>
|
<script src="/mixed/lib/wanakana.min.js"></script>
|
||||||
|
|
||||||
<script src="/mixed/js/extension.js"></script>
|
<script src="/mixed/js/extension.js"></script>
|
||||||
@ -49,6 +46,7 @@
|
|||||||
<script src="/fg/js/source.js"></script>
|
<script src="/fg/js/source.js"></script>
|
||||||
<script src="/mixed/js/display.js"></script>
|
<script src="/mixed/js/display.js"></script>
|
||||||
<script src="/mixed/js/japanese.js"></script>
|
<script src="/mixed/js/japanese.js"></script>
|
||||||
|
<script src="/mixed/js/scroll.js"></script>
|
||||||
|
|
||||||
<script src="/bg/js/search.js"></script>
|
<script src="/bg/js/search.js"></script>
|
||||||
<script src="/bg/js/search-frontend.js"></script>
|
<script src="/bg/js/search-frontend.js"></script>
|
||||||
|
@ -31,7 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/mixed/lib/jquery.min.js"></script>
|
|
||||||
<script src="/mixed/lib/wanakana.min.js"></script>
|
<script src="/mixed/lib/wanakana.min.js"></script>
|
||||||
|
|
||||||
<script src="/mixed/js/extension.js"></script>
|
<script src="/mixed/js/extension.js"></script>
|
||||||
@ -41,6 +40,7 @@
|
|||||||
<script src="/fg/js/document.js"></script>
|
<script src="/fg/js/document.js"></script>
|
||||||
<script src="/fg/js/source.js"></script>
|
<script src="/fg/js/source.js"></script>
|
||||||
<script src="/mixed/js/display.js"></script>
|
<script src="/mixed/js/display.js"></script>
|
||||||
|
<script src="/mixed/js/scroll.js"></script>
|
||||||
|
|
||||||
<script src="/fg/js/float.js"></script>
|
<script src="/fg/js/float.js"></script>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
class DisplayFloat extends Display {
|
class DisplayFloat extends Display {
|
||||||
constructor() {
|
constructor() {
|
||||||
super($('#spinner'), $('#definitions'));
|
super(document.querySelector('#spinner'), document.querySelector('#definitions'));
|
||||||
this.autoPlayAudioTimer = null;
|
this.autoPlayAudioTimer = null;
|
||||||
this.styleNode = null;
|
this.styleNode = null;
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ class DisplayFloat extends Display {
|
|||||||
|
|
||||||
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
this.dependencies = Object.assign({}, this.dependencies, {docRangeFromPoint, docSentenceExtract});
|
||||||
|
|
||||||
$(window).on('message', utilAsync(this.onMessage.bind(this)));
|
window.addEventListener('message', (e) => this.onMessage(e), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
@ -42,8 +42,16 @@ class DisplayFloat extends Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onOrphaned() {
|
onOrphaned() {
|
||||||
$('#definitions').hide();
|
const definitions = document.querySelector('#definitions');
|
||||||
$('#error-orphaned').show();
|
const errorOrphaned = document.querySelector('#error-orphaned');
|
||||||
|
|
||||||
|
if (definitions !== null) {
|
||||||
|
definitions.style.setProperty('display', 'none', 'important');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorOrphaned !== null) {
|
||||||
|
errorOrphaned.style.setProperty('display', 'block', 'important');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearchClear() {
|
onSearchClear() {
|
||||||
@ -86,7 +94,7 @@ class DisplayFloat extends Display {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const {action, params} = e.originalEvent.data;
|
const {action, params} = e.data;
|
||||||
const handler = handlers[action];
|
const handler = handlers[action];
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler(params);
|
handler(params);
|
||||||
|
@ -230,3 +230,7 @@ div.glossary-item.compact-glossary {
|
|||||||
.info-output td {
|
.info-output td {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.entry:not(.entry-current) .current {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@ -28,11 +28,14 @@ class Display {
|
|||||||
this.index = 0;
|
this.index = 0;
|
||||||
this.audioCache = {};
|
this.audioCache = {};
|
||||||
this.optionsContext = {};
|
this.optionsContext = {};
|
||||||
|
this.eventListeners = [];
|
||||||
|
|
||||||
this.dependencies = {};
|
this.dependencies = {};
|
||||||
|
|
||||||
$(document).keydown(this.onKeyDown.bind(this));
|
this.windowScroll = new WindowScroll();
|
||||||
$(document).on('wheel', this.onWheel.bind(this));
|
|
||||||
|
document.addEventListener('keydown', this.onKeyDown.bind(this));
|
||||||
|
document.addEventListener('wheel', this.onWheel.bind(this), {passive: false});
|
||||||
}
|
}
|
||||||
|
|
||||||
onError(error) {
|
onError(error) {
|
||||||
@ -52,12 +55,13 @@ class Display {
|
|||||||
try {
|
try {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const link = $(e.target);
|
const link = e.target;
|
||||||
|
this.windowScroll.toY(0);
|
||||||
const context = {
|
const context = {
|
||||||
source: {
|
source: {
|
||||||
definitions: this.definitions,
|
definitions: this.definitions,
|
||||||
index: Display.entryIndexFind(link),
|
index: this.entryIndexFind(link),
|
||||||
scroll: $('html,body').scrollTop()
|
scroll: this.windowScroll.y
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,7 +71,7 @@ class Display {
|
|||||||
context.source.source = this.context.source;
|
context.source.source = this.context.source;
|
||||||
}
|
}
|
||||||
|
|
||||||
const kanjiDefs = await apiKanjiFind(link.text(), this.optionsContext);
|
const kanjiDefs = await apiKanjiFind(link.textContent, this.optionsContext);
|
||||||
this.kanjiShow(kanjiDefs, this.options, context);
|
this.kanjiShow(kanjiDefs, this.options, context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
@ -80,7 +84,7 @@ class Display {
|
|||||||
|
|
||||||
const {docRangeFromPoint, docSentenceExtract} = this.dependencies;
|
const {docRangeFromPoint, docSentenceExtract} = this.dependencies;
|
||||||
|
|
||||||
const clickedElement = $(e.target);
|
const clickedElement = e.target;
|
||||||
const textSource = docRangeFromPoint(e.clientX, e.clientY, this.options);
|
const textSource = docRangeFromPoint(e.clientX, e.clientY, this.options);
|
||||||
if (textSource === null) {
|
if (textSource === null) {
|
||||||
return false;
|
return false;
|
||||||
@ -102,11 +106,12 @@ class Display {
|
|||||||
textSource.cleanup();
|
textSource.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.windowScroll.toY(0);
|
||||||
const context = {
|
const context = {
|
||||||
source: {
|
source: {
|
||||||
definitions: this.definitions,
|
definitions: this.definitions,
|
||||||
index: Display.entryIndexFind(clickedElement),
|
index: this.entryIndexFind(clickedElement),
|
||||||
scroll: $('html,body').scrollTop()
|
scroll: this.windowScroll.y
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,38 +129,38 @@ class Display {
|
|||||||
|
|
||||||
onAudioPlay(e) {
|
onAudioPlay(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const link = $(e.currentTarget);
|
const link = e.currentTarget;
|
||||||
const definitionIndex = Display.entryIndexFind(link);
|
const entry = link.closest('.entry');
|
||||||
const expressionIndex = link.closest('.entry').find('.expression .action-play-audio').index(link);
|
const definitionIndex = this.entryIndexFind(entry);
|
||||||
|
const expressionIndex = Display.indexOf(entry.querySelectorAll('.expression .action-play-audio'), link);
|
||||||
this.audioPlay(this.definitions[definitionIndex], expressionIndex);
|
this.audioPlay(this.definitions[definitionIndex], expressionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNoteAdd(e) {
|
onNoteAdd(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const link = $(e.currentTarget);
|
const link = e.currentTarget;
|
||||||
const index = Display.entryIndexFind(link);
|
const index = this.entryIndexFind(link);
|
||||||
this.noteAdd(this.definitions[index], link.data('mode'));
|
this.noteAdd(this.definitions[index], link.dataset.mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
onNoteView(e) {
|
onNoteView(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const link = $(e.currentTarget);
|
const link = e.currentTarget;
|
||||||
const index = Display.entryIndexFind(link);
|
apiNoteView(link.dataset.noteId);
|
||||||
apiNoteView(link.data('noteId'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyDown(e) {
|
onKeyDown(e) {
|
||||||
const noteTryAdd = mode => {
|
const noteTryAdd = mode => {
|
||||||
const button = Display.adderButtonFind(this.index, mode);
|
const button = this.adderButtonFind(this.index, mode);
|
||||||
if (button.length !== 0 && !button.hasClass('disabled')) {
|
if (button !== null && !button.classList.contains('disabled')) {
|
||||||
this.noteAdd(this.definitions[this.index], mode);
|
this.noteAdd(this.definitions[this.index], mode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const noteTryView = mode => {
|
const noteTryView = mode => {
|
||||||
const button = Display.viewerButtonFind(this.index);
|
const button = this.viewerButtonFind(this.index);
|
||||||
if (button.length !== 0 && !button.hasClass('disabled')) {
|
if (button !== null && !button.classList.contains('disabled')) {
|
||||||
apiNoteView(button.data('noteId'));
|
apiNoteView(button.dataset.noteId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -237,7 +242,8 @@ class Display {
|
|||||||
|
|
||||||
80: /* p */ () => {
|
80: /* p */ () => {
|
||||||
if (e.altKey) {
|
if (e.altKey) {
|
||||||
if ($('.entry').eq(this.index).data('type') === 'term') {
|
const entry = this.getEntry(this.index);
|
||||||
|
if (entry !== null && entry.dataset.type === 'term') {
|
||||||
this.audioPlay(this.definitions[this.index], this.firstExpressionIndex);
|
this.audioPlay(this.definitions[this.index], this.firstExpressionIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,13 +265,12 @@ class Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onWheel(e) {
|
onWheel(e) {
|
||||||
const event = e.originalEvent;
|
|
||||||
const handler = () => {
|
const handler = () => {
|
||||||
if (event.altKey) {
|
if (e.altKey) {
|
||||||
if (event.deltaY < 0) { // scroll up
|
if (e.deltaY < 0) { // scroll up
|
||||||
this.entryScrollIntoView(this.index - 1, null, true);
|
this.entryScrollIntoView(this.index - 1, null, true);
|
||||||
return true;
|
return true;
|
||||||
} else if (event.deltaY > 0) { // scroll down
|
} else if (e.deltaY > 0) { // scroll down
|
||||||
this.entryScrollIntoView(this.index + 1, null, true);
|
this.entryScrollIntoView(this.index + 1, null, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -273,12 +278,14 @@ class Display {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (handler()) {
|
if (handler()) {
|
||||||
event.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async termsShow(definitions, options, context) {
|
async termsShow(definitions, options, context) {
|
||||||
try {
|
try {
|
||||||
|
this.clearEventListeners();
|
||||||
|
|
||||||
if (!context || context.focus !== false) {
|
if (!context || context.focus !== false) {
|
||||||
window.focus();
|
window.focus();
|
||||||
}
|
}
|
||||||
@ -310,7 +317,7 @@ class Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const content = await apiTemplateRender('terms.html', params);
|
const content = await apiTemplateRender('terms.html', params);
|
||||||
this.container.html(content);
|
this.container.innerHTML = content;
|
||||||
const {index, scroll} = context || {};
|
const {index, scroll} = context || {};
|
||||||
this.entryScrollIntoView(index || 0, scroll);
|
this.entryScrollIntoView(index || 0, scroll);
|
||||||
|
|
||||||
@ -318,13 +325,13 @@ class Display {
|
|||||||
this.autoPlayAudio();
|
this.autoPlayAudio();
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.action-add-note').click(this.onNoteAdd.bind(this));
|
this.addEventListeners('.action-add-note', 'click', this.onNoteAdd.bind(this));
|
||||||
$('.action-view-note').click(this.onNoteView.bind(this));
|
this.addEventListeners('.action-view-note', 'click', this.onNoteView.bind(this));
|
||||||
$('.action-play-audio').click(this.onAudioPlay.bind(this));
|
this.addEventListeners('.action-play-audio', 'click', this.onAudioPlay.bind(this));
|
||||||
$('.kanji-link').click(this.onKanjiLookup.bind(this));
|
this.addEventListeners('.kanji-link', 'click', this.onKanjiLookup.bind(this));
|
||||||
$('.source-term').click(this.onSourceTermView.bind(this));
|
this.addEventListeners('.source-term', 'click', this.onSourceTermView.bind(this));
|
||||||
if (this.options.scanning.enablePopupSearch) {
|
if (this.options.scanning.enablePopupSearch) {
|
||||||
$('.glossary-item').click(this.onTermLookup.bind(this));
|
this.addEventListeners('.glossary-item', 'click', this.onTermLookup.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.adderButtonUpdate(['term-kanji', 'term-kana'], sequence);
|
await this.adderButtonUpdate(['term-kanji', 'term-kana'], sequence);
|
||||||
@ -335,6 +342,8 @@ class Display {
|
|||||||
|
|
||||||
async kanjiShow(definitions, options, context) {
|
async kanjiShow(definitions, options, context) {
|
||||||
try {
|
try {
|
||||||
|
this.clearEventListeners();
|
||||||
|
|
||||||
if (!context || context.focus !== false) {
|
if (!context || context.focus !== false) {
|
||||||
window.focus();
|
window.focus();
|
||||||
}
|
}
|
||||||
@ -362,13 +371,13 @@ class Display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const content = await apiTemplateRender('kanji.html', params);
|
const content = await apiTemplateRender('kanji.html', params);
|
||||||
this.container.html(content);
|
this.container.innerHTML = content;
|
||||||
const {index, scroll} = context || {};
|
const {index, scroll} = context || {};
|
||||||
this.entryScrollIntoView(index || 0, scroll);
|
this.entryScrollIntoView(index || 0, scroll);
|
||||||
|
|
||||||
$('.action-add-note').click(this.onNoteAdd.bind(this));
|
this.addEventListeners('.action-add-note', 'click', this.onNoteAdd.bind(this));
|
||||||
$('.action-view-note').click(this.onNoteView.bind(this));
|
this.addEventListeners('.action-view-note', 'click', this.onNoteView.bind(this));
|
||||||
$('.source-term').click(this.onSourceTermView.bind(this));
|
this.addEventListeners('.source-term', 'click', this.onSourceTermView.bind(this));
|
||||||
|
|
||||||
await this.adderButtonUpdate(['kanji'], sequence);
|
await this.adderButtonUpdate(['kanji'], sequence);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -390,14 +399,13 @@ class Display {
|
|||||||
for (let i = 0; i < states.length; ++i) {
|
for (let i = 0; i < states.length; ++i) {
|
||||||
const state = states[i];
|
const state = states[i];
|
||||||
for (const mode in state) {
|
for (const mode in state) {
|
||||||
const button = Display.adderButtonFind(i, mode);
|
const button = this.adderButtonFind(i, mode);
|
||||||
if (state[mode]) {
|
if (button === null) {
|
||||||
button.removeClass('disabled');
|
continue;
|
||||||
} else {
|
|
||||||
button.addClass('disabled');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
button.removeClass('pending');
|
button.classList.toggle('disabled', !state[mode]);
|
||||||
|
button.classList.remove('pending');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -409,22 +417,29 @@ class Display {
|
|||||||
index = Math.min(index, this.definitions.length - 1);
|
index = Math.min(index, this.definitions.length - 1);
|
||||||
index = Math.max(index, 0);
|
index = Math.max(index, 0);
|
||||||
|
|
||||||
$('.current').hide().eq(index).show();
|
const entryPre = this.getEntry(this.index);
|
||||||
|
if (entryPre !== null) {
|
||||||
|
entryPre.classList.remove('entry-current');
|
||||||
|
}
|
||||||
|
|
||||||
const container = $('html,body').stop();
|
const entry = this.getEntry(index);
|
||||||
const entry = $('.entry').eq(index);
|
if (entry !== null) {
|
||||||
|
entry.classList.add('entry-current');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.windowScroll.stop();
|
||||||
let target;
|
let target;
|
||||||
|
|
||||||
if (scroll) {
|
if (scroll) {
|
||||||
target = scroll;
|
target = scroll;
|
||||||
} else {
|
} else {
|
||||||
target = index === 0 ? 0 : entry.offset().top;
|
target = index === 0 || entry === null ? 0 : Display.getElementTop(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (smooth) {
|
if (smooth) {
|
||||||
container.animate({scrollTop: target}, 200);
|
this.windowScroll.animate(this.windowScroll.x, target, 200);
|
||||||
} else {
|
} else {
|
||||||
container.scrollTop(target);
|
this.windowScroll.toY(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.index = index;
|
this.index = index;
|
||||||
@ -446,7 +461,7 @@ class Display {
|
|||||||
|
|
||||||
async noteAdd(definition, mode) {
|
async noteAdd(definition, mode) {
|
||||||
try {
|
try {
|
||||||
this.spinner.show();
|
this.setSpinnerVisible(true);
|
||||||
|
|
||||||
const context = {};
|
const context = {};
|
||||||
if (this.noteUsesScreenshot()) {
|
if (this.noteUsesScreenshot()) {
|
||||||
@ -459,21 +474,28 @@ class Display {
|
|||||||
const noteId = await apiDefinitionAdd(definition, mode, context, this.optionsContext);
|
const noteId = await apiDefinitionAdd(definition, mode, context, this.optionsContext);
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
const index = this.definitions.indexOf(definition);
|
const index = this.definitions.indexOf(definition);
|
||||||
Display.adderButtonFind(index, mode).addClass('disabled');
|
const adderButton = this.adderButtonFind(index, mode);
|
||||||
Display.viewerButtonFind(index).removeClass('pending disabled').data('noteId', noteId);
|
if (adderButton !== null) {
|
||||||
|
adderButton.classList.add('disabled');
|
||||||
|
}
|
||||||
|
const viewerButton = this.viewerButtonFind(index);
|
||||||
|
if (viewerButton !== null) {
|
||||||
|
viewerButton.classList.remove('pending', 'disabled');
|
||||||
|
viewerButton.dataset.noteId = noteId;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw 'Note could note be added';
|
throw 'Note could note be added';
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
} finally {
|
} finally {
|
||||||
this.spinner.hide();
|
this.setSpinnerVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async audioPlay(definition, expressionIndex) {
|
async audioPlay(definition, expressionIndex) {
|
||||||
try {
|
try {
|
||||||
this.spinner.show();
|
this.setSpinnerVisible(true);
|
||||||
|
|
||||||
const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex];
|
const expression = expressionIndex === -1 ? definition : definition.expressions[expressionIndex];
|
||||||
let url = await apiAudioGetUrl(expression, this.options.general.audioSource);
|
let url = await apiAudioGetUrl(expression, this.options.general.audioSource);
|
||||||
@ -505,7 +527,7 @@ class Display {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
} finally {
|
} finally {
|
||||||
this.spinner.hide();
|
this.setSpinnerVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,6 +564,15 @@ class Display {
|
|||||||
return apiForward('popupSetVisible', {visible});
|
return apiForward('popupSetVisible', {visible});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSpinnerVisible(visible) {
|
||||||
|
this.spinner.style.display = visible ? 'block' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getEntry(index) {
|
||||||
|
const entries = this.container.querySelectorAll('.entry');
|
||||||
|
return index >= 0 && index < entries.length ? entries[index] : null;
|
||||||
|
}
|
||||||
|
|
||||||
static clozeBuild(sentence, source) {
|
static clozeBuild(sentence, source) {
|
||||||
const result = {
|
const result = {
|
||||||
sentence: sentence.text.trim()
|
sentence: sentence.text.trim()
|
||||||
@ -556,19 +587,51 @@ class Display {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static entryIndexFind(element) {
|
entryIndexFind(element) {
|
||||||
return $('.entry').index(element.closest('.entry'));
|
const entry = element.closest('.entry');
|
||||||
|
return entry !== null ? Display.indexOf(this.container.querySelectorAll('.entry'), entry) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static adderButtonFind(index, mode) {
|
adderButtonFind(index, mode) {
|
||||||
return $('.entry').eq(index).find(`.action-add-note[data-mode="${mode}"]`);
|
const entry = this.getEntry(index);
|
||||||
|
return entry !== null ? entry.querySelector(`.action-add-note[data-mode="${mode}"]`) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static viewerButtonFind(index) {
|
viewerButtonFind(index) {
|
||||||
return $('.entry').eq(index).find('.action-view-note');
|
const entry = this.getEntry(index);
|
||||||
|
return entry !== null ? entry.querySelector('.action-view-note') : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static delay(time) {
|
static delay(time) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, time));
|
return new Promise((resolve) => setTimeout(resolve, time));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static indexOf(nodeList, node) {
|
||||||
|
for (let i = 0, ii = nodeList.length; i < ii; ++i) {
|
||||||
|
if (nodeList[i] === node) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListeners(selector, type, listener, options) {
|
||||||
|
this.container.querySelectorAll(selector).forEach((node) => {
|
||||||
|
node.addEventListener(type, listener, options);
|
||||||
|
this.eventListeners.push([node, type, listener, options]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clearEventListeners() {
|
||||||
|
for (const [node, type, listener, options] of this.eventListeners) {
|
||||||
|
node.removeEventListener(type, listener, options);
|
||||||
|
}
|
||||||
|
this.eventListeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static getElementTop(element) {
|
||||||
|
const elementRect = element.getBoundingClientRect();
|
||||||
|
const documentRect = document.documentElement.getBoundingClientRect();
|
||||||
|
return elementRect.top - documentRect.top;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
100
ext/mixed/js/scroll.js
Normal file
100
ext/mixed/js/scroll.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 Alex Yatskov <alex@foosoft.net>
|
||||||
|
* Author: Alex Yatskov <alex@foosoft.net>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class WindowScroll {
|
||||||
|
constructor() {
|
||||||
|
this.animationRequestId = null;
|
||||||
|
this.animationStartTime = 0;
|
||||||
|
this.animationStartX = 0;
|
||||||
|
this.animationStartY = 0;
|
||||||
|
this.animationEndTime = 0;
|
||||||
|
this.animationEndX = 0;
|
||||||
|
this.animationEndY = 0;
|
||||||
|
this.requestAnimationFrameCallback = (t) => this.onAnimationFrame(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
toY(y) {
|
||||||
|
this.to(this.x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
toX(x) {
|
||||||
|
this.to(x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
to(x, y) {
|
||||||
|
this.stop();
|
||||||
|
window.scroll(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate(x, y, time) {
|
||||||
|
this.animationStartX = this.x;
|
||||||
|
this.animationStartY = this.y;
|
||||||
|
this.animationStartTime = window.performance.now();
|
||||||
|
this.animationEndX = x;
|
||||||
|
this.animationEndY = y;
|
||||||
|
this.animationEndTime = this.animationStartTime + time;
|
||||||
|
this.animationRequestId = window.requestAnimationFrame(this.requestAnimationFrameCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (this.animationRequestId === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cancelAnimationFrame(this.animationRequestId);
|
||||||
|
this.animationRequestId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onAnimationFrame(time) {
|
||||||
|
if (time >= this.animationEndTime) {
|
||||||
|
window.scroll(this.animationEndX, this.animationEndY);
|
||||||
|
this.animationRequestId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = WindowScroll.easeInOutCubic((time - this.animationStartTime) / (this.animationEndTime - this.animationStartTime));
|
||||||
|
window.scroll(
|
||||||
|
WindowScroll.lerp(this.animationStartX, this.animationEndX, t),
|
||||||
|
WindowScroll.lerp(this.animationStartY, this.animationEndY, t)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.animationRequestId = window.requestAnimationFrame(this.requestAnimationFrameCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
get x() {
|
||||||
|
return window.scrollX || window.pageXOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
get y() {
|
||||||
|
return window.scrollY || window.pageYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static easeInOutCubic(t) {
|
||||||
|
if (t < 0.5) {
|
||||||
|
return (4.0 * t * t * t);
|
||||||
|
} else {
|
||||||
|
t = 1.0 - t;
|
||||||
|
return 1.0 - (4.0 * t * t * t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lerp(start, end, percent) {
|
||||||
|
return (end - start) * percent + start;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user