/* Copyright: Ankitects Pty Ltd and contributors * License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html */ var currentField = null; var changeTimer = null; var dropTarget = null; var currentNoteId = null; String.prototype.format = function () { var args = arguments; return this.replace(/\{\d+\}/g, function (m) { return args[m.match(/\d+/)]; }); }; function setFGButton(col) { $("#forecolor")[0].style.backgroundColor = col; } function saveNow(keepFocus) { if (!currentField) { return; } clearChangeTimer(); if (keepFocus) { saveField("key"); } else { // triggers onBlur, which saves currentField.blur(); } } function triggerKeyTimer() { clearChangeTimer(); changeTimer = setTimeout(function () { updateButtonState(); saveField("key"); }, 600); } function onKey() { // esc clears focus, allowing dialog to close if (window.event.which === 27) { currentField.blur(); return; } // shift+tab goes to previous field if (navigator.platform === "MacIntel" && window.event.which === 9 && window.event.shiftKey) { window.event.preventDefault(); focusPrevious(); return; } triggerKeyTimer(); } function insertNewline() { if (!inPreEnvironment()) { setFormat("insertText", "\n"); return; } // in some cases inserting a newline will not show any changes, // as a trailing newline at the end of a block does not render // differently. so in such cases we note the height has not // changed and insert an extra newline. var r = window.getSelection().getRangeAt(0); if (!r.collapsed) { // delete any currently selected text first, making // sure the delete is undoable setFormat("delete"); } var oldHeight = currentField.clientHeight; setFormat("inserthtml", "\n"); if (currentField.clientHeight === oldHeight) { setFormat("inserthtml", "\n"); } } // is the cursor in an environment that respects whitespace? function inPreEnvironment() { var n = window.getSelection().anchorNode; if (n.nodeType === 3) { n = n.parentNode; } return window.getComputedStyle(n).whiteSpace.startsWith("pre"); } function onInput() { // make sure IME changes get saved triggerKeyTimer(); } function updateButtonState() { var buts = ["bold", "italic", "underline", "superscript", "subscript"]; for (var i = 0; i < buts.length; i++) { var name = buts[i]; if (document.queryCommandState(name)) { $("#" + name).addClass("highlighted"); } else { $("#" + name).removeClass("highlighted"); } } // fixme: forecolor // 'col': document.queryCommandValue("forecolor") } function toggleEditorButton(buttonid) { if ($(buttonid).hasClass("highlighted")) { $(buttonid).removeClass("highlighted"); } else { $(buttonid).addClass("highlighted"); } } function setFormat(cmd, arg, nosave) { document.execCommand(cmd, false, arg); if (!nosave) { saveField('key'); updateButtonState(); } } function clearChangeTimer() { if (changeTimer) { clearTimeout(changeTimer); changeTimer = null; } } function onFocus(elem) { if (currentField === elem) { // anki window refocused; current element unchanged return; } currentField = elem; pycmd("focus:" + currentFieldOrdinal()); enableButtons(); // don't adjust cursor on mouse clicks if (mouseDown) { return; } // do this twice so that there's no flicker on newer versions caretToEnd(); // scroll if bottom of element off the screen function pos(obj) { var cur = 0; do { cur += obj.offsetTop; } while (obj = obj.offsetParent); return cur; } var y = pos(elem); if ((window.pageYOffset + window.innerHeight) < (y + elem.offsetHeight) || window.pageYOffset > y) { window.scroll(0, y + elem.offsetHeight - window.innerHeight); } } function focusField(n) { if (n === null) { return; } $("#f" + n).focus(); } function focusPrevious() { if (!currentField) { return; } var previous = currentFieldOrdinal() - 1; if (previous >= 0) { focusField(previous); } } function onDragOver(elem) { var e = window.event; e.dataTransfer.dropEffect = "copy"; e.preventDefault(); // if we focus the target element immediately, the drag&drop turns into a // copy, so note it down for later instead dropTarget = elem; } function makeDropTargetCurrent() { dropTarget.focus(); // the focus event may not fire if the window is not active, so make sure // the current field is set currentField = dropTarget; } function onPaste(elem) { pycmd("paste"); window.event.preventDefault(); } function caretToEnd() { var r = document.createRange(); r.selectNodeContents(currentField); r.collapse(false); var s = document.getSelection(); s.removeAllRanges(); s.addRange(r); } function onBlur() { if (!currentField) { return; } if (document.activeElement === currentField) { // other widget or window focused; current field unchanged saveField("key"); } else { saveField("blur"); currentField = null; disableButtons(); } } function saveField(type) { clearChangeTimer(); if (!currentField) { // no field has been focused yet return; } // type is either 'blur' or 'key' pycmd(type + ":" + currentFieldOrdinal() + ":" + currentNoteId + ":" + currentField.innerHTML); } function currentFieldOrdinal() { return currentField.id.substring(1); } function wrappedExceptForWhitespace(text, front, back) { var match = text.match(/^(\s*)([^]*?)(\s*)$/); return match[1] + front + match[2] + back + match[3]; } function disableButtons() { $("button.linkb:not(.perm)").prop("disabled", true); } function enableButtons() { $("button.linkb").prop("disabled", false); } // disable the buttons if a field is not currently focused function maybeDisableButtons() { if (!document.activeElement || document.activeElement.className !== "field") { disableButtons(); } else { enableButtons(); } } function wrap(front, back) { if (currentField.dir === "rtl") { front = "" + front + ""; back = "" + back + ""; } var s = window.getSelection(); var r = s.getRangeAt(0); var content = r.cloneContents(); var span = document.createElement("span"); span.appendChild(content); var new_ = wrappedExceptForWhitespace(span.innerHTML, front, back); setFormat("inserthtml", new_); if (!span.innerHTML) { // run with an empty selection; move cursor back past postfix r = s.getRangeAt(0); r.setStart(r.startContainer, r.startOffset - back.length); r.collapse(true); s.removeAllRanges(); s.addRange(r); } } function onCutOrCopy() { pycmd("cutOrCopy"); return true; } function setFields(fields) { var txt = ""; for (var i = 0; i < fields.length; i++) { var n = fields[i][0]; var f = fields[i][1]; txt += "