Building custom Quill Editor theme - javascript

I am trying to put together a custom Quill theme for my application that's based on the default Snow theme.
My project is built in ES6, and so I have had to adapt the Snow theme accordingly (mostly replacing instances of this with a variable name (let tooltip = this).
The area I'm having trouble in is displaying the link tooltip. I am super close in getting it to work (I can add links) but I am now stuck in displaying the preview for a link when the user puts the cursor on a hyperlink.
I have managed to narrow it down to the following line:
let [link, offset] = tooltip.quill.scroll.descendant(LinkBlot, range.index);
Range.index is the correct index of the user's cursor. But link is always null and offset is always -1, no matter if the user's cursor position is on or off a hyperlink.
Why won't the scroll.descendant function correctly retrieve the link that the user is on?
Complete theme code:
import extend from 'extend';
import Emitter from 'quill/core/emitter';
import BaseTheme, { BaseTooltip } from 'quill/themes/base';
import LinkBlot from 'quill/formats/link';
import { Range } from 'quill/core/selection';
const TOOLBAR_CONFIG = [
[{ header: ['1', '2', '3', false] }],
['bold', 'italic', 'underline', 'link'],
[{ list: 'ordered' }, { list: 'bullet' }],
['clean']
];
class ZSSnowTooltip extends BaseTooltip {
constructor(quill, bounds) {
super(quill, bounds);
this.preview = this.root.querySelector('a.ql-preview');
}
listen() {
super.listen();
let tooltip = this;
// on action add
tooltip.root.querySelector('a.ql-action').addEventListener('click', function(event) {
if (tooltip.root.classList.contains('ql-editing')) {
tooltip.save();
} else {
tooltip.edit('link', tooltip.preview.textContent);
}
event.preventDefault();
});
// on action remove
tooltip.root.querySelector('a.ql-remove').addEventListener('click', function(event) {
if (tooltip.linkRange !== null) {
tooltip.restoreFocus();
tooltip.quill.formatText(tooltip.linkRange, 'link', false, Emitter.sources.USER);
delete tooltip.linkRange;
}
event.preventDefault();
tooltip.hide();
});
// on selection change
tooltip.quill.on(Emitter.events.SELECTION_CHANGE, function(range) {
// if no range is selected
if (range === null) {
return;
}
// if range length is 0, try to get a link, if there is a link, show preview box
if (range.length === 0) {
let [link, offset] = tooltip.quill.scroll.descendant(LinkBlot, range.index); // link IS ALWAYS NULL
if (link !== null) {
tooltip.linkRange = new Range(range.index - offset, link.length());
let preview = LinkBlot.formats(link.domNode);
tooltip.preview.textContent = preview;
tooltip.preview.setAttribute('href', preview);
tooltip.show();
tooltip.position(tooltip.quill.getBounds(tooltip.linkRange));
return;
}
} else {
delete tooltip.linkRange;
}
tooltip.hide();
});
}
show() {
super.show();
this.root.removeAttribute('data-mode');
}
}
class ZSSnowTheme extends BaseTheme {
constructor(quill, options) {
if (options.modules.toolbar !== null && options.modules.toolbar.container === null) {
options.modules.toolbar.container = TOOLBAR_CONFIG;
}
super(quill, options);
}
extendToolbar(toolbar) {
this.tooltip = new ZSSnowTooltip(this.quill, this.options.bounds); //eslint-disable-line
if (toolbar.container.querySelector('.ql-link')) {
this.quill.keyboard.addBinding({ key: 'K', shortKey: true }, function(range, context) { //eslint-disable-line
toolbar.handlers['link'].call(toolbar, !context.format.link); //eslint-disable-line
});
}
}
}
ZSSnowTheme.DEFAULTS = extend(true, {}, BaseTheme.DEFAULTS, {
modules: {
toolbar: {
handlers: {
link: function(value) { // eslint-disable-line
if (value) {
let range = this.quill.getSelection(),
tooltip,
preview;
if (range === null || range.length === 0) {
return;
}
preview = this.quill.getText(range);
if (/^\S+#\S+\.\S+$/.test(preview) && preview.indexOf('mailto:') !== 0) {
preview = `mailto:${preview}`;
}
tooltip = this.quill.theme.tooltip;
tooltip.edit('link', preview);
} else {
this.quill.format('link', false);
}
}
}
}
}
});
ZSSnowTooltip.TEMPLATE =
`<span class="title">
Bezoek link
</span>
<a class="ql-preview" target="_blank" href="about:blank"></a>
<input type="text" />
<span> - </span>
<a class="ql-action">
<i class="mdi"></i>
<span class="ql-action-label"></span>
</a>
<a class="ql-remove">
<i class="mdi mdi-delete"></i>
Verwijder
</a>`;
export default ZSSnowTheme;

Related

Referencing part of the url from html href element in javascript

I'm new to programming and have run into what is probably a beginner problem. I'm not quite sure how to phrase this question but I am basically trying to figure out how to reference part of a URL for a userscript I'm modifying.
Here's the website HTML:
<div class="header module">
<h2 class="heading">
Title
I'm trying to refer to the "12345" part. "/example/12345" would also work. Here's the relevant part of the code:
function selectFromBlurb(blurb) {
return {
reference: selectTextsIn(blurb, "header .heading a:first-child")
};
}
Currently the code is referring to the Title. I tried a bunch of things but as an amateur I couldn't figure it out. Thanks for any help!
Edit: Thank you everyone for the answers! Unfortunately I couldn't figure out how to make them work with the current code, so I'm posting the full userscript here.
The parts I added are the ones with "storyid". The purpose of this userscript is to be able to hide/filter out stories based on tags, title and such, I wanted to adapt it to also be able to filter based on the unique id of each story.
Here's the original: https://greasyfork.org/en/scripts/409956-ao3-blocker
// ==UserScript==
// #name AO3 Blocker Modified
// #description Fork of ao3 savior; blocks works based on certain conditions
// #author JacenBoy
// #namespace https://github.com/JacenBoy/ao3-blocker#readme
// #license Apache-2.0; http://www.apache.org/licenses/LICENSE-2.0
// #match http*://archiveofourown.org/*
// #version 2.2
// #require https://openuserjs.org/src/libs/sizzle/GM_config.js
// #require https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js
// #grant GM_getValue
// #grant GM_setValue
// #run-at document-end
// ==/UserScript==
(function () {
"use strict";
window.ao3Blocker = {};
// Initialize GM_config options
GM_config.init({
"id": "ao3Blocker",
"title": "AO3 Blocker",
"fields": {
"tagBlacklist": {
"label": "Tag Blacklist",
"type": "text",
"default": ""
},
"tagWhitelist": {
"label": "Tag Whitelist",
"type": "text",
"default": ""
},
"authorBlacklist": {
"label": "Author Blacklist",
"type": "text",
"default": ""
},
"titleBlacklist": {
"label": "Title Blacklist",
"type": "text",
"default": ""
},
"summaryBlacklist": {
"label": "Summary Blacklist",
"type": "text",
"default": ""
},
"storyidBlacklist": {
"label": "ID Blacklist",
"type": "textarea",
"default": ""
},
"showReasons": {
"label": "Show Block Reason",
"type": "checkbox",
"default": true
},
"showPlaceholders": {
"label": "Show Work Placeholder",
"type": "checkbox",
"default": true
},
"alertOnVisit": {
"label": "Alert When Opening Blocked Work",
"type": "checkbox",
"default": false
}
},
"events": {
"save": () => {
window.ao3Blocker.updated = true;
alert("Your changes have been saved.");
},
"close": () => {
if (window.ao3Blocker.updated) location.reload();
}
},
"css": ".config_var {display: grid; grid-template-columns: repeat(2, 0.7fr);}"
});
// Define the custom styles for the script
const STYLE = "\n html body .ao3-blocker-hidden {\n display: none;\n }\n \n .ao3-blocker-cut {\n display: none;\n }\n \n .ao3-blocker-cut::after {\n clear: both;\n content: '';\n display: block;\n }\n \n .ao3-blocker-reason {\n margin-left: 5px;\n }\n \n .ao3-blocker-hide-reasons .ao3-blocker-reason {\n display: none;\n }\n \n .ao3-blocker-unhide .ao3-blocker-cut {\n display: block;\n }\n \n .ao3-blocker-fold {\n align-items: center;\n display: flex;\n justify-content: flex-start;\n }\n \n .ao3-blocker-unhide .ao3-blocker-fold {\n border-bottom: 1px dashed;\n margin-bottom: 15px;\n padding-bottom: 5px;\n }\n \n button.ao3-blocker-toggle {\n margin-left: auto;\n }\n";
// addMenu() - Add a custom menu to the AO3 menu bar to control our configuration options
function addMenu() {
// Define our custom menu and add it to the AO3 menu bar
const headerMenu = $("ul.primary.navigation.actions");
const blockerMenu = $("<li class=\"dropdown\"></li>").html("<a>AO3 Blocker Modified</a>");
headerMenu.find("li.search").before(blockerMenu);
const dropMenu = $("<ul class=\"menu dropdown-menu\"></ul>");
blockerMenu.append(dropMenu);
// Add the "Toggle Block Reason" option to the menu
const reasonButton = $("<li></li>").html(`<a>${GM_config.get("showReasons") ? "Hide" : "Show"} Block Reason</a>`);
reasonButton.on("click", () => {
if (GM_config.get("showReasons")) {
GM_config.set("showReasons", false);
} else {
GM_config.set("showReasons", true);
}
GM_config.save();
reasonButton.html(`<a>${GM_config.get("showReasons") ? "Hide" : "Show"} Block Reason</a>`);
});
dropMenu.append(reasonButton);
// Add the "Toggle Work Placeholder" option to the menu
const placeholderButton = $("<li></li>").html(`<a>${GM_config.get("showPlaceholders") ? "Hide" : "Show"} Work Placeholder</a>`);
placeholderButton.on("click", () => {
if (GM_config.get("showPlaceholders")) {
GM_config.set("showPlaceholders", false);
} else {
GM_config.set("showPlaceholders", true);
}
GM_config.save();
placeholderButton.html(`<a>${GM_config.get("showPlaceholders") ? "Hide" : "Show"} Work Placeholder</a>`);
});
dropMenu.append(placeholderButton);
// Add the "Toggle Block Alerts" option to the menu
const alertButton = $("<li></li>").html(`<a>${GM_config.get("alertOnVisit") ? "Don't Show" : "Show"} Blocked Work Alerts</a>`);
alertButton.on("click", () => {
if (GM_config.get("alertOnVisit")) {
GM_config.set("alertOnVisit", false);
} else {
GM_config.set("alertOnVisit", true);
}
GM_config.save();
alertButton.html(`<a>${GM_config.get("alertOnVisit") ? "Don't Show" : "Show"} Blocked Work Alerts</a>`);
});
dropMenu.append(alertButton);
// Add an option to show the config dialog
const settingsButton = $("<li></li>").html("<a>All Settings</a>");
settingsButton.on("click", () => {GM_config.open();});
dropMenu.append(settingsButton);
}
// Define the CSS namespace. All CSS classes are prefixed with this.
const CSS_NAMESPACE = "ao3-blocker";
// addStyle() - Apply the custom stylesheet to AO3
function addStyle() {
const style = $(`<style class="${CSS_NAMESPACE}"></style>`).html(STYLE);
$("head").append(style);
}
// getCut(work) - Move standard AO3 work information (tags, summary, etc.) to a custom element for blocked works. This will be hidden by default on blocked works but can be shown if thre user chooses.
function getCut(work) {
const cut = $(`<div class="${CSS_NAMESPACE}-cut"></div>`);
$.makeArray(work.children()).forEach((child) => {
return cut.append(child);
});
return cut;
}
// getFold(reason) - Create the work placeholder for blocked works. Optionally, this will show why the work was blocked and give the user the option to unhide it.
function getFold(reason) {
const fold = $(`<div class="${CSS_NAMESPACE}-fold"></div>`);
const note = $(`<span class="${CSS_NAMESPACE}-note"</span>`).text("This work is hidden! ");
fold.html(note);
fold.append(getReasonSpan(reason));
fold.append(getToggleButton());
return fold;
}
// getToggleButton() - Create a button that will show or hide the "cut" on blocked works.
function getToggleButton() {
const button = $(`<button class="${CSS_NAMESPACE}-toggle"></button>`).text("Unhide");
const unhideClassFragment = `${CSS_NAMESPACE}-unhide`;
button.on("click", (event) => {
const work = $(event.target).closest(`.${CSS_NAMESPACE}-work`);
if (work.hasClass(unhideClassFragment)) {
work.removeClass(unhideClassFragment);
work.find(`.${CSS_NAMESPACE}-note`).text("This work is hidden.");
$(event.target).text("Unhide");
} else {
work.addClass(unhideClassFragment);
work.find(`.${CSS_NAMESPACE}-note`).text("ℹ️ This work was hidden.");
$(event.target).text("Hide");
}
});
return button;
}
// getReasonSpan(reason) - Create the element that holds the block reason information on blocked works.
function getReasonSpan(reason) {
const span = $(`<span class="${CSS_NAMESPACE}-reason"></span>`);
let text = undefined;
if (reason.tag) {
text = `tags include <strong>${reason.tag}</strong>`;
} else if (reason.author) {
text = `authors include <strong>${reason.author}</strong>`;
} else if (reason.title) {
text = `title is <strong>${reason.title}</strong>`;
} else if (reason.summary) {
text = `summary includes <strong>${reason.summary}</strong>`;
} else if (reason.storyid) {
text = `storyid includes <strong>${reason.storyid}</strong>`;
}
if (text) {
span.html(`(Reason: ${text}.)`);
}
return span;
}
// blockWork(work, reason, config) - Replace the standard AO3 work information with the placeholder "fold", and place the "cut" below it, hidden.
function blockWork(work, reason, config) {
if (!reason) return;
if (config.showPlaceholders) {
const fold = getFold(reason);
const cut = getCut(work);
work.addClass(`${CSS_NAMESPACE}-work`);
work.html(fold);
work.append(cut);
if (!config.showReasons) {
work.addClass(`${CSS_NAMESPACE}-hide-reasons`);
}
} else {
work.addClass(`${CSS_NAMESPACE}-hidden`);
}
}
function matchTermsWithWildCard(term0, pattern0) {
const term = term0.toLowerCase();
const pattern = pattern0.toLowerCase();
if (term === pattern) return true;
if (pattern.indexOf("*") === -1) return false;
const lastMatchedIndex = pattern.split("*").filter(Boolean).reduce((prevIndex, chunk) => {
const matchedIndex = term.indexOf(chunk);
return prevIndex >= 0 && prevIndex <= matchedIndex ? matchedIndex : -1;
}, 0);
return lastMatchedIndex >= 0;
}
function isTagWhitelisted(tags, whitelist) {
const whitelistLookup = whitelist.reduce((lookup, tag) => {
lookup[tag.toLowerCase()] = true;
return lookup;
}, {});
return tags.some((tag) => {
return !!whitelistLookup[tag.toLowerCase()];
});
}
function findBlacklistedItem(list, blacklist, comparator) {
let matchingEntry = void 0;
list.some((item) => {
blacklist.some((entry) => {
const matched = comparator(item.toLowerCase(), entry.toLowerCase());
if (matched) matchingEntry = entry;
return matched;
});
});
return matchingEntry;
}
function equals(a, b) {
return a === b;
}
function contains(a, b) {
return a.indexOf(b) !== -1;
}
function getBlockReason(_ref, _ref2) {
const _ref$authors = _ref.authors,
authors = _ref$authors === undefined ? [] : _ref$authors,
_ref$title = _ref.title,
title = _ref$title === undefined ? "" : _ref$title,
_ref$tags = _ref.tags,
tags = _ref$tags === undefined ? [] : _ref$tags,
_ref$summary = _ref.summary,
summary = _ref$summary === undefined ? "" : _ref$summary,
_ref$storyid = _ref.storyid,
storyid = _ref$storyid === undefined ? [] : _ref$storyid;
const _ref2$authorBlacklist = _ref2.authorBlacklist,
authorBlacklist = _ref2$authorBlacklist === undefined ? [] : _ref2$authorBlacklist,
_ref2$titleBlacklist = _ref2.titleBlacklist,
titleBlacklist = _ref2$titleBlacklist === undefined ? [] : _ref2$titleBlacklist,
_ref2$tagBlacklist = _ref2.tagBlacklist,
tagBlacklist = _ref2$tagBlacklist === undefined ? [] : _ref2$tagBlacklist,
_ref2$tagWhitelist = _ref2.tagWhitelist,
tagWhitelist = _ref2$tagWhitelist === undefined ? [] : _ref2$tagWhitelist,
_ref2$summaryBlacklis = _ref2.summaryBlacklist,
summaryBlacklist = _ref2$summaryBlacklis === undefined ? [] : _ref2$summaryBlacklis,
_ref2$storyidBlacklist = _ref2.storyidBlacklist,
storyidBlacklist = _ref2$storyidBlacklist === undefined ? [] : _ref2$storyidBlacklist;
if (isTagWhitelisted(tags, tagWhitelist)) {
return null;
}
const blockedTag = findBlacklistedItem(tags, tagBlacklist, matchTermsWithWildCard);
if (blockedTag) {
return { tag: blockedTag };
}
const author = findBlacklistedItem(authors, authorBlacklist, equals);
if (author) {
return { author: author };
}
const blockedTitle = findBlacklistedItem([title.toLowerCase()], titleBlacklist, matchTermsWithWildCard);
if (blockedTitle) {
return { title: blockedTitle };
}
const summaryTerm = findBlacklistedItem([summary.toLowerCase()], summaryBlacklist, contains);
if (summaryTerm) {
return { summary: summaryTerm };
}
const blockedStoryid = findBlacklistedItem(storyid, storyidBlacklist, contains);
if (blockedStoryid) {
return { storyid: blockedStoryid };
}
return null;
}
const _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
function getText(element) {
return $(element).text().replace(/^\s*|\s*$/g, "");
}
function selectTextsIn(root, selector) {
return $.makeArray($(root).find(selector)).map(getText);
}
function selectFromWork(container) {
return _extends({}, selectFromBlurb(container), {
title: selectTextsIn(container, ".title")[0],
summary: selectTextsIn(container, ".summary .userstuff")[0]
});
}
function selectFromBlurb(blurb) {
return {
authors: selectTextsIn(blurb, "a[rel=author]"),
tags: [].concat(selectTextsIn(blurb, "a.tag"), selectTextsIn(blurb, ".required-tags .text")),
title: selectTextsIn(blurb, ".header .heading a:first-child")[0],
summary: selectTextsIn(blurb, "blockquote.summary")[0],
storyid: selectTextsIn(blurb, ".header .heading a:first-child")
};
}
// checkWorks() - Scan all works on the page and block them if they match one of the conditions set by the user.
function checkWorks () {
const debugMode = true; // Set to true to enable extra logging
// Load our config information into a convenient JSON file.
const config = {
"showReasons": GM_config.get("showReasons"),
"showPlaceholders": GM_config.get("showPlaceholders"),
"alertOnVisit": GM_config.get("alertOnVisit"),
"authorBlacklist": GM_config.get("authorBlacklist").split(/,(?:\s)?/g).map(i=>i.trim()),
"titleBlacklist": GM_config.get("titleBlacklist").split(/,(?:\s)?/g).map(i=>i.trim()),
"tagBlacklist": GM_config.get("tagBlacklist").split(/,(?:\s)?/g).map(i=>i.trim()),
"tagWhitelist": GM_config.get("tagWhitelist").split(/,(?:\s)?/g).map(i=>i.trim()),
"summaryBlacklist": GM_config.get("summaryBlacklist").split(/,(?:\s)?/g).map(i=>i.trim()),
"storyidBlacklist": GM_config.get("storyidBlacklist").split(/,(?:\s)?/g).map(i=>i.trim())
};
// If this is a work page, save the element for future use.
const workContainer = $("#main.works-show") || $("#main.chapters-show");
let blocked = 0;
let total = 0;
if (debugMode) {
console.groupCollapsed("AO3 BLOCKER");
if (!config) {
console.warn("Exiting due to missing config.");
return;
}
}
// Loop through all works on the search page and check if they match one of the conditions.
$.makeArray($("li.blurb")).forEach((blurb) => {
blurb = $(blurb);
const blockables = selectFromBlurb(blurb);
const reason = getBlockReason(blockables, config);
total++;
if (reason) {
blockWork(blurb, reason, config);
blocked++;
if (debugMode) {
console.groupCollapsed(`- blocked ${blurb.attr("id")}`);
console.log(blurb.html(), reason);
console.groupEnd();
}
} else if (debugMode) {
console.groupCollapsed(` skipped ${blurb.attr("id")}`);
console.log(blurb.html());
console.groupEnd();
}
});
// If this is a work page, the work was navigated to from another site (i.e. an external link), and the user had block alerts enabled, show a warning.
if (config.alertOnVisit && workContainer && document.referrer.indexOf("//archiveofourown.org") === -1) {
const blockables = selectFromWork(workContainer);
const reason = getBlockReason(blockables, config);
if (reason) {
blocked++;
blockWork(workContainer, reason, config);
}
}
if (debugMode) {
console.log(`Blocked ${blocked} out of ${total} works`);
console.groupEnd();
}
}
addMenu();
addStyle();
setTimeout(checkWorks, 10);
}());
You could do
reference: blurb.querySelector(".header .heading a:first-child").href.split('/').at(-1)
I'm not sure I understood your requirement but I guess you want to return "12345" as a reference.
If so, please try this.
function selectFromBlurb(blurb) {
var refer = blurb.substring(0, blurb.lastIndexOf("/") + 1);
return {
reference: selectTextsIn(refer, "header .heading a:first-child")
};
console.log(reference)
}

Asprise / scannerjs.javascript-scanner-web-twain-wia-browsers-scanner.js connection problem reactjs

i try to use scanner.js but the same error keeps on showing
i have a problem with websocket connection when i try using scanner.js from Asprise.
i installed scanner.js using npm i scanner.js and imported it in my react code. i even added the following script script in my html page and it does notwork.
import React from "react";
import scanner from 'scanner-js';
let scanRequest = {
"use_asprise_dialog": true, // Whether to use Asprise Scanning Dialog
"show_scanner_ui": true, // Whether scanner UI should be shown
"twain_cap_setting" : {
"ICAP_PIXELTYPE" : "TWPT_RGB", // Color
"ICAP_XRESOLUTION" : "100", // DPI: 100
"ICAP_YRESOLUTION" : "100",
"ICAP_SUPPORTEDSIZES" : "TWSS_USLETTER" // Paper size: TWSS_USLETTER, TWSS_A4, ...
},
"output_settings": [{
"type": "return-base64",
"format": "pdf",
"thumbnail_height": 200,
}]
};
/** Triggers the scan */
const scan = () => {
scanner.scan(displayImagesOnPage, scanRequest);
}
/** Processes the scan result */
const displayImagesOnPage = (successful, mesg, response) => {
if (!successful) { // On error
console.error('Failed: ' + mesg);
return;
}
if (successful && mesg != null && mesg.toLowerCase().indexOf('user cancel') >= 0) { // User cancelled.
console.info('User cancelled');
return;
}
let scannedImages = scanner.getScannedImages(response, true, false); // returns an array of ScannedImage
for (let i = 0;
(scannedImages instanceof Array) && i < scannedImages.length; i++) {
let scannedImage = scannedImages[i];
let elementImg = scanner.createDomElementFromModel({
'name': 'img',
'attributes': {
'class': 'scanned',
'src': scannedImage.src
}
});
(document.getElementById('images') ? document.getElementById('images') : document.body).appendChild(elementImg);
}
}
export const Scanner = () => {
return (
<div>
<h2>Scanner.js TEST</h2>
<button type="button" onClick={()=>scan()}>
Scan
</button>
<div id="images"></div>
</div>
);
};
export default Scanner;

How do I call a modal from a custom quill editor button

I have a custom button (embed) in my quill editor that uses the prompt to call the windows popup
Now, I want to change the windows popup to a modal on the click of the embed button.
I honestly don't know how to go about this as this is my first time using a quill.
I'm using a quill extension file to create the custom button(embed)
I'm perplexed on were to create the modal and also how to call the modal when the quill custom button is clicked.
This is how I call the quill from my view
<div class="form-group col-xs-12">
<label class="control-label">
Description
</label>
<div id="createSchool_Description_div" style="height:300px;"></div>
</div>
<script>
$(document).ready(function () {
<text>
prepareQuill('#createSchool_Description_div');
</text>
}
My quill extension file
var quills = {};
function addQuillExtension(__quill) {
if (__quill === undefined || __quill === null) {
throw new this.DOMException("__quill editor not defined");
}
else {
__quill.root.addEventListener("paste", function (e) {
retrieveImageFromClipboardAsBase64(e, function (imageDataBase64) {
// If there's an image, open it in the browser as a new window :)
if (imageDataBase64) {
// data:image/png;base64,iVBORw0KGgoAAAAN......
let content = __quill.getContents();
__quill.clipboard.dangerouslyPasteHTML(content.length, "<img src=" + imageDataBase64 + ">");
//window.open(imageDataBase64);
}
});
}, false);
let embedbutton = __quill.container.previousSibling.querySelector('.ql-embed');
embedbutton.setAttribute('title', 'Embed video/audio');
embedbutton.onclick = function () {
let url = prompt("Enter youtube URL");
let spliturl = url.split('=');
let suffix = spliturl[1];
let embedurl = `https://www.youtube.com/embed/${suffix}?rel=0`;
//debugger;
let embedhtml = `<iframe class="youtubeembed" src="${embedurl}" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
let content = __quill.getContents();
__quill.clipboard.dangerouslyPasteHTML(content.length, embedhtml);
};
}
function retrieveImageFromClipboardAsBase64(pasteEvent, callback, imageFormat) {
if (pasteEvent.clipboardData === false) {
if (typeof callback === "function") {
callback(undefined);
}
}
var items = pasteEvent.clipboardData.items;
if (items === undefined) {
if (typeof callback === "function") {
callback(undefined);
}
}
for (var i = 0; i < items.length; i++) {
// Skip content if not image
if (items[i].type.indexOf("image") === -1) continue;
// Retrieve image on clipboard as blob
var blob = items[i].getAsFile();
// Create an abstract canvas and get context
var mycanvas = document.createElement("canvas");
var ctx = mycanvas.getContext('2d');
// Create an image
var img = new Image();
// Once the image loads, render the img on the canvas
img.onload = function () {
// Update dimensions of the canvas with the dimensions of the image
mycanvas.width = this.width;
mycanvas.height = this.height;
// Draw the image
ctx.drawImage(img, 0, 0);
// Execute callback with the base64 URI of the image
if (typeof callback === "function") {
callback(mycanvas.toDataURL(imageFormat || "image/png"));
}
};
// Crossbrowser support for URL
var URLObj = window.URL || window.webkitURL;
// Creates a DOMString containing a URL representing the object given in the parameter
// namely the original Blob
img.src = URLObj.createObjectURL(blob);
}
}
}
function prepareQuill(selector, html) {
let quill;
var toolbarOptions = [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
//[{ 'header': 1 }, { 'header': 2 }], // custom button values
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'script': 'sub' }, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1' }, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
//[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['link', 'image', 'embed'],
['clean'], // remove formatting button
['save']
];
quill = new Quill(selector, {
modules: {
toolbar: toolbarOptions
},
theme: 'snow'
});
addQuillExtension();
if (html) {
quill.clipboard.dangerouslyPasteHTML(html);
}
quills[selector] = quill;
return quill;
//quill.enable();
}
function getEditorHtml(id) {
let holder = document.getElementById(id).querySelector('.ql-editor');
let clone = holder.cloneNode(true);
return clone.innerHTML;
}
function getQuillEditorHtml(id) {
let html = getEditorHtml(id);
return html;
}
function setQuillEditorHtml(selector, html) {
let quill = quills[selector];
if (quill) {
quill.clipboard.dangerouslyPasteHTML(html);
}
else {
throw `quill editor for '${selector}' not found`;
}
}
I do not think you need to write a custom button for this as quill already has a video embed button.
Just add 'video' in your toolbar options when creating the quill example below :
['bold', 'italic', 'underline', 'strike', '**video**']]

addCssFile() no longer functions in our app. Is not called and does nothing

Our app is no longer able to use the tns core function addCssFile(). The function is not called at all.
I tried altering one of the addCssFile() calls by calling a made up filename. It does not trigger any errors and the code seems to skip right over it. We made no changes to this script but suddenly this function does absolutely nothing.
I added the import addCssFile portion to the code just now, and it says "module has no exported member"
import * as applicationSettings from 'tns-core-modules/application-settings';
import { localize } from 'nativescript-localize';
import { ToastDuration, Toasty } from 'nativescript-toasty';
import { fromObject } from 'tns-core-modules/data/observable';
import { Page, topmost } from 'tns-core-modules/ui/frame';
import {addCssFile} from 'tns-core-modules/ui/core/view';
const source = fromObject({});
source.set('settingsItems', [
{ name: localize('ColorSchemeMenu.ob'), enabled: true },
{ name: localize('ColorSchemeMenu.bw'), enabled: true },
{ name: localize('ColorSchemeMenu.rw'), enabled: true },
{ name: localize('ColorSchemeMenu.py'), enabled: true }
]);
export function onNavigatingTo(args: any) {
const page = args.object;
page.bindingContext = source;
}
export function settingsItemTapped(args: any) {
const button = args.object;
const homePage = button.page.navigationContext.homePage as Page;
const currentTheme = applicationSettings.getString('theme', 'blue');
if (button.text === localize('ColorSchemeMenu.ob')) {
//Orange
homePage.addCssFile('themes/orange.css');
applicationSettings.setString('theme', 'orange');
} else if (button.text === localize('ColorSchemeMenu.bw')) {
// Blue
homePage.addCssFile('themes/blue.css');
applicationSettings.setString('theme', 'blue');
} else if (button.text === localize('ColorSchemeMenu.rw')) {
// Red
homePage.addCssFile('themes/red.css');
applicationSettings.setString('theme', 'red');
} else if (button.text === localize('ColorSchemeMenu.py')) {
// Purple
//homePage.addCssFile('themes/purple.css');
homePage.addCssFile('themes/needstofail.css');
applicationSettings.setString('theme', 'purple');
}
console.log('\n\ncolor scheme has been set to: ', applicationSettings.getString('theme'), '\n\n');
new Toasty(
`${localize('ColorSchemeMenu.changedMsg')} ${button.text}`,
ToastDuration.SHORT
).show();
//transition back to settings page after selecting color scheme
topmost().navigate({
moduleName: 'home/home-page',
animated: true,
transition: {
name: 'slideRight'
}
});
}
export function homeBtnTapped(args: any) {
topmost().navigate({
moduleName: 'home/home-page',
animated: true,
transition: {
name: 'slideRight'
}
});
}
Clicking any of the buttons on the settings page should add the css info to the home page and alter the object colors. The app recognizes that the buttons are pressed, but no css changes occur. Manually changing the css file of the home page does alter the appearance of the page.
You can use application.addCss as below by using file-system.
import * as application from 'tns-core-modules/application/application';
const fs = require('tns-core-modules/file-system');
if (button.text === localize('ColorSchemeMenu.ob')) {
//Orange
pageCss = fs.path.join(fs.knownFolders.currentApp().path, 'themes/orange.css');
fs.File.fromPath(pageCss).readText().then((res) => {
application.addCss(res);
});
applicationSettings.setString('theme', 'orange');
} else if (button.text === localize('ColorSchemeMenu.bw')) {
// Blue
applicationSettings.setString('theme', 'blue');
} else if (button.text === localize('ColorSchemeMenu.rw')) {
// Red
applicationSettings.setString('theme', 'red');
} else if (button.text === localize('ColorSchemeMenu.py')) {
applicationSettings.setString('theme', 'purple');
}

Vue JS unable to display data

I'm trying to display data in my vue component and I have an array that has object on it. If I try to use
<div class="bar">
{{playersStats[0]}}
</div>
it displays
{ "GP": 13, "GS": 6, "MPG": 20.74, "PPG": 12.85, "FGM": 4.46, "FGA": 9.77, "FGP": 0.46 }
but if I try using
<div class="bar">
<span v-if="playersStats[0]">{{playersStats[0].GS}}</span>
</div>
EDITED Javascript:
export default {
data: function(){
return {
showPlayersSelection: true,
selectedPlayers: [],
playersStats: []
}
},
methods: {
selectPlayers(player) {
if(this.selectedPlayers.length < 2){
this.selectedPlayers.push(player);
if(this.selectedPlayers.length == 2){
this.getPlayerStats(this.selectedPlayers[0][0], this.selectedPlayers[1][0]);
this.showPlayersSelection = false;
}
}
return false;
},
getPlayerStats(player1, player2) {
let self = this;
axios.get(config.API.URL + config.API.STATS, {
params: {
'player1Id': player1,
'player2Id': player2
}
})
.then( (response) => {
if(response.status == 200 && typeof(response.data) == 'object'){
self.playersStats = [];
self.playersStats.push(response.data[0][0]);
self.playersStats.push(response.data[1][0]);
}
});
},
}
}
It displays nothing even in DOM. How can I be able to display the data?
<span v-if="playersStats[0]">{{playersStats[0]["GS"]}}</span>
maybe u need this?

Categories

Resources