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

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;

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)
}

Javascript How to get Nested Objects Correctly

I am building an Electron app that gets a certain kind of json file from the user and logs all the data from it. But I am getting an error about getting undefined from the quantity:
Json File:
{
"tiers": [
{
"trades": [
{
"wants": [
{
"item": "minecraft:string",
"quantity": {
"min": 15,
"max": 20
}
}
],
"gives": [
{
"item": "minecraft:emerald"
}
]
},
{
"wants": [
{
"item": "minecraft:emerald"
}
],
"gives": [
{
"item": "minecraft:arrow",
"quantity": {
"min": 8,
"max": 12
}
}
]
}
]
},
{
"trades": [
{
"wants": [
{
"item": "minecraft:gravel",
"quantity": 10
},
{
"item": "minecraft:emerald",
"quantity": 1
}
],
"gives": [
{
"item": "minecraft:flint",
"quantity": {
"min": 6,
"max": 10
}
}
]
},
{
"wants": [
{
"item": "minecraft:emerald",
"quantity": {
"min": 2,
"max": 3
}
}
],
"gives": [
{
"item": "minecraft:bow"
}
]
}
]
}
]
}
And this is the main.js:
const { app, BrowserWindow, ipcMain, dialog, ipcRenderer, globalShortcut } = require('electron');
const { autoUpdater } = require('electron-updater');
const path = require('path');
const Store = require('./classes/Store.js');
const { electron } = require('process');
const { setTimeout } = require('timers');
// import Vue from 'vue';
// import Vuetify from 'vuetify';
// import "vuetify/dist/vuetify.min.css";
// Vue.use(Vuetify);
let win;
let loadingScreen;
const store = new Store({
configName: 'settings',
defaults: {
windowBounds: {
width: 800,
height: 600,
x: 0,
y: 0,
},
isMaximized: false,
fullscreen: false
}
});
function createLoadingScreen() {
loadingScreen = new BrowserWindow(Object.assign({
width: 200,
height: 220,
frame: false,
transparent: true,
icon: 'build/icons/icon.png',
webPreferences: {
worldSafeExecuteJavaScript: true
}
}));
loadingScreen.setResizable(false);
loadingScreen.loadURL('file://' + __dirname + '/extraWindows/loadingScreen/loading.html');
loadingScreen.setOverlayIcon('build/icons/icon.png', "Route");
loadingScreen.on('closed', () => loadingScreen = null);
loadingScreen.webContents.on('did-finish-load', () => {
loadingScreen.show();
});
}
function createWindow() {
let width = store.get('windowBounds.width');
let height = store.get('windowBounds.height');
let x = store.get('windowBounds.x');
let y = store.get('windowBounds.y');
let isMaximized = store.get('isMaximized');
win = new BrowserWindow({
width,
height,
x,
y,
icon: 'build/icons/icon.png',
frame: false,
titleBarStyle: "hidden",
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
webSafeExecuteJavaScript: true
},
show: false
})
win.setOverlayIcon('build/icons/icon.png', "Route");
if(isMaximized == true) {
win.maximize();
}
win.loadFile('index.html');
win.on('closed', function() {
win = null;
settingScreen = null;
});
win.webContents.on('did-finish-load', () => {
if(loadingScreen) {
loadingScreen.close();
win.show();
}
})
win.on('resize', () => {
store.set('windowBounds', win.getBounds());
});
win.on('move', () => {
store.set('windowBounds', win.getBounds());
})
win.on('maximize', () => {
store.set('isMaximized', true);
})
win.on('unmaximize', () => {
store.set('isMaximized', false);
})
win.on('show', () => {
win.setFullScreen(store.get('fullscreen'));
})
win.once('ready-to-show', () => {
autoUpdater.checkForUpdatesAndNotify();
})
}
function createSettings() {
settingScreen = new BrowserWindow({
width: 600,
height: 800,
frame: false,
parent: win,
modal: true,
titleBarStyle: "hidden",
webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
worldSafeExecuteJavaScript: true
},
show: false
})
settingScreen.loadFile('extraWindows/settingsScreen/settings.html');
settingScreen.on('close', (event) => {
event.preventDefault();
settingScreen.hide();
})
}
app.on('ready', () => {
createLoadingScreen();
setTimeout(() => {
createWindow();
createSettings();
}, 5000); // Set to 5000
});
app.on('window-all-closed', () => {
if(process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if(BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
})
app.whenReady().then(() => {
globalShortcut.register('CmdOrCtrl+O', () => {
win.webContents.send('openFile');
})
globalShortcut.register('CmdOrCtrl+D', () => {
win.webContents.send('closeFile');
})
})
// These are all of the ipc functions needed for Route to work
ipcMain.handle('viewSettings', (event, arg) => {
if(arg === "true") {
settingScreen.show();
} else if(arg === "false") {
settingScreen.hide();
}
});
ipcMain.handle('changeSettings', (event, arg) => {
if(arg === "fullScreen") {
if(win.isFullScreen() == false) {
win.setFullScreen(true);
store.set('fullscreen', true);
} else {
win.setFullScreen(false);
store.set('fullscreen', false);
}
// Fill in the data for fullscreen
} else if(arg === 'themechooser') {
// Fill in the data for themes
}
});
ipcMain.on('appVersion', (event) => {
event.sender.send('appVersion', { version: app.getVersion() });
});
ipcMain.on('restartApp', () => {
autoUpdater.quitAndInstall();
})
autoUpdater.on('updateAvailable', () => {
win.webContents.send('updateAvailable');
});
autoUpdater.on('updateDownloaded', () => {
win.webContents.send('updateDownloaded');
})
And this is the renderer.js:
const { app, ipcRenderer } = require('electron');
const Store = require('./classes/Store.js');
const remote = require('electron').remote;
const dialog = require('electron').remote.dialog;
const fs = require('fs');
const { S_IFDIR } = require('constants');
const { electron } = require('process');
const { version } = require('os');
let currentWindow = remote.getCurrentWindow();
let $ = function(selector) {
return document.querySelector(selector);
}
let projectArray = [];
// There can only be 5 projects max. Might add more.
let projectCount = 0;
document.querySelector('#tradeSetup').addEventListener('click', () => {
//Create a new setup.
alert("This works!");
});
let fileOpenerOptions = {
title: "Open Trade File",
defaultPath: "C:\\",
buttonLabel: "Start Coding",
properties: [ 'openFile' ],
filters: [
{ name: 'Json', extensions: ['json'] },
{ name: 'All FIles', extensions: ['*'] }
]
}
//Top bar buttons
function openFile() {
dialog.showOpenDialog(currentWindow, fileOpenerOptions).then(fileNames => {
if(fileNames == undefined || fileNames == null) {
console.log("No file selected.");
return;
} else {
let projectName = path.basename(fileNames.filePaths[0], path.extname(fileNames.filePaths[0]));
let rawFileData = fs.readFileSync(fileNames.filePaths[0]);
let fileData = JSON.parse(rawFileData);
console.log(fileData);
if(projectCount < 5) {
projectCount++;
createProject(projectName, fileData);
// Run code
} else if (projectCount == 5) {
alert('Support for more projects is not available yet!\nPlease wait until a later update to have more than 5 projects.');
}
}
});
}
function openSettings() {
ipcRenderer.invoke('viewSettings', "true");
}
function closeFile() {
if(document.querySelector('.activeProjectButton') == null) {
console.log("No project to close.");
} else {
let contine = confirm("Is this project the furthest project to the right? If not, please don't close it.\nThis is a bug that is being worked on.");
if(contine == true) {
document.querySelector('.activeProjectButton').remove();
document.querySelector('.activeWorkspace').remove();
projectCount--;
document.querySelectorAll('.inactiveProjectWorkspace').j--;
}
// Add a fun little secret for users
}
}
document.querySelector('#openFile').addEventListener('click', () => {
openFile();
});
document.querySelector('#openSettings').addEventListener('click', () => {
openSettings();
});
document.querySelector('#closeFile').addEventListener('click', () => {
closeFile();
})
// Create and switch projects
function createProject(name, projectData) {
// This is a beta feature for now. It is not finished
// Create the tab
let projectInProduction = document.createElement("button");
projectInProduction.id = "project" + projectCount + "Click";
projectInProduction.classList.add("invisButton");
projectInProduction.classList.add("inactiveProjectButton");
projectInProduction.classList.add("project" + projectCount);
projectInProduction.innerHTML += (name);
// Remove the welcome message
document.querySelector('.unHidden').classList.add('hidden');
document.querySelector('.unHidden').classList.remove('unHidden');
//Create the workspace
let workspaceInProduction = document.createElement("div");
workspaceInProduction.classList.add("inactiveWorkspace");
projectArray.push(projectData);
if(projectArray[projectCount - 1].tiers == null) {
alert("This is not a trade file!\nPlease use a trade file!");
} else {
$('.projects').appendChild(projectInProduction);
$('#jsonProjectContainer').appendChild(workspaceInProduction);
let table = document.createElement('table');
table.innerHTML += '<tr><th colspan="3">Buying</th><th colspan="2">Selling</th></tr>';
table.classList.add('table' + projectCount);
workspaceInProduction.appendChild(table);
let tiers = Object.keys(projectArray[projectCount - 1].tiers).length;
console.log(tiers);
for(let i = 0; i < tiers; i++) {
let tradesC = Object.keys(projectArray[projectCount - 1].tiers[i].trades).length;
for(let j = 0; j < tradesC; j++) {
let wants = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].wants);
let wantsC = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].wants).length;
let gives = Object.keys(projectArray[projectCount - 1].tiers[i].trades[j].gives);
for(let k = 0; k < wantsC; k++) {
let wantsItem = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].item;
let wantsMin = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity["min"];
let wantsMax = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity["max"];
let givesItem = projectArray[projectCount - 1].tiers[i].trades[j].gives[0].item;
console.log("Wants: " + wants[k]);
console.log("Min: " + wantsMin);
console.log("Max: " + wantsMax);
console.log("Item: " + wantsItem);
console.log("Gives: " + gives);
console.log("Item: " + givesItem);
}
}
}
}
// Add a onlclick function to make switching projects work
j = projectCount;
document.getElementById('project' + projectCount + 'Click').addEventListener('click', function(j) {
let buttonIndex = j;
let buttonNodes = $('.projects').children;
let workspaceNodes = $('#jsonProjectContainer').children;
for(let i = 0; i < buttonNodes.length; i++) {
if(i == (j - 1)) {
buttonNodes[i].classList.add('activeProjectButton');
buttonNodes[i].classList.remove('inactiveProjectButton');
workspaceNodes[i].classList.add('activeWorkspace');
workspaceNodes[i].classList.remove('inactiveWorkspace');
} else {
buttonNodes[i].classList.remove('activeProjectButton');
buttonNodes[i].classList.add('inactiveProjectButton');
workspaceNodes[i].classList.remove('activeWorkspace');
workspaceNodes[i].classList.add('inactiveWorkspace');
}
}
}.bind(null, j));
}
// Title bar scripts
ipcRenderer.on('openFile', () => {
openFile();
})
ipcRenderer.on('closeFile', () => {
closeFile();
})
// App updates
ipcRenderer.send('appVersion');
ipcRenderer.on('appVersion', (event, arg) => {
ipcRenderer.removeAllListeners('appVersion');
//version.innerText = ;
});
const updateNotifier = document.getElementById('updateNotifier');
const updateAvailability = document.getElementById('updateAvailability');
const restartButton = document.getElementById('restartButton');
ipcRenderer.on('updateAvailable', () => {
ipcRenderer.removeAllListeners('updateAvailable');
updateAvailability.innerText = 'A new version of Route is available. Downloading now...';
updateNotifier.classList.remove('hidden');
});
ipcRenderer.on('update_downloaded', () => {
ipcRenderer.removeAllListeners('updateDownloaded');
updateAvailability.innerText = 'Update downloaded. It will be installed on restart. Restart to continue.';
restartButton.classList.remove('hidden');
updateNotifier.classList.remove('hidden');
});
function restartApp() {
ipcRenderer.send('restartApp');
}
And this is the index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>route - Minecraft Addon Tool</title>
<!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
<link href="index.css" type="text/css" rel="stylesheet" />
<link href="node_modules/#mdi/font/css/materialdesignicons.min.css" type="text/css" rel="stylesheet" />
</head>
<body>
<script>
// const electron = require('electron').remote;
const path = require('path');
const customTitleBar = require('custom-electron-titlebar');
const Menu = require('electron').remote.Menu;
const MenuItem = require('electron').remote.MenuItem;
let mainTitlebar = new customTitleBar.Titlebar({
backgroundColor: customTitleBar.Color.fromHex('#391B47'),
icon: 'build/icons/icon.png'
});
const menu = new Menu();
menu.append(new MenuItem({
label: 'Github',
click: () => {
currentWindow.webContents.on('new-window', require('electron').shell.openExternal('https://github.com/Gekocaretaker/route'));
}
}));
menu.append(new MenuItem({
label: 'Discord',
click: () => {
console.log("Hiding the link!");
}
}));
menu.append(new MenuItem({
label: 'File',
submenu: [
{
label: 'Open File',
accelerator: 'Ctrl+O',
click: () => {
openFile();
}
},
{
label: 'Close File',
accelerator: 'Ctrl+D',
click: () => {
closeFile();
}
},
{
label: 'New File',
accelerator: 'Ctrl+N',
click: () => {
console.log("This is not yet available.");
}
}
]
}))
mainTitlebar.updateMenu(menu);
</script>
<div class="grid-container">
<div class="buttons">
<div class="tooltip openFileContainer">
<button id="openFile" class="invisButton"><span class="mdi mdi-file mdi-48px"></span></button>
<div>File</div>
</div>
<div class="tooltip settingsContainer">
<button id="openSettings" class="invisButton"><span class="mdi mdi-cog-outline mdi-48px"></span></button>
<div>Settings</div>
</div>
<div class="tooltip closeProjectContainer">
<button id="closeFile" class="invisButton"><span class="mdi mdi-close mdi-48px"></span></button>
<div>Close Project</div>
</div>
</div>
<div class="projects">
<!-- <div class="project1">
<button class="invisButton activeProjectButton" id="project1Click">Project 1</button>
</div> -->
</div>
<div class="jsonEditor">
<!-- <div class="workSpace1 activeWorkSpace">
<h3>This is Workspace 1</h3>
</div> -->
<div id="jsonProjectContainer"></div>
<div class="unHidden">
<p id="welcomeMessage">Hello User! Welcome to Route. I hope you enjoy using this!</p>
</div>
<div id="updateNotifier" class="hidden">
<p id="updateAvailability"></p>
<button id="restartButton" onclick="restartApp()"></button>
</div>
</div>
<div class="sidebar">
<div class="tooltip tradeBuilder">
<button id="tradeSetup" class="invisButton"><span class="mdi mdi-apache-kafka mdi-48px"></span></button>
<div>Basic Trade</div>
</div>
<div class="tooltip whatNext">
<button id="whatsNext" class="invisButton"><span class="mdi mdi-help mdi-48px"></span></button>
<div>Whats Next?</div>
</div>
</div>
</div>
<script src="renderer.js"></script>
</body>
</html>
This is the error:
Uncaught (in promise) TypeError: Cannot read property 'min' of undefined
at createProject (renderer.js:126)
at renderer.js:48
And I am pretty sure the error comes from how I get "min". I have tried using ["min"], ['min'], .min, and as a last resort, [0].
I have the code hosted on github, if needing to be shared.
If you would debug your code, you would notice that the error occurs when j=1 and k=0. Then if you check the object, you notice that at that location there is no question property:
{
"tiers": [
{
"trades": [
{
/* ... */
},
{
"wants": [
{
"item": "minecraft:emerald"
/* no quantity here */
}
],
"gives": [
/* ... */
]
}
]
},
/* ... */
So you'll have to decide what you want to happen when there is no quantity property. You can for instance use the optional chaining operator to use a default value:
let wantsMin = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity.?min || 0;
let wantsMax = projectArray[projectCount - 1].tiers[i].trades[j].wants[k].quantity.?max || 0;
The above would give 0 as value for the min/max values that are missing. But all depends on what you want to happen in this scenario.
This is just an example on how you can circumvent the error, but might not be the right way to deal with it in light of the rest of your code. That is up to you to determine.
This code is to access or process the values that are in nested array.we are iterating with loops and checks the iterating values that either it is an array or not with .isArray if yes we fetch the value ,if no we return the value.
var arr=[['hi'],
['hello',['welcome','bye']],
['world']];
for(var i=0; i<arr.length; i++)
{
for(var j=0; j<arr.length; j++)
{
if(Array.isArray(arr[i][j]))
{
for(var k=0; k<arr.length; k++)
{
console.log(arr[i][j][k]);
}
}
else
{
console.log(arr[i][j]);
}
}
}

Prevent Jasmine Test expect() Resolving Before JS Finished Executing

I am hoping you can help. I am fairly new to Unit Testing. I have a Karma + Jasmine set up which is running a PhantomJS browser. This is all good.
What I am struggling with is I have a link on the page, when this link is clicked it injects some HTML. I want to test that the HTML has been injected.
Now at this point, I have the test working but only sometimes, from what I can figure out if my JS runs fast enough the HTML gets injected before the expect() is run. If not the test fails.
How can I make my Jasmine test wait for all JS to finish executing before the expect() is run?
The test in question is it("link can be clicked to open a modal", function() {
modal.spec.js
const modalTemplate = require('./modal.hbs');
import 'regenerator-runtime/runtime';
import 'core-js/features/array/from';
import 'core-js/features/array/for-each';
import 'core-js/features/object/assign';
import 'core-js/features/promise';
import Modal from './modal';
describe("A modal", function() {
beforeAll(function() {
const data = {"modal": {"modalLink": {"class": "", "modalId": "modal_1", "text": "Open modal"}, "modalSettings": {"id": "", "modifierClass": "", "titleId": "", "titleText": "Modal Title", "closeButton": true, "mobileDraggable": true}}};
const modal = modalTemplate(data);
document.body.insertAdjacentHTML( 'beforeend', modal );
});
it("link exists on the page", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
expect(modalLink).not.toBeNull();
});
it("is initialised", function() {
spyOn(Modal, 'init').and.callThrough();
Modal.init();
expect(Modal.init).toHaveBeenCalled();
});
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
modalLink.click();
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
afterAll(function() {
console.log(document.body);
// TODO: Remove HTML
});
});
EDIT - More Info
To further elaborate on this, The link Jasmine 2.0 how to wait real time before running an expectation put in the comments has helped me understand a bit better, I think. So what we are saying it we want to spyOn the function and wait for it to be called and then initiate a callback which then resolves the test.
Great.
My next issue is, if you look at the structure of my ModalViewModel class below, I need to be able to spyOn insertModal() to be able to do this, but the only function that is accessible in init(). What would I do to be able to move forward with this method?
import feature from 'feature-js';
import { addClass, removeClass, hasClass } from '../../01-principles/utils/classModifiers';
import makeDraggableItem from '../../01-principles/utils/makeDraggableItem';
import '../../01-principles/utils/polyfil.nodeList.forEach'; // lt IE 12
const defaultOptions = {
id: '',
modifierClass: '',
titleId: '',
titleText: 'Modal Title',
closeButton: true,
mobileDraggable: true,
};
export default class ModalViewModel {
constructor(module, settings = defaultOptions) {
this.options = Object.assign({}, defaultOptions, settings);
this.hookModalLink(module);
}
hookModalLink(module) {
module.addEventListener('click', (e) => {
e.preventDefault();
this.populateModalOptions(e);
this.createModal(this.options);
this.insertModal();
if (this.options.closeButton) {
this.hookCloseButton();
}
if (this.options.mobileDraggable && feature.touch) {
this.hookDraggableArea();
}
addClass(document.body, 'modal--active');
}, this);
}
populateModalOptions(e) {
this.options.id = e.target.getAttribute('data-modal');
this.options.titleId = `${this.options.id}_title`;
}
createModal(options) {
// Note: As of ARIA 1.1 it is no longer correct to use aria-hidden when aria-modal is used
this.modalTemplate = `<section id="${options.id}" class="modal ${options.modifierClass}" role="dialog" aria-modal="true" aria-labelledby="${options.titleId}" draggable="true">
${options.closeButton ? '<a href="#" class="modal__close icon--cross" aria-label="Close" ></a>' : ''}
${options.mobileDraggable ? '<a href="#" class="modal__mobile-draggable" ></a>' : ''}
<div class="modal__content">
<div class="row">
<div class="columns small-12">
<h2 class="modal__title" id="${options.titleId}">${options.titleText}</h2>
</div>
</div>
</div>
</section>`;
this.modal = document.createElement('div');
addClass(this.modal, 'modal__container');
this.modal.innerHTML = this.modalTemplate;
}
insertModal() {
document.body.appendChild(this.modal);
}
hookCloseButton() {
this.closeButton = this.modal.querySelector('.modal__close');
this.closeButton.addEventListener('click', (e) => {
e.preventDefault();
this.removeModal();
removeClass(document.body, 'modal--active');
});
}
hookDraggableArea() {
this.draggableSettings = {
canMoveLeft: false,
canMoveRight: false,
moveableElement: this.modal.firstChild,
};
makeDraggableItem(this.modal, this.draggableSettings, (touchDetail) => {
this.handleTouch(touchDetail);
}, this);
}
handleTouch(touchDetail) {
this.touchDetail = touchDetail;
const offset = this.touchDetail.moveableElement.offsetTop;
if (this.touchDetail.type === 'tap') {
if (hasClass(this.touchDetail.eventObject.target, 'modal__mobile-draggable')) {
if (offset === this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '0px';
} else {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
}
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
} else {
this.touchDetail.eventObject.target.click();
}
} else if (this.touchDetail.type === 'flick' || (this.touchDetail.type === 'drag' && this.touchDetail.distY > 200)) {
if (this.touchDetail.direction === 'up') {
if (offset < this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '0px';
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
}
} else if (this.touchDetail.direction === 'down') {
if (offset < this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.originY}px`;
} else if (offset > this.touchDetail.originY) {
this.touchDetail.moveableElement.style.top = '95%';
}
}
} else {
this.touchDetail.moveableElement.style.top = `${this.touchDetail.moveableElementStartY}px`;
}
}
removeModal() {
document.body.removeChild(this.modal);
}
static init() {
const instances = document.querySelectorAll('[data-module="modal"]');
instances.forEach((module) => {
const settings = JSON.parse(module.getAttribute('data-modal-settings')) || {};
new ModalViewModel(module, settings);
});
}
}
UPDATE
After working through it has been discovered that .click() events are asynchronous which is why I am gettnig the race issue. Documentation & Stack Overflow issues thoughtout the web recommend using createEvent() and dispatchEvent() as PhantomJs does not understand new MouseEvent().
Here is my code which is now trying to do this.
modal.spec.js
// All my imports and other stuff
// ...
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
// Some other tests
// Some other tests
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
// After all code
// ...
});
Unfortunately this is producting the same results. 1 step closer but not quite there.
After a touch of research, it looks as though your use of the click event is triggering an asynchronous event loop essentially saying "Hey set this thing to be clicked and then fire all the handlers"
Your current code can't see that and has no real way of waiting for it. I do believe you should be able to build and dispatch a mouse click event using the info here.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent
I think that should allow you to build a click event and dispatch it onto your element. The difference is that dispatchEvent is synchronous - it should block your test until the click handlers have completed. That should allow you to do your assertion without failures or race conditions.
I have finally found a solution.
There are 2 parts to this, the first part came from #CodyKnapp. His insight into a click() function running asynchronously helped to solve the first part of the issue.
Here is the code for this part.
modal.spec.js
// All my imports and other stuff
// ...
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
// Some other tests
// Some other tests
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
// After all code
// ...
});
This allowed for the code to run synchronously.
The second part was a poor understanding on my part of how to write Jasmine tests. In my original tests I was running Modal.init() inside of it("is initialised", function() { when actually I want to be running this inside of beforeAll(). This fixed the issue I had where my tests would not always be successful.
Here is my final code:
modal.spec.js
const modalTemplate = require('./modal.hbs');
import '#babel/polyfill';
import Modal from './modal';
function click(element){
var event = document.createEvent('MouseEvent');
event.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
element.dispatchEvent(event);
}
describe("A modal", function() {
beforeAll(function() {
const data = {"modal": {"modalLink": {"class": "", "modalId": "modal_1", "text": "Open modal"}, "modalSettings": {"id": "", "modifierClass": "", "titleId": "", "titleText": "Modal Title", "closeButton": true, "mobileDraggable": true}}};
const modal = modalTemplate(data);
document.body.insertAdjacentHTML( 'beforeend', modal );
spyOn(Modal, 'init').and.callThrough();
Modal.init();
});
it("link exists on the page", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
expect(modalLink).not.toBeNull();
});
it("is initialised", function() {
expect(Modal.init).toHaveBeenCalled();
});
it("link can be clicked to open a modal", function() {
const modalLink = document.body.querySelector('[data-module="modal"]');
click(modalLink);
const modal = document.body.querySelector('.modal');
expect(modal).not.toBeNull();
});
afterAll(function() {
console.log(document.body);
// TODO: Remove HTML
});
});

Picking images from PhotoLibrary not working - Ionic 4

I am implementing a picture upload functionality to my app which I am developing with Ionic 4. I'm using the native plugin camera and a few others to do the following:
async selectImage() {
const actionSheet = await this.actionsheet.create({
header: "Select Image source",
buttons: [{
text: 'Load from Library',
handler: () => {
this.takePicture(this.camera.PictureSourceType.PHOTOLIBRARY);
}
},
{
text: 'Use Camera',
handler: () => {
this.takePicture(this.camera.PictureSourceType.CAMERA);
}
},
{
text: 'Cancel',
role: 'cancel'
}
]
});
await actionSheet.present();
}
takePicture(sourceType: PictureSourceType) {
var options: CameraOptions = {
quality: 100,
sourceType: sourceType,
saveToPhotoAlbum: false,
correctOrientation: true
};
this.camera.getPicture(options).then(imagePath => {
var currentName = imagePath.substr(imagePath.lastIndexOf('/') + 1);
var correctPath = imagePath.substr(0, imagePath.lastIndexOf('/') + 1);
this.copyFileToLocalDir(correctPath, currentName, this.createFileName());
});
}
copyFileToLocalDir(namePath, currentName, newFileName) {
this.file.copyFile(namePath, currentName, this.file.dataDirectory, newFileName).then(success => {
this.presentToast('Dispongo a actualizar.');
this.updateStoredImages(newFileName);
}, error => {
// this.presentToast('Error while storing file.');
});
}
updateStoredImages(name) {
this.storage.get(STORAGE_KEY).then(images => {
let arr = JSON.parse(images);
if (!arr) {
let newImages = [name];
this.storage.set(STORAGE_KEY, JSON.stringify(newImages));
} else {
arr.push(name);
this.storage.set(STORAGE_KEY, JSON.stringify(arr));
}
let filePath = this.file.dataDirectory + name;
let resPath = this.pathForImage(filePath);
let newEntry = {
name: name,
path: resPath,
filePath: filePath
};
this.images = [newEntry, ...this.images];
this.ref.detectChanges(); // trigger change detection cycle
});
}
So, in the action sheet, when I press the first option (Load from Library) it opens the library and I can choose the picture without any problem. When I press ok, it throws an error: the error expected from the copyFileToLocalDir. However, if I do the same with the second option (Use Camera) and I take a photo with the camera, it loads it fine and I can store it later.
I can't find the problem, please help.
im using this code using ionic 3 and it's working fine .
and after i chose one image it will be uploading to firebase and on the same time view it at page.html
app.module.ts
you have to import
import { Camera } from "#ionic-native/camera";
import { File } from "#ionic-native/file";
and added them #providers
then use this code at page.ts which you will chose one image :
html view
<button ion-button full (click)="openGallery()">open gallery</button>
<img [src]="camel_profile_image_path" />
ts page
import { Camera, CameraOptions } from "#ionic-native/camera";
private camera: Camera,
async openGallery() {
try {
const opstions: CameraOptions = {
quality: 100,
targetHeight: 600,
targetWidth: 600,
destinationType: this.camera.DestinationType.DATA_URL,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
correctOrientation: true
}
const result = await this.camera.getPicture(opstions);
const image = 'data:image/jpeg;base64,' + result;
const pictures = storage().ref('Profile Images/' + this.randomNumber + '.jpg');
pictures.putString(image, 'data_url');
this.base64Image = image;
this.camel_profile_image_path = this.randomNumber; // view the image on html page
this.slidetothis();
} catch (error) {
console.error(error);
}
}

Building custom Quill Editor theme

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;

Categories

Resources