import {
folderItem,
plotItem,
imageItem,
figureItem,
breadcrumb
} from "./elements";
import { NS } from "./namespaces.js";
import { isValidUnit } from "../../../common/units.js";
import * as hstry from "../../../svgcanvas/history";
const { InsertElementCommand, BatchCommand, UndoManager } = hstry;
/**
* @file ext-figlinq.js
* @license MIT
* @copyright 2021 figlinq.com
*/
const name = "figlinq";
const baseUrl =
location.hostname == "svgedit.plotly.local"
? "https://plotly.local/"
: "https://" + location.hostname + "/";
export default {
name,
async init() {
const svgEditor = this;
const { svgCanvas } = svgEditor;
return {
name: svgEditor.i18next.t(`${name}:name`),
callback() {
document
.querySelector("#se-cmenu_canvas")
.shadowRoot.querySelector(".contextMenu")
.setAttribute("style", "min-width:250px;");
jQuery(document).keydown(function(e) {
// Ctrl + z (undo)
if (
e.originalEvent.ctrlKey &&
!e.originalEvent.shiftKey &&
e.originalEvent.keyCode == 90
) {
clickUndo();
}
// Ctrl + Shift + z (redo)
else if (
e.originalEvent.ctrlKey &&
e.originalEvent.shiftKey &&
e.originalEvent.keyCode == 90
) {
clickRedo();
}
// Ctrl + y (redo)
else if (e.originalEvent.ctrlKey && e.originalEvent.keyCode == 89) {
clickRedo();
} else if (e.originalEvent.ctrlKey && e.originalEvent.keyCode == 89) {
clickRedo();
}
});
jQuery(window).bind("mousewheel DOMMouseScroll", function(event) {
if (event.ctrlKey == true) {
event.preventDefault();
showZoomWarning();
return false;
}
});
jQuery(".modal").bind("mousewheel DOMMouseScroll", function(event) {
if (event.ctrlKey == true) {
event.preventDefault();
return false;
}
});
// Initiate global vars
var fqItemListFolder,
fqItemListFile,
fqItemListPreselected = false,
fqUsername,
fqUserData,
fqCurrentFigData = false,
fqModalMode,
fqModalFileTabMode = "my",
fqCsrfToken,
fqSelectedFolderId = {
my: false,
shared: false,
preselected: false
},
fqSearchMode = false,
fqHighlightedFids = false,
fqExportDocType,
fqExportDocQuality,
fqExportDocSize,
fqExportDocFname,
fqExportMode,
fqThumbWidth = 1200,
_typeMap,
fqDefaultMargins = {
// in mm
left: 15,
top: 15,
right: 15,
bottom: 15
},
fqDefaultSpacing = {
// in mm
horizontal: 5,
vertical: 10
};
const svgAttrWhitelist = ["class", "height", "width", "x", "y", "id"];
const fqPdfPageSizes = {
A0: "2383.94x3370.39",
A1: "1683.78x2383.94",
A2: "1190.55x1683.78",
A3: "841.89x1190.55",
A4: "595.28x841.89",
A5: "419.53x595.28",
Letter: "612.00x792.00"
};
const fqExportFileFormats = {
"1x (current)": 1,
"2x": 2,
"3x": 3,
"4x": 4,
"6x": 6,
"8x": 8
};
const NSSVG = "http://www.w3.org/2000/svg";
const alphabet = [
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z"
];
const setDeep = function(obj, path, value, setrecursively = false) {
path.reduce((a, b, level) => {
if (
setrecursively &&
typeof a[b] === "undefined" &&
level !== path.length
) {
a[b] = {};
return a[b];
}
if (level === path.length - 1) {
a[b] = value;
return value;
}
return a[b];
}, obj);
};
// To use this function we need to get content_type field into the "children" object returned from v2
// const getExt = (contentType) => {
// if (contentType == "image/jpeg" || contentType == "image/jpg") return "jpg";
// if (contentType == "image/svg" || contentType == "image/svg+xml") return "svg";
// if (contentType == "image/png") return "png";
// if (contentType == "image/bmp") return "bmp";
// if (contentType == "image/gif") return "gif";
// }
// Replace the broken zoom functionality in v7
const upgradeUi = () => {
// Replace zoom button
const element = `
<div id="zoom" class="select is-small" title="Hold shift while scrolling your mouse to zoom dynamically">
<select>
<option value="default">Zoom level</option>
<option value="1000">1000</option>
<option value="400">400</option>
<option value="200">200</option>
<option value="100">100</option>
<option value="50">50</option>
<option value="25">25</option>
<option value="canvas">Fit to canvas</option>
<option value="selection">Fit to selection</option>
<option value="layer">Fit to content</option>
</select>
</div>`;
jQuery("#zoom").replaceWith(element);
// Hide image URL input
jQuery("#image_url").hide();
};
// Fitting to content does not work
// <option value="layer">Fit to layer content</option>
// <option value="content">Fit to all content</option>
const showZoomWarning = () => {
jQuery("#fq-modal-warning-zoom").addClass("is-active");
setTimeout(function() {
jQuery("#fq-modal-warning-zoom").removeClass("is-active");
}, 1250);
};
const getFqUsername = () => {
jQuery
.ajax({
url: baseUrl + "v2/users/current",
xhrFields: { withCredentials: true }
})
.done(function(data) {
if (data.username) {
jQuery("#fq-menu-login-btn").addClass("is-hidden");
jQuery("#fq-menu-signup-btn").addClass("is-hidden");
jQuery(".fq-menu-add-content-btn").removeClass("is-hidden");
jQuery("#fq-menu-interact-switch-item").removeClass(
"is-hidden"
);
jQuery("#fq-menu-file-open-figure").removeClass("is-hidden");
jQuery("#fq-menu-file-save-figure").removeClass("is-hidden");
jQuery("#fq-menu-file-save-figure-as").removeClass("is-hidden");
jQuery("#fq-menu-file-import-local-content").removeClass(
"is-hidden"
);
jQuery("#fq-breadcrumb-item-home")
.data("fid", `${data.username}:-1`)
.find(".fq-modal-folder-item")
.data("fid", `${data.username}:-1`);
fqUsername = data.username;
jQuery("#fq-menu-account-user-name").html(
fqUsername.slice(0, 2)
);
jQuery("#fq-menu-account-dropdown-user-name").html(fqUsername);
jQuery("#fq-menu-account-navbar-item1").removeClass(
"is-hidden"
);
jQuery("#fq-menu-account-navbar-item2").removeClass(
"is-hidden"
);
jQuery("#fq-user-link-files").attr(
"href",
baseUrl + "organize/home"
);
jQuery("#fq-user-link-charts").attr("href", baseUrl + "create");
jQuery("#fq-user-link-figures").attr(
"href",
baseUrl + "figures"
);
jQuery("#fq-user-link-collections").attr(
"href",
baseUrl + "dashboard/create"
);
jQuery("#fq-menu-account-sign-out").attr(
"href",
baseUrl + "signout"
);
jQuery("#fq-menu-account-settings").attr(
"href",
baseUrl + "settings/profile"
);
fqCsrfToken = data.csrf_token;
fqUserData = data;
} else {
showToast(
"It looks like you are not logged in to FiglinQ in this browser!",
"is-danger"
);
}
})
.fail(function() {
jQuery("#fq-menu-login-btn").removeClass("is-hidden");
jQuery("#fq-menu-signup-btn").removeClass("is-hidden");
jQuery(".fq-menu-add-content-btn").addClass("is-hidden");
jQuery(".fq-menu-add-content-btn").addClass("is-hidden");
jQuery(".fq-menu-add-content-btn").addClass("is-hidden");
jQuery(".fq-menu-add-content-btn").addClass("is-hidden");
showToast(
"Could not connect to FiglinQ - are you logged in?",
"is-danger"
);
});
};
const setInteractiveOff = () => {
jQuery("#fq-menu-interact-switch").prop("checked", false);
const fObjects = jQuery("svg[class='fq-fobj-container']");
fObjects.each(function() {
var ref_id = jQuery(this).data("ref_id");
jQuery("#" + ref_id).attr("visibility", "visible");
this.remove();
});
};
const setInteractiveOn = () => {
svgCanvas.clearSelection();
// Get all plots
const plots = jQuery(".fq-plot");
var plot, foreignObject, newElem;
plots.each(function() {
plot = jQuery(this);
// Generate foreignObject JSON
foreignObject = generateForeignObject(plot);
// Add to canvas
newElem = svgCanvas.addSVGElementFromJson(foreignObject);
// Hide the plot image
this.setAttribute("visibility", "hidden");
});
};
const updateBreadcrumb = (fid, fname) => {
var fidPresent = false;
jQuery(".breadcrumb-item").each(function() {
if (jQuery(this).data("fid") == fid) fidPresent = true;
});
if (fidPresent) {
jQuery(
jQuery(".breadcrumb-item")
.get()
.reverse()
).each(function() {
if (jQuery(this).data("fid") == fid) {
return false;
} else {
jQuery(this).remove();
}
});
} else if (fname) {
jQuery(breadcrumb(fid, fname)).insertAfter(".breadcrumb-item:last");
} else {
jQuery(".breadcrumb-item:not(#fq-breadcrumb-item-home)").remove();
}
};
const getSortedElems = (selector, attrName) => {
return jQuery(
jQuery(selector)
.toArray()
.sort((a, b) => {
var aVal = parseInt(a.getAttribute(attrName)),
bVal = parseInt(b.getAttribute(attrName));
return aVal - bVal;
})
);
};
const getUrlParameter = function getUrlParameter(sParam) {
var sPageURL = window.location.search.substring(1),
sURLVariables = sPageURL.split("&"),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split("=");
if (sParameterName[0] === sParam) {
return typeof sParameterName[1] === undefined
? true
: decodeURIComponent(sParameterName[1]);
}
}
return false;
};
/**
* Loads figure from url or opens content add modal
* @returns {Null}
*/
const loadFqFigure = () => {
const fid = getUrlParameter("fid");
const add = getUrlParameter("add");
if (fid) {
svgCanvas.clear();
openFigure({ data: { fid: fid } });
} else if (add) {
// preload multiple files, open modal
const fidArray = add.split(",");
var checked = jQuery("#fq-menu-interact-switch").is(":checked");
if (checked) jQuery("#fq-menu-interact-switch").click();
fqModalFileTabMode = "preselected";
showModalSpinner();
prepareFileModal("addFiglinqPreselectedContent");
refreshModalContents(fidArray);
}
};
const parseFid = (dataFid, index = 1) => {
if (!dataFid || !dataFid.includes(":")) return false;
return dataFid.split(":")[index];
};
const getFileDataFromFiglinQ = (fid, endpoint = "files") => {
const url = `${baseUrl}v2/${endpoint}/${fid}`;
return new Promise(resolve => {
jQuery
.ajax({
url: url,
xhrFields: {
withCredentials: true
}
})
.done(function(data) {
resolve(data);
})
.fail(function(error) {
resolve({
fid: fid,
error: error.responseJSON.detail
});
});
});
};
const updateItemList = (dataFid, page, searchQuery = false) => {
if (Array.isArray(dataFid)) {
// Get data for each file
var elementProps = [];
dataFid.forEach(fid => {
elementProps.push({
fid: fid,
endpoint: "files"
});
});
var actions = elementProps.map(function(prop) {
return getFileDataFromFiglinQ(prop.fid, prop.endpoint);
});
var results = Promise.all(actions); // pass array of promises
results.then(data => {
var dataFormatted = {
children: {
results: data,
next: null
}
};
fqItemListPreselected = {
data: dataFormatted,
fids: dataFid
};
jQuery(".fq-modal-file-tab").removeClass("is-active");
jQuery("#fq-modal-file-tab-preselected").removeClass("is-hidden");
jQuery("#fq-modal-file-tab-preselected").addClass("is-active");
populateFileModal(dataFormatted);
});
return;
}
const fid = parseFid(dataFid);
var url;
const myFileTypes =
fqModalMode == "upload"
? "filetype=fold"
: "filetype=plot&filetype=fold&filetype=external_image";
if (fqModalFileTabMode == "my") {
url = searchQuery
? `${baseUrl}v2/folders/all?s=${searchQuery}&filetype=plot&filetype=external_image&page_size=1000`
: `${baseUrl}v2/folders/${dataFid}?page=${page}&${myFileTypes}&order_by=filename&page_size=1000`;
} else if (fqModalFileTabMode == "shared") {
if (fid == -1) {
url = `${baseUrl}v2/folders/shared?filetype=fold&filetype=plot&filetype=external_image&order_by=filename&page_size=1000`;
} else {
url = `${baseUrl}v2/folders/${dataFid}?page=${page}&filetype=fold&filetype=plot&filetype=external_image&order_by=filename&page_size=1000`;
}
}
jQuery
.ajax({
url: url,
xhrFields: {
withCredentials: true
}
})
.done(populateFileModal)
.fail(function() {
showToast(
"Communication error, file list has not been updated",
"is-danger"
);
})
.always(function() {});
};
const resetPlotImageUrls = () => {
const plotImages = jQuery(".fq-plot");
plotImages.each(function() {
const href = jQuery(this).attr("href");
const hrefCleaned = href.split("#")[0];
jQuery(this).attr("href", hrefCleaned);
});
};
const populateFileModal = (data, selectedFids = false) => {
jQuery("#fq-modal-files-open-figure-confirm").prop("disabled", true);
let val = jQuery("#fq-modal-save-name-input").val();
if (val.length) {
jQuery("#fq-modal-save-confirm-btn").prop("disabled", false);
} else {
jQuery("#fq-modal-save-confirm-btn").prop("disabled", true);
}
var index = 0,
results = data.children.results;
results.forEach(result => {
const isFigure = "svgedit" in result.metadata;
const includeNonSvg =
fqModalMode === "addContent" || fqModalMode === "upload";
if (result.filetype === "fold" && !result.deleted) {
fqItemListFolder += folderItem(result.filename, result.fid);
} else if (
isFigure &&
result.filetype === "external_image" &&
!result.deleted
) {
// TODO importing figures with external links into other figures is currently disabled, but could be enabled if they are inlined
var haslinkedcontent = false;
if (
fqModalMode === "addContent" &&
result.hasOwnProperty("metadata") &&
result.metadata.hasOwnProperty("svgedit") &&
result.metadata.svgedit.hasOwnProperty("haslinkedcontent") &&
result.metadata.svgedit.haslinkedcontent === true
) {
haslinkedcontent = true;
}
fqItemListFile += figureItem(
result.filename,
result.fid,
index,
haslinkedcontent
);
index += 1;
} else if (
includeNonSvg &&
!isFigure &&
result.filetype === "external_image" &&
!result.deleted
) {
fqItemListFile += imageItem(result.filename, result.fid, index);
index += 1;
} else if (
includeNonSvg &&
result.filetype === "plot" &&
!result.deleted
) {
fqItemListFile += plotItem(result.filename, result.fid, index);
index += 1;
} else if ("error" in result) {
showToast(
`Error loading file ${result.fid} - ${result.error}.`,
"is-danger"
);
}
});
if (data.children.next == null) {
jQuery(".panel-list-item").remove();
jQuery("#fq-modal-item-list-container").html(
fqItemListFolder + fqItemListFile
);
jQuery("#fq-modal-refresh-btn").removeClass("is-loading");
} else {
page = page + 1;
updateItemList(dataFid, page);
}
var items = results.length == 1 ? "item" : "items";
jQuery("#fq-modal-file-search-items-found").text(
results.length + " " + items + " found"
);
if (fqItemListPreselected && fqModalFileTabMode === "preselected") {
fqItemListPreselected.fids.forEach(fid => {
const isDisabled = jQuery("*[data-fid='" + fid + "']").hasClass(
"is-disabled"
);
if (!isDisabled)
jQuery("*[data-fid='" + fid + "']").addClass("is-active");
jQuery("#fq-modal-add-confirm-btn").prop("disabled", false);
});
}
if (fqHighlightedFids) {
fqHighlightedFids.forEach(fid => {
jQuery("*[data-fid='" + fid + "']").addClass("is-active");
});
fqHighlightedFids = false;
}
hideModalSpinner();
};
const generateForeignObject = currentImg => {
const imgHref = currentImg.attr("href");
const contentHref = decodeURIComponent(
currentImg.data("content_href")
);
const contentHrefGuess = imgHref.replace(".svg", ".embed");
const iframeSrc =
contentHref == null ? contentHrefGuess : contentHref;
const height = currentImg.attr("height");
const width = currentImg.attr("width");
const x = currentImg.attr("x");
const y = currentImg.attr("y");
const id = currentImg.attr("id");
const originalDimensions = currentImg
.data("original_dimensions")
.split(",");
const fid = currentImg.data("fid");
let newIframe = {
element: "iframe",
namespace: NS.HTML,
attr: {
x: 0,
y: 0,
width: originalDimensions[0],
height: originalDimensions[1],
id: svgCanvas.getNextId(),
src: iframeSrc,
xmlns: NS.HTML,
allow: "fullscreen"
}
};
let newBody = {
element: "body",
namespace: NS.HTML,
attr: {
id: svgCanvas.getNextId(),
xmlns: NS.HTML
},
children: [newIframe]
};
let newForeignObj = {
element: "foreignObject",
namespace: NS.SVG,
attr: {
x: 0,
y: 0,
width: originalDimensions[0],
height: originalDimensions[1],
id: svgCanvas.getNextId(),
xmlns: NS.SVG
},
children: [newBody]
};
let newSvg = {
element: "svg",
namespace: NS.SVG,
attr: {
x: x,
y: y,
width: width,
height: height,
viewBox: `0 0 ${originalDimensions[0]} ${originalDimensions[1]}`,
preserveAspectRatio: "none",
id: svgCanvas.getNextId(),
class: "fq-fobj-container",
"data-ref_id": id,
"data-fid": fid,
xmlns: NS.SVG
},
children: [newForeignObj]
};
return newSvg;
};
const placeTextElement = (attr, text = "") => {
const _elem = {
element: "text",
attr: attr
};
const batchCmd = new BatchCommand("Insert text");
const newElem = svgCanvas.addSVGElementFromJson(_elem);
newElem.textContent = text;
batchCmd.addSubCommand(new InsertElementCommand(newElem));
svgCanvas.undoMgr.addCommandToHistory(batchCmd);
svgCanvas.call("changed", [newElem]);
};
const placeElement = imgProps => {
var attr = {
xmlns: NS.SVG,
preserveAspectRatio: "none",
id: svgCanvas.getNextId(),
style: "pointer-events:inherit",
class: "fq-" + imgProps.filetype,
href: imgProps.imgHref,
width: imgProps.width,
height: imgProps.height,
x: imgProps.x,
y: imgProps.y,
"data-original_dimensions": `${imgProps.widthOriginal},${imgProps.heightOriginal}`,
"data-fid": imgProps.fid
};
if (imgProps.filetype == "plot")
attr["data-content_href"] = imgProps.contentHref;
const _img = {
element: "image",
namespace: NS.SVG,
attr: attr
};
const batchCmd = new BatchCommand("Insert plot");
const newElem = svgCanvas.addSVGElementFromJson(_img);
batchCmd.addSubCommand(new InsertElementCommand(newElem));
svgCanvas.undoMgr.addCommandToHistory(batchCmd);
svgCanvas.call("changed", [newElem]);
};
const getSvgFromEditor = () => {
svgCanvas.clearSelection();
// Temporarily switch units to pixels for correct viewbox settings
var revertUnit = false;
const initialUnit = svgEditor.configObj.curConfig.baseUnit;
if (initialUnit !== "px") {
revertUnit = true;
svgEditor.configObj.curConfig.baseUnit = "px";
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
}
const saveOpts = {
images: svgEditor.configObj.pref("img_save"),
round_digits: 6,
apply: true
};
const saveOptions = svgCanvas.mergeDeep(
svgCanvas.getSvgOption(),
saveOpts
);
for (const [key, value] of Object.entries(saveOptions)) {
svgCanvas.setSvgOption(key, value);
}
const svg = '<?xml version="1.0"?>' + svgCanvas.svgCanvasToString();
// Revert back to previous units
if (initialUnit !== "px" && revertUnit) {
svgEditor.configObj.curConfig.baseUnit = initialUnit;
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
}
return svg;
};
const showToast = (msg, type) => {
const duration = type === "is-danger" ? 10000 : 4000;
bulmaToast.toast({
message: msg,
type: type,
position: "bottom-right",
closeOnClick: true,
dismissible: true,
duration: duration
});
};
const ensureRulesGrids = () => {
let showGrid = svgEditor.configObj.curConfig.showGrid;
if (showGrid) {
jQuery("#fq-menu-view-show-grid")
.find("i")
.removeClass("fa-square")
.addClass("fa-check-square");
} else {
jQuery("#fq-menu-view-show-grid")
.find("i")
.addClass("fa-square")
.removeClass("fa-check-square");
}
let showRulers = svgEditor.configObj.curConfig.showRulers;
if (showRulers) {
jQuery("#fq-menu-view-show-rulers")
.find("i")
.removeClass("fa-square")
.addClass("fa-check-square");
} else {
jQuery("#fq-menu-view-show-rulers")
.find("i")
.addClass("fa-square")
.removeClass("fa-check-square");
}
};
const closeModalOnEscape = e => {
if (e.key === "Escape") {
jQuery("#fq-modal-file").removeClass("is-active");
jQuery(document).unbind("keyup", closeModalOnEscape);
}
};
const refreshModalContents = (fidArray = false) => {
fqItemListFolder = "";
fqItemListFile = "";
jQuery("#fq-modal-file").addClass("is-active");
jQuery(document).keyup(closeModalOnEscape);
let q = jQuery("#fq-modal-file-search-input").val();
if (q && fqModalFileTabMode == "my") {
jQuery("#fq-modal-file-search-icon").removeClass("fas fa-search");
jQuery("#fq-modal-file-search-icon").addClass(
"far fa-times-circle"
);
} else {
jQuery("#fq-modal-file-search-icon").removeClass(
"far fa-times-circle"
);
jQuery("#fq-modal-file-search-icon").addClass("fas fa-search");
}
q = q.length >= 2 ? q : false;
if (fidArray) {
// Open specific fids in modal
updateItemList(fidArray, 1, q);
} else if (q && fqModalFileTabMode == "my") {
// Search query present, "My files" tab
fqSearchMode = true;
jQuery("#fq-modal-file-search-title").removeClass("is-hidden");
jQuery("#fq-modal-file-panel-breadcrumb").addClass("is-hidden");
updateItemList("shared", 1, q);
} else {
fqSearchMode = false;
jQuery("#fq-modal-file-search-title").addClass("is-hidden");
jQuery("#fq-modal-file-panel-breadcrumb").removeClass("is-hidden");
if (fqSelectedFolderId[fqModalFileTabMode]) {
updateItemList(fqSelectedFolderId[fqModalFileTabMode], 1);
} else {
fqSelectedFolderId[fqModalFileTabMode] =
fqModalFileTabMode == "my" ? fqUsername + ":-1" : "shared:-1";
updateItemList(fqSelectedFolderId[fqModalFileTabMode], 1);
}
}
updateBreadcrumb(fqSelectedFolderId[fqModalFileTabMode], false);
};
const decodeBase64 = function(s) {
var e = {},
i,
b = 0,
c,
x,
l = 0,
a,
r = "",
w = String.fromCharCode,
L = s.length;
var A =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (i = 0; i < 64; i++) {
e[A.charAt(i)] = i;
}
for (x = 0; x < L; x++) {
c = e[s.charAt(x)];
b = (b << 6) + c;
l += 6;
while (l >= 8) {
((a = (b >>> (l -= 8)) & 0xff) || x < L - 2) && (r += w(a));
}
}
return r;
};
const clickUndo = function() {
const { undoMgr, textActions } = svgCanvas;
if (undoMgr.getUndoStackSize() > 0) {
undoMgr.undo();
svgEditor.layersPanel.populateLayers();
if (svgEditor.svgCanvas.getMode() === "textedit") {
textActions.clear();
}
}
};
const clickRedo = function() {
const { undoMgr } = svgCanvas;
if (undoMgr.getRedoStackSize() > 0) {
undoMgr.redo();
svgEditor.layersPanel.populateLayers();
}
};
const onConfirmClear = async function() {
jQuery("#fq-modal-confirm").removeClass("is-active");
// Remove fid from url
var currentUrl = new URL(document.location);
currentUrl.searchParams.delete("fid");
window.history.pushState(null, null, currentUrl.href);
fqCurrentFigData = false;
const [x, y] = svgEditor.configObj.curConfig.dimensions;
svgEditor.leftPanel.clickSelect();
svgEditor.svgCanvas.clear();
svgEditor.svgCanvas.setResolution(x, y);
svgEditor.updateCanvas(true);
svgEditor.zoomImage();
svgEditor.layersPanel.populateLayers();
svgEditor.topPanel.updateContextPanel();
svgEditor.svgCanvas.runExtensions("onNewDocument");
setTimeout(function() {
svgEditor.zoomChanged(window, "canvas");
}, 300);
};
const openFigure = async function(e) {
var url = baseUrl + "v2/external-images/" + e.data.fid;
jQuery
.ajax({
url: url,
xhrFields: { withCredentials: true }
})
.done(function(data) {
if (
!"image_content" in data ||
(data.content_type != "image/svg" &&
data.content_type != "image/svg+xml")
) {
showToast("This file type is not supported", "is-danger");
return;
}
var dataUrl = data.image_content;
var svgString = decodeBase64(dataUrl.split(",")[1]);
svgEditor.loadSvgString(svgString);
jQuery("#fq-modal-confirm").removeClass("is-active");
fqCurrentFigData = data;
if (
typeof fqCurrentFigData.metadata === "string" ||
fqCurrentFigData.metadata instanceof String
)
fqCurrentFigData.metadata = JSON.parse(
fqCurrentFigData.metadata
);
setTimeout(function() {
svgEditor.zoomChanged(window, "canvas");
}, 250);
showToast('File "' + data.filename + '" loaded', "is-success");
// Add fid to url
var currentUrl = new URL(document.location);
currentUrl.searchParams.set("fid", data.fid);
window.history.pushState(
null,
null,
decodeURIComponent(currentUrl.href)
);
})
.fail(function(data) {
showToast("This file could not be loaded", "is-danger");
});
};
const inlineRasterImage = async function(imgId) {
var imgUrl = jQuery("#" + imgId).attr("href");
let fetchResult = await fetch(imgUrl, {
method: "GET",
mode: "cors",
credentials: "include",
headers: {
"X-CSRFToken": fqCsrfToken
}
}).then(async r => {
const blob = await r.blob();
const type = r.headers.get("Content-Type");
return { blob, type };
});
let dataUrl = await new Promise(resolve => {
let reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.readAsDataURL(fetchResult.blob);
});
jQuery("#" + imgId).attr("href", dataUrl);
};
const inlineSvgImage = async function(imgId) {
var imgUrl = jQuery("#fq-svg-container")
.find("#" + imgId)
.attr("href");
await fetch(imgUrl, {
method: "GET",
mode: "cors",
credentials: "include"
})
.then(result => result.text())
.then(text => {
var replacedObj = jQuery("#fq-svg-container").find("#" + imgId);
var doc = new DOMParser().parseFromString(
text,
"application/xml"
);
var newObj = jQuery(doc.documentElement).replaceAll(replacedObj);
jQuery(newObj).attr("id", imgId);
});
};
const svgToRaster = svgString => {
return new Promise(resolve => {
const canvas = document.getElementById("fq-canvas");
const resolution = svgCanvas.getResolution();
var w, h;
if (fqExportMode === "thumb") {
w = fqThumbWidth;
h = Math.round((resolution.h * fqThumbWidth) / resolution.w);
} else {
w = resolution.w * parseInt(fqExportDocSize);
h = resolution.h * parseInt(fqExportDocSize);
}
canvas.width = w;
canvas.height = h;
var blob = new Blob([svgString], { type: "image/svg+xml" });
var win = window.URL || window.webkitURL || window;
var img = new Image();
var url = win.createObjectURL(blob);
img.onload = function() {
const ctx = canvas.getContext("2d");
if (fqExportDocType != "png") {
ctx.fillStyle = "white";
ctx.fillRect(0, 0, w, h);
}
ctx.drawImage(img, 0, 0, w, h);
win.revokeObjectURL(url);
// If saving image/thumbnail to FiglinQ, return blob so that it can be added to form
if (fqExportMode === "thumb") {
canvas.toBlob(function(blob) {
resolve(blob);
});
return;
} else if (fqExportMode === "download") {
var uri = canvas
.toDataURL("image/" + fqExportDocType, fqExportDocQuality)
.replace("image/" + fqExportDocType, "octet/stream");
}
// Add link to download file
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = uri;
a.download = fqExportDocFname + "." + fqExportDocType;
a.click();
window.URL.revokeObjectURL(uri);
document.body.removeChild(a);
};
img.src = url;
});
};
const svgToPdf = svgString => {
const docDimsStr = jQuery("#fq-modal-export-size-select").val();
const docDims = docDimsStr.split("x");
const doc = new PDFDocument({ size: fqExportDocSize });
const chunks = [];
doc.pipe({
// writable stream implementation
write: chunk => chunks.push(chunk),
end: () => {
const pdfBlob = new Blob(chunks, {
type: "application/octet-stream"
});
var blobUrl = window.URL.createObjectURL(pdfBlob);
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
a.href = blobUrl;
a.download = fqExportDocFname + ".pdf";
a.click();
window.URL.revokeObjectURL(blobUrl);
document.body.removeChild(a);
},
// readable stream stub iplementation
on: (event, action) => {},
once: (...args) => {},
emit: (...args) => {}
});
SVGtoPDF(doc, svgString, 0, 0, {
width: docDims[0],
height: docDims[1],
preserveAspectRatio: "xMinYMin meet"
});
doc.end();
};
const inlineRasterImageLoop = async function(imgIdArray) {
for (const imgId of imgIdArray) {
await inlineRasterImage(imgId);
}
};
const inlineSvgImageLoop = async function(imgIdArray) {
for (const imgId of imgIdArray) {
await inlineSvgImage(imgId);
}
};
const updateExportFormSizeSelect = options => {
jQuery("#fq-modal-export-size-select")
.find("option")
.remove();
jQuery.each(options, function(key, value) {
jQuery("#fq-modal-export-size-select").append(
jQuery("<option></option>")
.attr("value", value)
.text(key)
);
});
};
const updateExportFormState = () => {
let format = jQuery("#fq-modal-export-format-select").val();
if (format === "jpeg") {
jQuery('[id^="fq-modal-export-quality"]').prop("disabled", false);
jQuery('[id^="fq-modal-export-size"]').prop("disabled", false);
updateExportFormSizeSelect(fqExportFileFormats);
jQuery("#fq-modal-export-size-select").val(1);
} else if (format === "png" || format === "bmp") {
jQuery('[id^="fq-modal-export-quality"]').prop("disabled", true);
updateExportFormSizeSelect(fqExportFileFormats);
jQuery("#fq-modal-export-size-select").val(1);
} else {
jQuery('[id^="fq-modal-export-quality"]').prop("disabled", true);
updateExportFormSizeSelect(fqPdfPageSizes);
jQuery("#fq-modal-export-size-select").val(fqPdfPageSizes["A4"]);
}
};
const getAttributes = $node => {
var attrs = {};
jQuery.each($node[0].attributes, function(index, attribute) {
attrs[attribute.name] = attribute.value;
});
return attrs;
};
const adjustFigureProperty = async () => {
const property = jQuery("#fq-modal-export-property-select").val();
const propertyPath = property.split("-");
const value = parseFloat(
jQuery("#fq-modal-export-property-input").val()
);
const selElems = svgCanvas.getSelectedElems();
let i = selElems.length;
let plotSelected = false;
if (i < 1) {
alert("Please select at least one object!");
return;
}
while (i--) {
const elem = selElems[i];
const fid = jQuery(elem).data("fid");
const isPlot = jQuery(elem).hasClass("fq-plot");
const href = jQuery(elem).attr("href");
const d = new Date();
if (!elem || !isPlot) {
continue;
}
plotSelected = true;
const plotUrl = `${baseUrl}v2/plots/${fid}/content`;
await fetch(plotUrl, {
method: "GET",
mode: "cors",
credentials: "include",
headers: {
"X-CSRFToken": fqCsrfToken
}
})
.then(result => {
return result.json();
})
.then(resultJson => {
setDeep(resultJson, propertyPath, value, true);
return resultJson;
})
.then(resultUpdated => {
const updatePlotUrl = `${baseUrl}v2/plots/${fid}`;
const data = {
figure: resultUpdated
};
fetch(updatePlotUrl, {
method: "PUT",
mode: "cors",
credentials: "include",
headers: {
"X-CSRFToken": fqCsrfToken,
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
})
.then(resultUpdated => {
const updateFileUrl = `${baseUrl}v2/files/${fid}`;
const data = {
figure: resultUpdated
};
fetch(updateFileUrl, {
method: "PATCH",
mode: "cors",
credentials: "include",
headers: {
"X-CSRFToken": fqCsrfToken,
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(data)
}).then(() => {
// Update image
fetch(href, {
cache: "reload",
mode: "cors",
credentials: "include",
headers: {
"X-CSRFToken": fqCsrfToken
}
}).then(() => {
jQuery(elem).attr(
"href",
href + "#" + new Date().getTime()
);
});
});
});
}
if (plotSelected === false) {
alert("Please select at least one plot!");
}
};
const exportImageFromEditor = async () => {
// Empty temp div
// jQuery("#fq-svg-container").empty();
// Clone SVG into temp div
var svgString = svgCanvas.svgCanvasToString();
var doc = new DOMParser().parseFromString(
svgString,
"application/xml"
);
jQuery("#fq-svg-container").append(doc.documentElement);
// Inline raster images and plots
var fqElements = jQuery("#fq-svg-container").find(
".fq-image, .fq-plot"
),
imageIdArray = [],
plotIdArray = [],
attrArray = {},
curId,
newId;
if (fqElements.length) {
jQuery(fqElements).each(function() {
curId = jQuery(this).attr("id");
newId = "__" + curId;
jQuery(this).attr("id", newId);
attrArray[newId] = getAttributes(jQuery(this));
if (jQuery(this).hasClass("fq-image")) {
imageIdArray.push(newId);
} else if (jQuery(this).hasClass("fq-plot")) {
plotIdArray.push(newId);
}
});
if (imageIdArray.length) await inlineRasterImageLoop(imageIdArray);
if (plotIdArray.length) await inlineSvgImageLoop(plotIdArray);
var curAttrObj;
for (const curId in attrArray) {
var isImage = jQuery("#" + curId).hasClass("fq-image");
curAttrObj = attrArray[curId];
for (const curAttr in curAttrObj) {
if (!(curAttr == "href" && isImage)) {
jQuery("#" + curId).attr(curAttr, curAttrObj[curAttr]);
}
}
}
// Remove non-whitelisted attributes
// if( jQuery.inArray(key, svgAttrWhitelist) == -1 ) {
// jQuery("#fq-svg-container").find("#" + info.id).removeAttr(key);
// }
}
// Get the svg string
var el = document.getElementById("fq-svg-container");
var svgEl = el.firstChild;
var serializer = new XMLSerializer();
var svgStr = serializer.serializeToString(svgEl);
jQuery("#fq-svg-container").empty();
// Create image
if (fqExportDocType === "pdf") {
svgToPdf(svgStr);
} else {
if (fqExportMode === "download") {
svgToRaster(svgStr);
} else if (fqExportMode === "thumb") {
var blob = await svgToRaster(svgStr);
return blob;
}
}
};
const setModalConfirmBtnHandler = (handler, args = null) => {
jQuery("#fq-modal-confirm-btn-ok").unbind("click", onConfirmClear);
jQuery("#fq-modal-confirm-btn-ok").unbind("click", openFigure);
jQuery("#fq-modal-confirm-btn-ok").on("click", args, handler);
};
const uploadFileToFiglinQ = (
formData,
apiEndpoint,
world_readable,
updateModal,
parentId
) => {
let xFileName, successMsg, errorMsg;
if (fqExportMode === "upload") {
// Uploading a new file
xFileName = fqExportDocFname;
successMsg = " uploaded";
errorMsg = " uploaded";
} else {
// Saving existing file
xFileName = fqExportDocFname;
successMsg = " saved";
errorMsg = " saved";
}
var headers = {
"X-File-Name": xFileName,
"Plotly-World-Readable": world_readable,
"X-CSRFToken": fqCsrfToken
};
const parentUsername = parseFid(parentId, 0);
const parentIndex = parseFid(parentId, 1);
const savingIntoSharedFolder = parentUsername !== fqUsername;
if (savingIntoSharedFolder) {
headers["Target-Fid"] = parentId;
headers["Plotly-Parent"] = -1;
} else {
headers["Plotly-Parent"] = parentIndex;
}
jQuery
.ajax({
method: "POST",
url: baseUrl + "v2/external-images/" + apiEndpoint,
xhrFields: { withCredentials: true },
headers: headers,
data: formData,
processData: false,
contentType: false
})
.done(function(response) {
if (updateModal) {
jQuery("#fq-modal-refresh-btn").addClass("is-loading");
fqItemListFolder = "";
fqItemListFile = "";
if (fqExportMode === "upload") {
const userId = parseFid(response.file.fid, 0);
const fileNumId = parseFid(response.file.fid, 1);
const baseHref = `${baseUrl}~${userId}/${fileNumId}.`;
const ext = response.file.filetype === "plot" ? "svg" : "src";
var elementProps = {
width: response.file.metadata.width,
height: response.file.metadata.height,
widthOriginal: response.file.metadata.width,
heightOriginal: response.file.metadata.height,
x: 0,
y: 0,
filetype: getSvgeditFiletype(response.file.filetype),
fid: response.file.fid,
contentHref: baseHref + "embed",
imgHref: baseHref + ext
};
placeElement(elementProps);
} else {
jQuery("#fq-modal-file").removeClass("is-active");
jQuery(document).unbind("keyup", closeModalOnEscape);
var currentUrl = new URL(document.location);
currentUrl.searchParams.set("fid", response.file.fid);
window.history.pushState(
null,
null,
decodeURIComponent(currentUrl.href)
);
refreshModalContents();
}
}
showToast(
"File " + response.file.filename + successMsg,
"is-success"
);
if (fqExportMode !== "upload") {
fqCurrentFigData = response.file;
if (
typeof fqCurrentFigData.metadata === "string" ||
fqCurrentFigData.metadata instanceof String
)
fqCurrentFigData.metadata = JSON.parse(
fqCurrentFigData.metadata
);
}
jQuery("#fq-menu-file-save-figure")
.find("i")
.removeClass("fa-spinner fa-pulse")
.addClass("fa-save");
jQuery("#fq-modal-save-confirm-btn").removeClass("is-loading");
if (fqExportMode !== "upload")
jQuery("#fq-modal-file").removeClass("is-active");
})
.fail(function() {
jQuery("#fq-menu-file-save-figure")
.find("i")
.removeClass("fa-spinner fa-pulse")
.addClass("fa-save");
jQuery("#fq-modal-save-confirm-btn").removeClass("is-loading");
showToast("Error - file was not" + errorMsg, "is-danger");
});
};
const showModalSpinner = () => {
console.log("show");
jQuery("#fq-loading-overlay").show();
};
const hideModalSpinner = () => {
console.log("hide");
jQuery("#fq-loading-overlay").hide();
};
const showSaveFigureAsDialog = () => {
if (fqCurrentFigData) {
var fName = fqCurrentFigData.filename.replace(/\.[^/.]+$/, "");
jQuery("#fq-modal-save-name-input").val(fName);
jQuery("#fq-modal-save-confirm-btn").prop("disabled", false);
} else {
jQuery("#fq-modal-save-name-input").val("");
}
fqModalFileTabMode = "my";
prepareFileModal("saveFigureAs");
refreshModalContents();
};
const createUnitMap = () => {
// Get correct em/ex values by creating a temporary SVG.
const svg = document.createElementNS(NSSVG, "svg");
document.body.append(svg);
const rect = document.createElementNS(NSSVG, "rect");
rect.setAttribute("width", "1em");
rect.setAttribute("height", "1ex");
rect.setAttribute("x", "1in");
svg.append(rect);
const bb = rect.getBBox();
svg.remove();
const inch = bb.x;
_typeMap = {
em: bb.width,
ex: bb.height,
in: inch,
cm: inch / 2.54,
mm: inch / 25.4,
pt: inch / 72,
pc: inch / 6,
px: 1,
"%": 0
};
};
const updateMarginsSpacingInputs = () => {
// Update margin/spacing unit and values
let curUnit = svgEditor.configObj.curConfig.baseUnit;
var mlPx = fqDefaultMargins.left * _typeMap["mm"];
var mtPx = fqDefaultMargins.top * _typeMap["mm"];
var mrPx = fqDefaultMargins.right * _typeMap["mm"];
var mbPx = fqDefaultMargins.bottom * _typeMap["mm"];
var shPx = fqDefaultSpacing.horizontal * _typeMap["mm"];
var svPx = fqDefaultSpacing.vertical * _typeMap["mm"];
var mlTarget = Math.round((mlPx / _typeMap[curUnit]) * 100) / 100;
var mtTarget = Math.round((mtPx / _typeMap[curUnit]) * 100) / 100;
var mrTarget = Math.round((mrPx / _typeMap[curUnit]) * 100) / 100;
var mbTarget = Math.round((mbPx / _typeMap[curUnit]) * 100) / 100;
var shTarget = Math.round((shPx / _typeMap[curUnit]) * 100) / 100;
var svTarget = Math.round((svPx / _typeMap[curUnit]) * 100) / 100;
jQuery(".margin-unit").html(curUnit);
jQuery("#fq-content-add-magin-left").val(mlTarget);
jQuery("#fq-content-add-magin-top").val(mtTarget);
jQuery("#fq-content-add-magin-right").val(mrTarget);
jQuery("#fq-content-add-magin-bottom").val(mbTarget);
jQuery("#fq-content-add-spacing-horizontal").val(shTarget);
jQuery("#fq-content-add-spacing-vertical").val(svTarget);
};
const prepareFileModal = (mode, launchModal = true) => {
showModalSpinner();
var elements = [];
var heading = "";
jQuery(".fq-modal-file-tab").removeClass("is-active");
switch (mode) {
case "openFigure":
elements.hide = ".modal-action-panel, .fq-modal-file-tab";
elements.reveal =
".figure-open-panel, #fq-modal-file-tab-my, #fq-modal-file-tab-shared";
elements.disable = "#fq-modal-files-open-figure-confirm";
elements.activate = "#fq-modal-file-tab-my";
heading = "Open figure";
fqModalMode = "openFigure";
break;
case "saveFigure":
break;
case "saveFigureAs":
elements.hide =
".modal-action-panel, .fq-modal-file-tab, #fq-modal-file-search-block, #fq-modal-file-tab-preselected";
elements.reveal =
".file-save-panel, #fq-modal-file-tab-my, #fq-modal-file-tab-shared";
elements.disable = "";
elements.activate = "#fq-modal-file-tab-my";
heading = "Save figure as";
fqModalMode = "saveFigure";
break;
case "importLocalContent":
elements.hide =
".modal-action-panel, .fq-modal-file-tab, #fq-modal-file-search-block, #fq-modal-file-tab-preselected, #fq-modal-file-tab-shared";
elements.reveal = ".file-upload-panel, #fq-modal-file-tab-my";
elements.disable = "#fq-modal-upload-confirm-btn";
elements.activate = "#fq-modal-file-tab-my";
heading = "Select destination folder in FiglinQ";
fqModalMode = "upload";
fqExportMode = "upload";
break;
case "addFiglinqContent":
elements.hide =
".modal-action-panel, .fq-modal-file-tab, #fq-modal-file-tab-preselected";
elements.reveal =
".content-add-panel, .content-add-options-panel, #fq-modal-file-search-block, #fq-modal-file-tab-my, #fq-modal-file-tab-shared";
if (fqItemListPreselected) {
elements.reveal += ", #fq-modal-file-tab-preselected";
}
elements.disable = "#fq-modal-add-confirm-btn";
elements.activate = "#fq-modal-file-tab-my";
heading = "Select content to add to this figure";
fqModalMode = "addContent";
updateMarginsSpacingInputs();
break;
case "addFiglinqPreselectedContent":
elements.hide =
".modal-action-panel, #fq-modal-file-panel-breadcrumb";
elements.reveal =
".content-add-panel, .content-add-options-panel, #fq-modal-file-tab-my, #fq-modal-file-tab-shared";
elements.disable = "#fq-modal-add-confirm-btn";
elements.activate = "#fq-modal-file-tab-preselected";
heading = "Select content to add to this figure";
fqModalMode = "addContent";
updateMarginsSpacingInputs();
break;
default:
break;
}
jQuery("#file-panel-heading").html(heading);
jQuery(elements.hide).addClass("is-hidden");
jQuery(elements.disable).prop("disabled", true);
jQuery(elements.reveal).removeClass("is-hidden");
jQuery(elements.activate).addClass("is-active");
if (launchModal) jQuery("#fq-modal-file").addClass("is-active");
};
const scaleElement = (
fixedDim,
elemWidth,
elemHeight,
refWidth,
refHeight
) => {
var scaledDims = {};
if (fixedDim === "width") {
scaledDims.width = refWidth;
scaledDims.height = Math.round((elemHeight * refWidth) / elemWidth);
} else if (fixedDim === "height") {
scaledDims.width = Math.round((elemWidth * refHeight) / elemHeight);
scaledDims.height = refHeight;
}
return scaledDims;
};
const getSvgeditFiletype = filetype => {
switch (filetype) {
case "external_image":
return "image";
case "plot":
return "plot";
default:
return false;
}
};
const adjustStyles = () => {
var style, ids;
// Top panel input labels
ids = [
"selected_x",
"selected_y",
"rect_width",
"rect_height",
"path_node_x",
"path_node_y",
"starNumPoints",
"RadiusMultiplier",
"radialShift",
"ellipse_cx",
"ellipse_cy",
"ellipse_rx",
"ellipse_ry",
"circle_cx",
"circle_cy",
"circle_r",
"line_x1",
"line_x2",
"line_y1",
"line_y2",
"polySides",
"image_width",
"image_height"
];
ids.forEach(id => {
style = document.createElement("style");
style.innerHTML =
"#label{ top: 4px; margin-right: 2px; margin-left: 2px; font-size: 12px; text-transform: capitalize;} #label:after{ content: ':' }";
document.getElementById(id).shadowRoot.appendChild(style);
});
// Top panel position input
style = document.createElement("style");
style.innerHTML =
"elix-dropdown-list{ margin-left: 4px; margin-right: 4px} elix-dropdown-list:hover{ cursor: pointer; }";
document
.getElementById("tool_position")
.shadowRoot.appendChild(style);
// Symbol library menu
style = document.createElement("style");
style.innerHTML =
".menu-item{background-color: var(--main-bg-color); color: white; text-transform: capitalize;} .menu-item:hover{ cursor: pointer; } .image-lib{background-color: var(--main-bg-color)}";
document
.getElementById("tool_shapelib")
.shadowRoot.appendChild(style);
// Dropdowns
ids = ["seg_type", "tool_font_family"];
ids.forEach(id => {
style = document.createElement("style");
style.innerHTML =
"select{margin-top: 10px; margin-right: 4px; border: none; border-radius: 3px; cursor: pointer;} .menu-item:hover{ cursor: pointer; }";
document.getElementById(id).shadowRoot.appendChild(style);
});
};
const getPlotProp = (element, propName, defaultPropValue) => {
// If property is not defined in figure layout, look in template
return element.figure.layout.hasOwnProperty(propName)
? element.figure.layout[propName]
: element.figure.layout.template.layout.hasOwnProperty(propName)
? element.figure.layout.template.layout[propName]
: defaultPropValue;
};
jQuery(document).on("change", "#fq-file-upload-input", () => {
var fileName = jQuery("#fq-file-upload-input")[0].files.length
? jQuery("#fq-file-upload-input")[0].files[0].name
: false;
if (fileName) {
jQuery("#fq-modal-upload-confirm-btn").prop("disabled", false);
jQuery("#fq-file-upload-input-label").html(fileName);
} else {
jQuery("#fq-modal-upload-confirm-btn").prop("disabled", true);
}
});
jQuery(document).on("click", "#fq-modal-upload-confirm-btn", e => {
e.target.blur();
if (!jQuery("#fq-file-upload-input")[0].files.length) {
showToast("Please select the file first!", "is-warning");
return;
}
const apiEndpoint = "upload";
var world_readable = jQuery("#fq-file-upload-world-readable").val();
var imageFile = jQuery("#fq-file-upload-input")[0].files[0];
fqExportDocFname = jQuery("#fq-file-upload-input-label").html();
fqExportMode = "upload";
var formData = new FormData();
formData.append("files", imageFile);
jQuery("#fq-modal-file").removeClass("is-active");
uploadFileToFiglinQ(
formData,
apiEndpoint,
world_readable,
true,
fqSelectedFolderId[fqModalFileTabMode]
);
});
jQuery(document).on("change", "#fq-modal-export-format-select", () => {
updateExportFormState();
});
jQuery(document).on("change", "#fq-doc-baseunit", e => {
let curUnit = svgEditor.configObj.curConfig.baseUnit;
let targetUnit = jQuery(e.target).val();
var w = jQuery("#fq-doc-setup-width").val();
var h = jQuery("#fq-doc-setup-height").val();
var wNum = w.match(/\d+/)[0];
var hNum = h.match(/\d+/)[0];
var wToPx = wNum * _typeMap[curUnit];
var wToTargetUnit =
Math.round((wToPx / _typeMap[targetUnit]) * 100) / 100;
var hToPx = hNum * _typeMap[curUnit];
var hToTargetUnit =
Math.round((hToPx / _typeMap[targetUnit]) * 100) / 100;
svgEditor.configObj.curConfig.baseUnit = targetUnit;
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
// Update inputs
jQuery("#fq-doc-setup-width").val("" + wToTargetUnit + targetUnit);
jQuery("#fq-doc-setup-height").val("" + hToTargetUnit + targetUnit);
jQuery("#fq-doc-size").val("");
});
jQuery(document).on("change", "#fq-doc-setup-width", e => {
jQuery("#fq-doc-size").val("");
var w = jQuery(e.target).val();
var resolution = svgEditor.svgCanvas.getResolution();
var x = w;
svgEditor.svgCanvas.setResolution(x, resolution.h);
});
jQuery(document).on("change", "#fq-doc-setup-height", e => {
jQuery("#fq-doc-size").val("");
var h = jQuery(e.target).val();
var resolution = svgEditor.svgCanvas.getResolution();
var y = h;
svgEditor.svgCanvas.setResolution(resolution.w, y);
});
jQuery(document).on("change", "#fq-doc-size", e => {
var w,
h,
val = jQuery(e.target).val();
if (val) {
const baseUnit = svgEditor.configObj.curConfig.baseUnit;
var [w, h] = val.split("x");
w = w / _typeMap[baseUnit] + baseUnit;
h = h / _typeMap[baseUnit] + baseUnit;
svgEditor.svgCanvas.setResolution(w, h);
jQuery("#fq-doc-setup-width").val(w);
jQuery("#fq-doc-setup-height").val(h);
}
});
jQuery(document).on("click", "#fq-menu-file-export", () => {
jQuery("#fq-modal-export").addClass("is-active");
});
jQuery(document).on("click", "#fq-menu-object-adjust", () => {
jQuery("#fq-modal-adjust").addClass("is-active");
});
jQuery(document).on("click", ".fq-modal-export-quality", e => {
const incr = parseInt(jQuery(e.target).data("increment"));
var value = parseInt(jQuery("#fq-modal-export-quality-input").val());
var newValue = value + incr;
if (newValue > 100) newValue = 100;
if (newValue < 10) newValue = 10;
jQuery("#fq-modal-export-quality-input").val(newValue);
});
jQuery(document).on("click", "#fq-modal-btn-confirm-save-figure", e => {
jQuery("#fq-modal-import-newfig").removeClass("is-active");
showSaveFigureAsDialog();
});
jQuery(document).on("click", ".navbar-dropdown .navbar-item", e => {
e.target.blur();
jQuery(e.target)
.parents(".navbar-item.has-dropdown")
.removeClass("is-hoverable");
setTimeout(function() {
jQuery(e.target)
.parents(".navbar-item.has-dropdown")
.addClass("is-hoverable");
}, 100);
});
jQuery(document).on(
"click",
"#fq-menu-file-import-local-content",
() => {
fqModalFileTabMode = "my";
prepareFileModal("importLocalContent");
refreshModalContents();
}
);
jQuery(document).on("click", "#fq-modal-adjust-btn-adjust", async e => {
jQuery(e.currentTarget).addClass("is-loading");
await adjustFigureProperty();
jQuery(e.currentTarget).removeClass("is-loading");
});
jQuery(document).on("click", "#fq-modal-export-btn-export", async e => {
jQuery(e.currentTarget).addClass("is-loading");
fqExportMode = "download";
fqExportDocType = jQuery("#fq-modal-export-format-select").val();
fqExportDocQuality =
parseFloat(jQuery("#fq-modal-export-quality-input").val()) / 100;
fqExportDocFname = jQuery("#fq-modal-export-fname-input").val();
if (fqExportDocType == "pdf") {
fqExportDocSize = jQuery(
"#fq-modal-export-size-select option:selected"
).text();
} else {
fqExportDocSize = parseInt(
jQuery("#fq-modal-export-size-select").val()
);
}
await exportImageFromEditor();
jQuery(e.currentTarget).removeClass("is-loading");
});
jQuery(document).on("focusout", "#fq-modal-export-quality-input", e => {
var newValue = parseInt(jQuery(e.target).val());
if (newValue > 100) newValue = 100;
if (newValue < 10) newValue = 10;
if (isNaN(newValue)) newValue = 80;
jQuery(e.target).val(newValue);
});
jQuery(document).on("click", "#fq-menu-view-show-grid", () => {
jQuery("#view_grid").click();
let showGrid = svgEditor.configObj.curConfig.showGrid;
if (showGrid) {
jQuery("#fq-menu-view-show-grid")
.find("i")
.removeClass("fa-square")
.addClass("fa-check-square");
} else {
jQuery("#fq-menu-view-show-grid")
.find("i")
.addClass("fa-square")
.removeClass("fa-check-square");
}
});
jQuery(document).on("click", "#fq-menu-view-show-rulers", () => {
let showRulers = svgEditor.configObj.curConfig.showRulers;
if (!showRulers) {
jQuery("#fq-menu-view-show-rulers")
.find("i")
.removeClass("fa-square")
.addClass("fa-check-square");
} else {
jQuery("#fq-menu-view-show-rulers")
.find("i")
.addClass("fa-square")
.removeClass("fa-check-square");
}
svgEditor.configObj.curConfig.showRulers = !showRulers;
svgEditor.rulers.display(!showRulers);
});
jQuery(document).on(
"click",
"#fq-menu-file-document-properties",
() => {
jQuery("#fq-doc-size").val("");
const baseUnit = svgEditor.configObj.curConfig.baseUnit;
const resolution = svgEditor.svgCanvas.getResolution();
jQuery("#fq-doc-baseunit").val(baseUnit);
const gridSnappingOn = svgEditor.configObj.curConfig.gridSnapping;
const gridSnappingStep = svgEditor.configObj.curConfig.snappingStep;
jQuery("#fq-doc-setup-snapping-enabled").prop(
"checked",
gridSnappingOn
);
jQuery("#fq-doc-setup-snapping-step").val(gridSnappingStep);
jQuery("#fq-doc-setup-width").val(
resolution.w / _typeMap[baseUnit] + baseUnit
);
jQuery("#fq-doc-setup-height").val(
resolution.h / _typeMap[baseUnit] + baseUnit
);
jQuery("#fq-modal-doc-setup").addClass("is-active");
}
);
jQuery(document).on("change", "#fq-doc-setup-snapping-enabled", e => {
const gridSnappingOn = jQuery(e.target).prop("checked");
svgEditor.configObj.curConfig.gridSnapping = gridSnappingOn;
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
});
jQuery(document).on("keyup", "#fq-doc-setup-snapping-step", e => {
const snappingStep = parseInt(jQuery(e.target).val());
svgEditor.configObj.curConfig.snappingStep = snappingStep;
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
});
jQuery(document).on("click", "#fq-doc-setup-save-btn", () => {
const predefined = jQuery("#fq-doc-size").val();
const gridSnappingStep = parseInt(
jQuery("#fq-doc-setup-snapping-step").val()
);
const gridSnappingOn = jQuery("#fq-doc-setup-snapping-enabled").prop(
"checked"
);
const w =
predefined === "fit" ? "fit" : jQuery("#fq-doc-setup-width").val();
const h =
predefined === "fit" ? "fit" : jQuery("#fq-doc-setup-height").val();
const baseunit = jQuery("#fq-doc-baseunit").val();
jQuery("#fq-modal-doc-setup").removeClass("is-active");
if (w !== "fit" && !isValidUnit("width", w)) {
showToast("Invalid width unit!", "is-danger");
return;
}
if (h !== "fit" && !isValidUnit("height", h)) {
showToast("Invalid height unit!", "is-danger");
return;
}
if (!svgCanvas.setResolution(w, h)) {
showToast("No content to fit!", "is-danger");
}
svgEditor.configObj.curConfig.baseUnit = baseunit;
svgEditor.configObj.curConfig.gridSnapping = gridSnappingOn;
svgEditor.configObj.curConfig.snappingStep = gridSnappingStep;
svgCanvas.setConfig(svgEditor.configObj.curConfig);
svgEditor.updateCanvas();
});
jQuery(document).on("click", ".fq-modal-cancel-btn", () => {
jQuery(".modal").removeClass("is-active");
});
jQuery(document).on("click", ".fq-modal-file-tab", e => {
showModalSpinner();
jQuery(".fq-modal-file-tab").removeClass("is-active");
jQuery("#fq-modal-add-confirm-btn, #col_select").prop(
"disabled",
true
);
jQuery(e.currentTarget).addClass("is-active");
fqModalFileTabMode = jQuery(e.currentTarget).data("mode");
if (fqModalFileTabMode == "shared") {
jQuery("#fq-modal-file-search-wrapper").prop("disabled", true);
} else if (fqModalFileTabMode == "my") {
jQuery("#fq-modal-file-search-wrapper").prop("disabled", false);
} else if (fqModalFileTabMode == "preselected") {
jQuery("#fq-modal-file-search-wrapper").prop("disabled", true);
jQuery("#fq-modal-file-panel-breadcrumb").addClass("is-hidden");
refreshModalContents(fqItemListPreselected.fids);
return;
}
(fqSelectedFolderId = {
my: false,
shared: false,
preselected: false
}),
refreshModalContents();
});
jQuery(document).on("click", "#fq-modal-refresh-btn", () => {
showModalSpinner();
refreshModalContents();
});
jQuery(document).on(
"click",
".fq-modal-plot-item, .fq-modal-image-item, .fq-modal-figure-item",
e => {
if (fqModalMode === "saveFigure") {
if (jQuery(e.target).hasClass("fq-modal-figure-item")) {
jQuery(
".fq-modal-plot-item, .fq-modal-image-item, .fq-modal-figure-item"
).removeClass("is-active");
jQuery(e.target).addClass("is-active");
var text = jQuery(e.target)
.find(".fq-list-item-text")
.html();
if (text.toLowerCase().endsWith(".svg")) {
text = text.replace(/\.[^/.]+$/, "");
jQuery("#fq-modal-save-name-input").val(text);
jQuery("#fq-modal-save-confirm-btn").prop("disabled", false);
}
}
return;
}
if (fqModalMode === "openFigure") {
jQuery(
".fq-modal-plot-item, .fq-modal-image-item, .fq-modal-figure-item"
).removeClass("is-active");
jQuery(e.target).addClass("is-active");
jQuery("#fq-modal-files-open-figure-confirm").prop(
"disabled",
false
);
return;
}
if (jQuery(e.target).hasClass("is-active")) {
jQuery(e.target).removeClass("is-active");
} else {
jQuery(e.target).addClass("is-active");
}
var activePresent = false;
var activeList = [];
jQuery(
".fq-modal-plot-item, .fq-modal-image-item, .fq-modal-figure-item"
).each(function() {
if (jQuery(this).hasClass("is-active")) {
activePresent = true;
activeList.push(jQuery(this).data("fid"));
}
});
if (activePresent) {
jQuery("#fq-modal-add-confirm-btn").prop("disabled", false);
jQuery("#fq-modal-add-confirm-btn").data(
"selectedFid",
activeList
);
if (activeList.length > 1) {
jQuery("#col_select").prop("disabled", false);
} else {
jQuery("#col_select").prop("disabled", true);
}
} else {
jQuery("#fq-modal-add-confirm-btn").prop("disabled", true);
}
}
);
jQuery(document).on("click", ".fq-modal-folder-item", e => {
const dataFid = jQuery(e.currentTarget)
.data("fid")
.toString();
const fname = jQuery(e.target)
.text()
.trim();
fqSelectedFolderId[fqModalFileTabMode] = dataFid;
updateBreadcrumb(dataFid, fname);
fqItemListFolder = "";
fqItemListFile = "";
updateItemList(dataFid, 1);
jQuery("#fq-modal-add-confirm-btn").prop("disabled", true);
jQuery("#fq-modal-refresh-btn").addClass("is-loading");
});
jQuery(document).on("click", "#fq-menu-interact-switch", e => {
e.target.blur();
var checked = jQuery("#fq-menu-interact-switch").is(":checked");
if (checked) {
setInteractiveOn();
} else {
setInteractiveOff();
}
});
jQuery(document).on("click", "#fq-modal-add-confirm-btn", e => {
e.target.blur();
svgCanvas.clearSelection();
jQuery(e.target).addClass("is-loading");
const selector =
".fq-modal-plot-item.is-active, .fq-modal-image-item.is-active, .fq-modal-figure-item.is-active";
const selectedItems = getSortedElems(selector, "data-index");
const columnNumber = parseInt(jQuery("#col_select").val());
var margins = {
left: parseFloat(jQuery("#fq-content-add-magin-left").val()),
top: parseFloat(jQuery("#fq-content-add-magin-top").val()),
right: parseFloat(jQuery("#fq-content-add-magin-right").val()),
bottom: parseFloat(jQuery("#fq-content-add-magin-bottom").val())
};
var spacing = {
horizontal: parseFloat(
jQuery("#fq-content-add-spacing-horizontal").val()
),
vertical: parseFloat(
jQuery("#fq-content-add-spacing-vertical").val()
)
};
// Calculate margins/spacing in pixels
let curUnit = svgEditor.configObj.curConfig.baseUnit;
if (curUnit !== "px") {
margins.left = margins.left * _typeMap[curUnit];
margins.top = margins.top * _typeMap[curUnit];
margins.right = margins.right * _typeMap[curUnit];
margins.bottom = margins.bottom * _typeMap[curUnit];
spacing.horizontal = spacing.horizontal * _typeMap[curUnit];
spacing.vertical = spacing.vertical * _typeMap[curUnit];
}
var elementProps = [];
jQuery.each(selectedItems, index => {
elementProps[index] = {
fid: jQuery(selectedItems[index]).data("fid"),
endpoint:
jQuery(selectedItems[index]).data("ftype") == "plot"
? "plots"
: "files"
};
});
const fixedDim = "height";
var pageDims = svgEditor.svgCanvas.getResolution();
// Get information about all elements
var actions = elementProps.map(function(prop) {
return getFileDataFromFiglinQ(prop.fid, prop.endpoint);
});
var results = Promise.all(actions); // pass array of promises
results.then(data => {
// Default plot width / height
const wDefault = 400;
const hDefault = 360;
// Reference plot width / height
var wRef = wDefault;
var hRef = hDefault;
// If there is at least 1 plot, use it as size reference
var refPlotIndex = false;
data.some((element, index) => {
// Get dimensions of the first selected plot, either from layout or from template (if not defined in layout)
const userId = parseFid(element.fid, 0);
const fileNumId = parseFid(element.fid, 1);
const baseHref = `${baseUrl}~${userId}/${fileNumId}.`;
var w, h;
if (element.filetype == "plot") {
w = getPlotProp(element, "width", wRef);
h = getPlotProp(element, "height", hRef);
// Set dimensions of the first plot as reference
if (refPlotIndex === false) {
refPlotIndex = index;
wRef = w;
hRef = h;
}
data[index].imgHref = baseHref + "svg";
data[index].svgeditFiletype = "plot";
} else if (element.filetype == "external_image") {
w = element.metadata.width;
h = element.metadata.height;
data[index].imgHref = baseHref + "src";
data[index].svgeditFiletype = "image";
}
data[index].width = w ? w : wDefault;
data[index].height = h ? h : hDefault;
data[index].contentHref = baseHref + "embed";
});
// Find the maximum width of all elements after applying the multi-column layout
var x = 0;
var y = 0;
var curColumn = 1;
const pageWidthUsable =
pageDims.w -
margins.right -
margins.left -
spacing.horizontal * (columnNumber - 1);
var maxX = pageWidthUsable; // Only scale if layout is wider than the page minus margins, otherwise keep original dimensions
data.some(element => {
// Scale all elements to appropriate height
const scaledDims = scaleElement(
fixedDim,
element.width,
element.height,
wRef,
hRef
);
element.widthScaled = scaledDims.width;
element.heightScaled = scaledDims.height;
x += scaledDims.width;
maxX = Math.max(maxX, x);
curColumn += 1;
if (curColumn > columnNumber) {
curColumn = 1;
x = 0;
y += hRef + spacing.vertical;
}
});
// Calculate scaling page factor
const pageScaleFactor = pageWidthUsable / maxX;
const hRefScaled = hRef * pageScaleFactor;
// Set initial positioning, including margins
curColumn = 1;
x = margins.left;
y = margins.top;
// Finally, place new elements and add letters
const addLetters = jQuery("#fq-add-panel-letters").val();
data.some((element, index) => {
element.widthScaled = element.widthScaled * pageScaleFactor;
element.heightScaled = element.heightScaled * pageScaleFactor;
var elementProps = {
width: element.widthScaled,
height: element.heightScaled,
widthOriginal: element.width,
heightOriginal: element.height,
x: x,
y: y,
filetype: element.svgeditFiletype,
fid: element.fid,
contentHref: element.contentHref,
imgHref: element.imgHref
};
placeElement(elementProps);
if (addLetters) {
let letter = alphabet[index];
if (addLetters === "lower") {
letter = letter.toLowerCase();
}
const attr = {
x: x - 6,
y: y - 6,
id: svgCanvas.getNextId(),
fill: "#000000",
"stroke-width": "0",
"font-size": "14",
"font-family": "Sans-serif",
"text-anchor": "middle",
"xml:space": "preserve",
opacity: 1
};
placeTextElement(attr, letter);
}
x += element.widthScaled + spacing.horizontal;
curColumn += 1;
if (curColumn > columnNumber) {
curColumn = 1;
x = margins.left;
y += hRefScaled + spacing.vertical;
}
});
jQuery(e.target).removeClass("is-loading");
jQuery("#fq-modal-file").removeClass("is-active");
jQuery(document).unbind("keyup", closeModalOnEscape);
setTimeout(function() {
svgEditor.zoomChanged(window, "layer");
}, 500);
});
});
jQuery(document).on(
"click",
"#fq-modal-file-search-icon.fa-times-circle",
e => {
jQuery("#fq-modal-file-search-input")
.val("")
.keyup();
fqSearchMode = false;
}
);
jQuery(document).on("keyup", "#fq-modal-file-search-input", e => {
refreshModalContents();
});
jQuery(document).on("keyup", "#fq-modal-save-name-input", e => {
let val = jQuery(e.target).val();
if (val.length) {
jQuery("#fq-modal-save-confirm-btn").prop("disabled", false);
} else {
jQuery("#fq-modal-save-confirm-btn").prop("disabled", true);
}
});
jQuery(document).on(
"click",
"#fq-modal-files-open-figure-confirm",
() => {
jQuery("#fq-modal-file").removeClass("is-active");
jQuery(document).unbind("keyup", closeModalOnEscape);
jQuery("#fq-modal-confirm-btn-ok").html("Open figure");
jQuery("#fq-modal-confirm").addClass("is-active");
let fid = jQuery(".fq-modal-figure-item.is-active").data("fid");
setModalConfirmBtnHandler(openFigure, { fid: fid });
}
);
jQuery(document).on("click", "#fq-menu-file-new-figure", () => {
jQuery("#fq-modal-confirm-btn-ok").html("New figure");
setModalConfirmBtnHandler(onConfirmClear);
jQuery("#fq-modal-confirm").addClass("is-active");
});
jQuery(document).on(
"click",
"#fq-modal-save-confirm-btn",
async event => {
jQuery("#fq-modal-save-confirm-btn").addClass("is-loading");
event.target.blur();
fqExportMode = "thumb";
fqExportDocType = "png";
fqExportDocFname = jQuery("#fq-modal-save-name-input").val();
fqExportDocSize = parseInt(
jQuery("#fq-modal-export-size-select").val()
);
const world_readable = jQuery("#file_upload_world_readable").val();
// TODO *properly* check if file name exists via API
let replacedFid,
nameExists = false;
jQuery(".fq-modal-image-item, .fq-modal-figure-item").each(
function() {
if (
jQuery(this)
.find(".fq-list-item-text")
.html() ==
fqExportDocFname + ".svg"
) {
nameExists = true;
replacedFid = jQuery(this).data("fid");
}
}
);
if (nameExists) {
if (!confirm("File already exists. Overwrite?")) {
jQuery("#fq-modal-save-confirm-btn").removeClass("is-loading");
return;
}
}
if (fqSelectedFolderId[fqModalFileTabMode] === "shared:-1") {
showToast(
"Please select one of the shared folders!",
"is-danger"
);
return;
}
// Clean up image URLs to remove cachebusting hashes (see adjustFigureProperty())
// resetPlotImageUrls();
const svg = getSvgFromEditor();
const apiEndpoint = "upload";
var imageBlob = new Blob([svg], { type: "image/svg+xml" });
var imageFile = new File([imageBlob], fqExportDocFname + ".svg");
var thumbBlob = await exportImageFromEditor();
var thumbFile = new File(
[thumbBlob],
fqExportDocFname + "_thumb.png"
);
var formData = new FormData();
formData.append("files", imageFile);
formData.append("thumb", thumbFile);
// Check if figure contains any linked content
var fqElements = jQuery(".fq-image, .fq-plot, .fq-figure");
const hasLinkedContent = fqElements.length > 0;
// Add metadata
var metadata = fqCurrentFigData ? fqCurrentFigData.metadata : {};
metadata.svgedit = metadata.svgedit || {};
if (hasLinkedContent) {
metadata.svgedit.haslinkedcontent = true;
} else {
metadata.svgedit.haslinkedcontent = false;
}
formData.append("metadata", JSON.stringify(metadata));
if (nameExists) formData.append("replaced_fid", replacedFid);
uploadFileToFiglinQ(
formData,
apiEndpoint,
world_readable,
true,
fqSelectedFolderId[fqModalFileTabMode]
);
}
);
jQuery(document).on("change", "#zoom", e => {
const value = jQuery(e.target).val();
switch (value) {
case "default":
return;
case "canvas":
case "selection":
case "layer":
case "content":
svgEditor.zoomChanged(window, value);
break;
default: {
const zoomlevel = Number(value) / 100;
if (zoomlevel < 0.001) {
value = 0.1;
return;
}
const zoom = svgCanvas.getZoom();
const { workarea } = svgEditor;
svgEditor.zoomChanged(
window,
{
width: 0,
height: 0,
// center pt of scroll position
x:
(workarea.scrollLeft +
parseFloat(
getComputedStyle(workarea, null).width.replace("px", "")
) /
2) /
zoom,
y:
(workarea.scrollTop +
parseFloat(
getComputedStyle(workarea, null).height.replace(
"px",
""
)
) /
2) /
zoom,
zoom: zoomlevel
},
true
);
}
}
jQuery(e.target).val("default");
});
jQuery(document).on("click", ".fq-menu-add-content-btn", () => {
setInteractiveOff();
prepareFileModal("addFiglinqContent");
fqModalFileTabMode = "my";
refreshModalContents();
});
jQuery(document).on(
"click",
"#fq-menu-file-save-figure",
async event => {
setInteractiveOff();
if (!fqCurrentFigData) {
// New figure >> show save as dialog
showSaveFigureAsDialog();
return;
}
jQuery("#fq-menu-file-save-figure")
.find("i")
.removeClass("fa-save")
.addClass("fa-spinner fa-pulse");
event.target.blur();
fqExportMode = "thumb";
fqExportDocType = "png";
fqExportDocFname = fqCurrentFigData.filename;
fqSelectedFolderId[fqModalFileTabMode] =
parseFid(fqCurrentFigData.fid, 0) + ":" + fqCurrentFigData.parent;
const world_readable = fqCurrentFigData.world_readable;
const replacedFid = fqCurrentFigData.fid;
// Clean up image URLs to remove cachebusting hashes (see adjustFigureProperty())
// resetPlotImageUrls();
const svg = getSvgFromEditor();
const apiEndpoint = "upload";
const imageBlob = new Blob([svg], {
type: "image/svg+xml"
});
const imageFile = new File([imageBlob], fqExportDocFname + ".svg");
const thumbBlob = await exportImageFromEditor();
const thumbFile = new File(
[thumbBlob],
fqExportDocFname + "_thumb.svg"
);
var formData = new FormData();
formData.append("files", imageFile);
formData.append("thumb", thumbFile);
formData.append("replaced_fid", replacedFid);
// Check if figure contains any linked content
var fqElements = jQuery(".fq-image, .fq-plot, .fq-figure");
const hasLinkedContent = fqElements.length > 0;
var metadata = fqCurrentFigData.metadata || {};
metadata.svgedit = metadata.svgedit || {};
if (hasLinkedContent) {
metadata.svgedit.haslinkedcontent = true;
} else {
metadata.svgedit.haslinkedcontent = false;
}
formData.append("metadata", JSON.stringify(metadata));
// const fid = fqCurrentFigData.fid;
// const username = parseFid(replacedFid, 0);
// const id = parseFid(replacedFid, 1);
// const parentId =
// username === fqUsername ? fqCurrentFigData.parent : -1;
// console.log(fqCurrentFigData);
uploadFileToFiglinQ(
formData,
apiEndpoint,
world_readable,
false,
fqSelectedFolderId[fqModalFileTabMode]
);
}
);
jQuery(document).on("click", "#fq-menu-file-save-figure-as", () => {
setInteractiveOff();
showSaveFigureAsDialog();
});
jQuery(document).on("click", ".navbar-burger", () => {
jQuery(".navbar-burger").toggleClass("is-active");
jQuery(".navbar-menu").toggleClass("is-active");
});
jQuery(document).on("keyup", ".fq-margin-input", e => {
if (jQuery("#fq-content-add-link-margins").prop("checked") === true) {
jQuery(".fq-margin-input").val(jQuery(e.target).val());
}
});
jQuery(document).on("click", "#fq-menu-file-open-figure", () => {
prepareFileModal("openFigure");
refreshModalContents();
});
// Init
createUnitMap();
ensureRulesGrids();
getFqUsername();
setInteractiveOff();
upgradeUi();
adjustStyles();
loadFqFigure();
updateExportFormState();
}
};
}
};