- Breaking change: Rename config file to svgedit-config-iife.js (or for the module version, svgedit-config-es.js);
also expect one directory higher; incorporates #207 (@iuyiuy) - Breaking change: Separate `extIconsPath` from `extPath` (not copying over icons) - Breaking change: Don't reference `custom.css` in HTML; can instead be referenced in JavaScript through the config file (provided in `svgedit-config-sample-iife.js`/`svgedit-config-sample-es.js` as `svgedit-custom.css` for better namespacing); incorporates #207 (@iuyiuy) - Breaking change: Remove minified jgraduate/spinbtn files (minified within Rollup routine) - Fix: Zoom when scrolled; incorporates #169 (@AndrolGenhald), adapting for conventions; also allow avoidance when shift key pressed - Fix: Update Atom feed reference in HTML - Fixes related to recent commits: Some path and method name fixes needed, function order, missing methods, variable scope declaration, no need for DOMContentLoaded listeners in modules, switch back to non-default export, avoid trimming nullish, deal with mock tests, fix `math.matrixMultiply`, use jquery-svg where needed for array/SVG attributes; add babel-polyfill and defer script to imagelib; other misc. fixes - Enhancement: Move config-sample.js out of `editor` directory - Enhancement: For `callback`-style extensions, also provide config object; add following to that object: buildCanvgCallback, canvg, decode64, encode64, executeAfterLoads, getTypeMap, isChrome, ieIE, NS, text2xml - Enhancement: Complete ES6 modules work (extensions, locales, tests), along with Babel; make Node build routine for converting modular source to non-modular, use `loadStylesheets` for modular stylehsheet defining (but parallel loading); - Enhancement: Add `stylesheets` config for modular but parallel stylesheet loading with `@default` option for simple inclusion/exclusion of defaults (if not going with default). - Refactoring: Clean up `svg-editor.html`: consistent indents; avoid extra lbs, avoid long lines - Refactoring: Avoid embedded API adding inline JavaScript listener - Refactoring: Move layers and context code to `draw.js` - Refactoring: Move `pathActions` from `svgcanvas.js` (though preserve aliases to these methods on `canvas`) and `convertPath` from `svgutils.js` to `path.js` - Refactoring: Move `getStrokedBBox` from `svgcanvas.js` (while keeping an alias) to `svgutils.js` (as `getStrokedBBoxDefaultVisible` to avoid conflict with existing) - Docs: Remove "dependencies" comments in code except where summarizing role of jQuery or a non-obvious dependency - Refactoring/Linting: Enfore `no-extra-semi` and `quote-props` rules - Refactoring: Further avoidance of quotes on properties (as possible) - Refactoring: Use `class` in place of functions where intended as classes - Refactoring: Consistency and granularity in extensions imports - Testing: Update QUnit to 2.6.1 (node_modules) and Sinon to 5.0.8 (and add sinon-test at 2.1.3) and enforce eslint-plugin-qunit linting rules; update custom extensions - Testing: Add node-static for automating (and accessing out-of-directory contents) - Testing: Avoid HTML attributes for styling - Testing: Add npm `test` script - Testing: Comment out unused jQuery SVG test - Testing: Add test1 and svgutils_performance_test to all tests page - Testing: Due apparently to Path having not been a formal class, the test was calling it without `new`; refactored now with sufficient mock data to take into account it is a class - npm: Update devDeps - npm: Add html modules and config build to test script
This commit is contained in:
299
dist/extensions/ext-arrows.js
vendored
Normal file
299
dist/extensions/ext-arrows.js
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-arrows.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('Arrows', function (S) {
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var $ = jQuery;
|
||||
// {svgcontent} = S,
|
||||
var addElem = S.addSvgElementFromJson,
|
||||
nonce = S.nonce,
|
||||
langList = {
|
||||
en: [{ id: 'arrow_none', textContent: 'No arrow' }],
|
||||
fr: [{ id: 'arrow_none', textContent: 'Sans flèche' }]
|
||||
},
|
||||
prefix = 'se_arrow_';
|
||||
|
||||
|
||||
var selElems = void 0,
|
||||
arrowprefix = void 0,
|
||||
randomizeIds = S.randomize_ids;
|
||||
|
||||
function setArrowNonce(window, n) {
|
||||
randomizeIds = true;
|
||||
arrowprefix = prefix + n + '_';
|
||||
pathdata.fw.id = arrowprefix + 'fw';
|
||||
pathdata.bk.id = arrowprefix + 'bk';
|
||||
}
|
||||
|
||||
function unsetArrowNonce(window) {
|
||||
randomizeIds = false;
|
||||
arrowprefix = prefix;
|
||||
pathdata.fw.id = arrowprefix + 'fw';
|
||||
pathdata.bk.id = arrowprefix + 'bk';
|
||||
}
|
||||
|
||||
svgCanvas.bind('setnonce', setArrowNonce);
|
||||
svgCanvas.bind('unsetnonce', unsetArrowNonce);
|
||||
|
||||
if (randomizeIds) {
|
||||
arrowprefix = prefix + nonce + '_';
|
||||
} else {
|
||||
arrowprefix = prefix;
|
||||
}
|
||||
|
||||
var pathdata = {
|
||||
fw: { d: 'm0,0l10,5l-10,5l5,-5l-5,-5z', refx: 8, id: arrowprefix + 'fw' },
|
||||
bk: { d: 'm10,0l-10,5l10,5l-5,-5l5,-5z', refx: 2, id: arrowprefix + 'bk' }
|
||||
};
|
||||
|
||||
function getLinked(elem, attr) {
|
||||
var str = elem.getAttribute(attr);
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
var m = str.match(/\(#(.*)\)/);
|
||||
if (!m || m.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
return S.getElem(m[1]);
|
||||
}
|
||||
|
||||
function showPanel(on) {
|
||||
$('#arrow_panel').toggle(on);
|
||||
if (on) {
|
||||
var el = selElems[0];
|
||||
var end = el.getAttribute('marker-end');
|
||||
var start = el.getAttribute('marker-start');
|
||||
var mid = el.getAttribute('marker-mid');
|
||||
var val = void 0;
|
||||
if (end && start) {
|
||||
val = 'both';
|
||||
} else if (end) {
|
||||
val = 'end';
|
||||
} else if (start) {
|
||||
val = 'start';
|
||||
} else if (mid) {
|
||||
val = 'mid';
|
||||
if (mid.includes('bk')) {
|
||||
val = 'mid_bk';
|
||||
}
|
||||
}
|
||||
|
||||
if (!start && !mid && !end) {
|
||||
val = 'none';
|
||||
}
|
||||
|
||||
$('#arrow_list').val(val);
|
||||
}
|
||||
}
|
||||
|
||||
function resetMarker() {
|
||||
var el = selElems[0];
|
||||
el.removeAttribute('marker-start');
|
||||
el.removeAttribute('marker-mid');
|
||||
el.removeAttribute('marker-end');
|
||||
}
|
||||
|
||||
function addMarker(dir, type, id) {
|
||||
// TODO: Make marker (or use?) per arrow type, since refX can be different
|
||||
id = id || arrowprefix + dir;
|
||||
|
||||
var data = pathdata[dir];
|
||||
|
||||
if (type === 'mid') {
|
||||
data.refx = 5;
|
||||
}
|
||||
|
||||
var marker = S.getElem(id);
|
||||
if (!marker) {
|
||||
marker = addElem({
|
||||
element: 'marker',
|
||||
attr: {
|
||||
viewBox: '0 0 10 10',
|
||||
id: id,
|
||||
refY: 5,
|
||||
markerUnits: 'strokeWidth',
|
||||
markerWidth: 5,
|
||||
markerHeight: 5,
|
||||
orient: 'auto',
|
||||
style: 'pointer-events:none' // Currently needed for Opera
|
||||
}
|
||||
});
|
||||
var arrow = addElem({
|
||||
element: 'path',
|
||||
attr: {
|
||||
d: data.d,
|
||||
fill: '#000000'
|
||||
}
|
||||
});
|
||||
marker.appendChild(arrow);
|
||||
S.findDefs().appendChild(marker);
|
||||
}
|
||||
|
||||
marker.setAttribute('refX', data.refx);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function setArrow() {
|
||||
resetMarker();
|
||||
|
||||
var type = this.value;
|
||||
if (type === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set marker on element
|
||||
var dir = 'fw';
|
||||
if (type === 'mid_bk') {
|
||||
type = 'mid';
|
||||
dir = 'bk';
|
||||
} else if (type === 'both') {
|
||||
addMarker('bk', type);
|
||||
svgCanvas.changeSelectedAttribute('marker-start', 'url(#' + pathdata.bk.id + ')');
|
||||
type = 'end';
|
||||
dir = 'fw';
|
||||
} else if (type === 'start') {
|
||||
dir = 'bk';
|
||||
}
|
||||
|
||||
addMarker(dir, type);
|
||||
svgCanvas.changeSelectedAttribute('marker-' + type, 'url(#' + pathdata[dir].id + ')');
|
||||
S.call('changed', selElems);
|
||||
}
|
||||
|
||||
function colorChanged(elem) {
|
||||
var color = elem.getAttribute('stroke');
|
||||
var mtypes = ['start', 'mid', 'end'];
|
||||
var defs = S.findDefs();
|
||||
|
||||
$.each(mtypes, function (i, type) {
|
||||
var marker = getLinked(elem, 'marker-' + type);
|
||||
if (!marker) {
|
||||
return;
|
||||
}
|
||||
|
||||
var curColor = $(marker).children().attr('fill');
|
||||
var curD = $(marker).children().attr('d');
|
||||
if (curColor === color) {
|
||||
return;
|
||||
}
|
||||
|
||||
var allMarkers = $(defs).find('marker');
|
||||
var newMarker = null;
|
||||
// Different color, check if already made
|
||||
allMarkers.each(function () {
|
||||
var attrs = $(this).children().attr(['fill', 'd']);
|
||||
if (attrs.fill === color && attrs.d === curD) {
|
||||
// Found another marker with this color and this path
|
||||
newMarker = this;
|
||||
}
|
||||
});
|
||||
|
||||
if (!newMarker) {
|
||||
// Create a new marker with this color
|
||||
var lastId = marker.id;
|
||||
var dir = lastId.includes('_fw') ? 'fw' : 'bk';
|
||||
|
||||
newMarker = addMarker(dir, type, arrowprefix + dir + allMarkers.length);
|
||||
|
||||
$(newMarker).children().attr('fill', color);
|
||||
}
|
||||
|
||||
$(elem).attr('marker-' + type, 'url(#' + newMarker.id + ')');
|
||||
|
||||
// Check if last marker can be removed
|
||||
var remove = true;
|
||||
$(S.svgcontent).find('line, polyline, path, polygon').each(function () {
|
||||
var elem = this;
|
||||
$.each(mtypes, function (j, mtype) {
|
||||
if ($(elem).attr('marker-' + mtype) === 'url(#' + marker.id + ')') {
|
||||
remove = false;
|
||||
return remove;
|
||||
}
|
||||
});
|
||||
if (!remove) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Not found, so can safely remove
|
||||
if (remove) {
|
||||
$(marker).remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'Arrows',
|
||||
context_tools: [{
|
||||
type: 'select',
|
||||
panel: 'arrow_panel',
|
||||
title: 'Select arrow type',
|
||||
id: 'arrow_list',
|
||||
options: {
|
||||
none: 'No arrow',
|
||||
end: '---->',
|
||||
start: '<----',
|
||||
both: '<--->',
|
||||
mid: '-->--',
|
||||
mid_bk: '--<--'
|
||||
},
|
||||
defval: 'none',
|
||||
events: {
|
||||
change: setArrow
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('#arrow_panel').hide();
|
||||
// Set ID so it can be translated in locale file
|
||||
$('#arrow_list option')[0].id = 'connector_no_arrow';
|
||||
},
|
||||
addLangData: function addLangData(lang) {
|
||||
return {
|
||||
data: langList[lang]
|
||||
};
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
|
||||
var markerElems = ['line', 'path', 'polyline', 'polygon'];
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && markerElems.includes(elem.tagName)) {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
var elem = opts.elems[0];
|
||||
if (elem && (elem.getAttribute('marker-start') || elem.getAttribute('marker-mid') || elem.getAttribute('marker-end'))) {
|
||||
// const start = elem.getAttribute('marker-start');
|
||||
// const mid = elem.getAttribute('marker-mid');
|
||||
// const end = elem.getAttribute('marker-end');
|
||||
// Has marker, so see if it should match color
|
||||
colorChanged(elem);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
97
dist/extensions/ext-closepath.js
vendored
Normal file
97
dist/extensions/ext-closepath.js
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-closepath.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*
|
||||
*/
|
||||
|
||||
// This extension adds a simple button to the contextual panel for paths
|
||||
// The button toggles whether the path is open or closed
|
||||
svgEditor.addExtension('ClosePath', function () {
|
||||
var $ = jQuery;
|
||||
var selElems = void 0;
|
||||
var updateButton = function updateButton(path) {
|
||||
var seglist = path.pathSegList,
|
||||
closed = seglist.getItem(seglist.numberOfItems - 1).pathSegType === 1,
|
||||
showbutton = closed ? '#tool_openpath' : '#tool_closepath',
|
||||
hidebutton = closed ? '#tool_closepath' : '#tool_openpath';
|
||||
$(hidebutton).hide();
|
||||
$(showbutton).show();
|
||||
};
|
||||
var showPanel = function showPanel(on) {
|
||||
$('#closepath_panel').toggle(on);
|
||||
if (on) {
|
||||
var path = selElems[0];
|
||||
if (path) {
|
||||
updateButton(path);
|
||||
}
|
||||
}
|
||||
};
|
||||
var toggleClosed = function toggleClosed() {
|
||||
var path = selElems[0];
|
||||
if (path) {
|
||||
var seglist = path.pathSegList,
|
||||
last = seglist.numberOfItems - 1;
|
||||
// is closed
|
||||
if (seglist.getItem(last).pathSegType === 1) {
|
||||
seglist.removeItem(last);
|
||||
} else {
|
||||
seglist.appendItem(path.createSVGPathSegClosePath());
|
||||
}
|
||||
updateButton(path);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
name: 'ClosePath',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'closepath_icons.svg',
|
||||
buttons: [{
|
||||
id: 'tool_openpath',
|
||||
type: 'context',
|
||||
panel: 'closepath_panel',
|
||||
title: 'Open path',
|
||||
events: {
|
||||
click: function click() {
|
||||
toggleClosed();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: 'tool_closepath',
|
||||
type: 'context',
|
||||
panel: 'closepath_panel',
|
||||
title: 'Close path',
|
||||
events: {
|
||||
click: function click() {
|
||||
toggleClosed();
|
||||
}
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('#closepath_panel').hide();
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
selElems = opts.elems;
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && elem.tagName === 'path') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
615
dist/extensions/ext-connector.js
vendored
Normal file
615
dist/extensions/ext-connector.js
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-connector.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('Connector', function (S) {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var svgroot = S.svgroot,
|
||||
getNextId = S.getNextId,
|
||||
getElem = S.getElem,
|
||||
curConfig = S.curConfig,
|
||||
addElem = S.addSvgElementFromJson,
|
||||
selManager = S.selectorManager,
|
||||
connSel = '.se_connector',
|
||||
elData = $.data;
|
||||
var startX = void 0,
|
||||
startY = void 0,
|
||||
curLine = void 0,
|
||||
startElem = void 0,
|
||||
endElem = void 0,
|
||||
seNs = void 0,
|
||||
svgcontent = S.svgcontent,
|
||||
started = false,
|
||||
connections = [],
|
||||
selElems = [];
|
||||
|
||||
|
||||
var langList = {
|
||||
en: [{ id: 'mode_connect', title: 'Connect two objects' }],
|
||||
fr: [{ id: 'mode_connect', title: 'Connecter deux objets' }]
|
||||
};
|
||||
|
||||
function getBBintersect(x, y, bb, offset) {
|
||||
if (offset) {
|
||||
offset -= 0;
|
||||
bb = $.extend({}, bb);
|
||||
bb.width += offset;
|
||||
bb.height += offset;
|
||||
bb.x -= offset / 2;
|
||||
bb.y -= offset / 2;
|
||||
}
|
||||
|
||||
var midX = bb.x + bb.width / 2;
|
||||
var midY = bb.y + bb.height / 2;
|
||||
var lenX = x - midX;
|
||||
var lenY = y - midY;
|
||||
|
||||
var slope = Math.abs(lenY / lenX);
|
||||
|
||||
var ratio = void 0;
|
||||
if (slope < bb.height / bb.width) {
|
||||
ratio = bb.width / 2 / Math.abs(lenX);
|
||||
} else {
|
||||
ratio = bb.height / 2 / Math.abs(lenY);
|
||||
}
|
||||
|
||||
return {
|
||||
x: midX + lenX * ratio,
|
||||
y: midY + lenY * ratio
|
||||
};
|
||||
}
|
||||
|
||||
function getOffset(side, line) {
|
||||
var giveOffset = !!line.getAttribute('marker-' + side);
|
||||
// const giveOffset = $(line).data(side+'_off');
|
||||
|
||||
// TODO: Make this number (5) be based on marker width/height
|
||||
var size = line.getAttribute('stroke-width') * 5;
|
||||
return giveOffset ? size : 0;
|
||||
}
|
||||
|
||||
function showPanel(on) {
|
||||
var connRules = $('#connector_rules');
|
||||
if (!connRules.length) {
|
||||
connRules = $('<style id="connector_rules"></style>').appendTo('head');
|
||||
}
|
||||
connRules.text(!on ? '' : '#tool_clone, #tool_topath, #tool_angle, #xy_panel { display: none !important; }');
|
||||
$('#connector_panel').toggle(on);
|
||||
}
|
||||
|
||||
function setPoint(elem, pos, x, y, setMid) {
|
||||
var pts = elem.points;
|
||||
var pt = svgroot.createSVGPoint();
|
||||
pt.x = x;
|
||||
pt.y = y;
|
||||
if (pos === 'end') {
|
||||
pos = pts.numberOfItems - 1;
|
||||
}
|
||||
// TODO: Test for this on init, then use alt only if needed
|
||||
try {
|
||||
pts.replaceItem(pt, pos);
|
||||
} catch (err) {
|
||||
// Should only occur in FF which formats points attr as "n,n n,n", so just split
|
||||
var ptArr = elem.getAttribute('points').split(' ');
|
||||
for (var i = 0; i < ptArr.length; i++) {
|
||||
if (i === pos) {
|
||||
ptArr[i] = x + ',' + y;
|
||||
}
|
||||
}
|
||||
elem.setAttribute('points', ptArr.join(' '));
|
||||
}
|
||||
|
||||
if (setMid) {
|
||||
// Add center point
|
||||
var ptStart = pts.getItem(0);
|
||||
var ptEnd = pts.getItem(pts.numberOfItems - 1);
|
||||
setPoint(elem, 1, (ptEnd.x + ptStart.x) / 2, (ptEnd.y + ptStart.y) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
function updateLine(diffX, diffY) {
|
||||
// Update line with element
|
||||
var i = connections.length;
|
||||
while (i--) {
|
||||
var conn = connections[i];
|
||||
var line = conn.connector;
|
||||
// const {elem} = conn;
|
||||
|
||||
var pre = conn.is_start ? 'start' : 'end';
|
||||
// const sw = line.getAttribute('stroke-width') * 5;
|
||||
|
||||
// Update bbox for this element
|
||||
var bb = elData(line, pre + '_bb');
|
||||
bb.x = conn.start_x + diffX;
|
||||
bb.y = conn.start_y + diffY;
|
||||
elData(line, pre + '_bb', bb);
|
||||
|
||||
var altPre = conn.is_start ? 'end' : 'start';
|
||||
|
||||
// Get center pt of connected element
|
||||
var bb2 = elData(line, altPre + '_bb');
|
||||
var srcX = bb2.x + bb2.width / 2;
|
||||
var srcY = bb2.y + bb2.height / 2;
|
||||
|
||||
// Set point of element being moved
|
||||
var pt = getBBintersect(srcX, srcY, bb, getOffset(pre, line)); // $(line).data(pre+'_off')?sw:0
|
||||
setPoint(line, conn.is_start ? 0 : 'end', pt.x, pt.y, true);
|
||||
|
||||
// Set point of connected element
|
||||
var pt2 = getBBintersect(pt.x, pt.y, elData(line, altPre + '_bb'), getOffset(altPre, line));
|
||||
setPoint(line, conn.is_start ? 'end' : 0, pt2.x, pt2.y, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {array} [elem=selElems] Array of elements
|
||||
*/
|
||||
function findConnectors() {
|
||||
var elems = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : selElems;
|
||||
|
||||
var connectors = $(svgcontent).find(connSel);
|
||||
connections = [];
|
||||
|
||||
// Loop through connectors to see if one is connected to the element
|
||||
connectors.each(function () {
|
||||
var addThis = void 0;
|
||||
function add() {
|
||||
if (elems.includes(this)) {
|
||||
// Pretend this element is selected
|
||||
addThis = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the ends
|
||||
var parts = [];
|
||||
['start', 'end'].forEach(function (pos, i) {
|
||||
var key = 'c_' + pos;
|
||||
var part = elData(this, key);
|
||||
if (part == null) {
|
||||
part = document.getElementById(this.attributes['se:connector'].value.split(' ')[i]);
|
||||
elData(this, 'c_' + pos, part.id);
|
||||
elData(this, pos + '_bb', svgCanvas.getStrokedBBox([part]));
|
||||
} else part = document.getElementById(part);
|
||||
parts.push(part);
|
||||
}.bind(this));
|
||||
|
||||
for (var i = 0; i < 2; i++) {
|
||||
var cElem = parts[i];
|
||||
|
||||
addThis = false;
|
||||
// The connected element might be part of a selected group
|
||||
$(cElem).parents().each(add);
|
||||
|
||||
if (!cElem || !cElem.parentNode) {
|
||||
$(this).remove();
|
||||
continue;
|
||||
}
|
||||
if (elems.includes(cElem) || addThis) {
|
||||
var bb = svgCanvas.getStrokedBBox([cElem]);
|
||||
connections.push({
|
||||
elem: cElem,
|
||||
connector: this,
|
||||
is_start: i === 0,
|
||||
start_x: bb.x,
|
||||
start_y: bb.y
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateConnectors(elems) {
|
||||
// Updates connector lines based on selected elements
|
||||
// Is not used on mousemove, as it runs getStrokedBBox every time,
|
||||
// which isn't necessary there.
|
||||
findConnectors(elems);
|
||||
if (connections.length) {
|
||||
// Update line with element
|
||||
var i = connections.length;
|
||||
while (i--) {
|
||||
var conn = connections[i];
|
||||
var line = conn.connector;
|
||||
var elem = conn.elem;
|
||||
|
||||
// const sw = line.getAttribute('stroke-width') * 5;
|
||||
|
||||
var pre = conn.is_start ? 'start' : 'end';
|
||||
|
||||
// Update bbox for this element
|
||||
var bb = svgCanvas.getStrokedBBox([elem]);
|
||||
bb.x = conn.start_x;
|
||||
bb.y = conn.start_y;
|
||||
elData(line, pre + '_bb', bb);
|
||||
/* const addOffset = */elData(line, pre + '_off');
|
||||
|
||||
var altPre = conn.is_start ? 'end' : 'start';
|
||||
|
||||
// Get center pt of connected element
|
||||
var bb2 = elData(line, altPre + '_bb');
|
||||
var srcX = bb2.x + bb2.width / 2;
|
||||
var srcY = bb2.y + bb2.height / 2;
|
||||
|
||||
// Set point of element being moved
|
||||
var pt = getBBintersect(srcX, srcY, bb, getOffset(pre, line));
|
||||
setPoint(line, conn.is_start ? 0 : 'end', pt.x, pt.y, true);
|
||||
|
||||
// Set point of connected element
|
||||
var pt2 = getBBintersect(pt.x, pt.y, elData(line, altPre + '_bb'), getOffset(altPre, line));
|
||||
setPoint(line, conn.is_start ? 'end' : 0, pt2.x, pt2.y, true);
|
||||
|
||||
// Update points attribute manually for webkit
|
||||
if (navigator.userAgent.includes('AppleWebKit')) {
|
||||
var pts = line.points;
|
||||
var len = pts.numberOfItems;
|
||||
var ptArr = [];
|
||||
for (var j = 0; j < len; j++) {
|
||||
pt = pts.getItem(j);
|
||||
ptArr[j] = pt.x + ',' + pt.y;
|
||||
}
|
||||
line.setAttribute('points', ptArr.join(' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do once
|
||||
(function () {
|
||||
var gse = svgCanvas.groupSelectedElements;
|
||||
|
||||
svgCanvas.groupSelectedElements = function () {
|
||||
svgCanvas.removeFromSelection($(connSel).toArray());
|
||||
return gse.apply(this, arguments);
|
||||
};
|
||||
|
||||
var mse = svgCanvas.moveSelectedElements;
|
||||
|
||||
svgCanvas.moveSelectedElements = function () {
|
||||
var cmd = mse.apply(this, arguments);
|
||||
updateConnectors();
|
||||
return cmd;
|
||||
};
|
||||
|
||||
seNs = svgCanvas.getEditorNS();
|
||||
})();
|
||||
|
||||
// Do on reset
|
||||
function init() {
|
||||
// Make sure all connectors have data set
|
||||
$(svgcontent).find('*').each(function () {
|
||||
var conn = this.getAttributeNS(seNs, 'connector');
|
||||
if (conn) {
|
||||
this.setAttribute('class', connSel.substr(1));
|
||||
var connData = conn.split(' ');
|
||||
var sbb = svgCanvas.getStrokedBBox([getElem(connData[0])]);
|
||||
var ebb = svgCanvas.getStrokedBBox([getElem(connData[1])]);
|
||||
$(this).data('c_start', connData[0]).data('c_end', connData[1]).data('start_bb', sbb).data('end_bb', ebb);
|
||||
svgCanvas.getEditorNS(true);
|
||||
}
|
||||
});
|
||||
// updateConnectors();
|
||||
}
|
||||
|
||||
// $(svgroot).parent().mousemove(function (e) {
|
||||
// // if (started
|
||||
// // || svgCanvas.getMode() !== 'connector'
|
||||
// // || e.target.parentNode.parentNode !== svgcontent) return;
|
||||
//
|
||||
// console.log('y')
|
||||
// // if (e.target.parentNode.parentNode === svgcontent) {
|
||||
// //
|
||||
// // }
|
||||
// });
|
||||
|
||||
return {
|
||||
name: 'Connector',
|
||||
svgicons: svgEditor.curConfig.imgPath + 'conn.svg',
|
||||
buttons: [{
|
||||
id: 'mode_connect',
|
||||
type: 'mode',
|
||||
icon: svgEditor.curConfig.imgPath + 'cut.png',
|
||||
title: 'Connect two objects',
|
||||
includeWith: {
|
||||
button: '#tool_line',
|
||||
isDefault: false,
|
||||
position: 1
|
||||
},
|
||||
events: {
|
||||
click: function click() {
|
||||
svgCanvas.setMode('connector');
|
||||
}
|
||||
}
|
||||
}],
|
||||
addLangData: function addLangData(lang) {
|
||||
return {
|
||||
data: langList[lang]
|
||||
};
|
||||
},
|
||||
mouseDown: function mouseDown(opts) {
|
||||
var e = opts.event;
|
||||
startX = opts.start_x;
|
||||
startY = opts.start_y;
|
||||
var mode = svgCanvas.getMode();
|
||||
|
||||
if (mode === 'connector') {
|
||||
if (started) {
|
||||
return;
|
||||
}
|
||||
|
||||
var mouseTarget = e.target;
|
||||
|
||||
var parents = $(mouseTarget).parents();
|
||||
|
||||
if ($.inArray(svgcontent, parents) !== -1) {
|
||||
// Connectable element
|
||||
|
||||
// If child of foreignObject, use parent
|
||||
var fo = $(mouseTarget).closest('foreignObject');
|
||||
startElem = fo.length ? fo[0] : mouseTarget;
|
||||
|
||||
// Get center of source element
|
||||
var bb = svgCanvas.getStrokedBBox([startElem]);
|
||||
var x = bb.x + bb.width / 2;
|
||||
var y = bb.y + bb.height / 2;
|
||||
|
||||
started = true;
|
||||
curLine = addElem({
|
||||
element: 'polyline',
|
||||
attr: {
|
||||
id: getNextId(),
|
||||
points: x + ',' + y + ' ' + x + ',' + y + ' ' + startX + ',' + startY,
|
||||
stroke: '#' + curConfig.initStroke.color,
|
||||
'stroke-width': !startElem.stroke_width || startElem.stroke_width === 0 ? curConfig.initStroke.width : startElem.stroke_width,
|
||||
fill: 'none',
|
||||
opacity: curConfig.initStroke.opacity,
|
||||
style: 'pointer-events:none'
|
||||
}
|
||||
});
|
||||
elData(curLine, 'start_bb', bb);
|
||||
}
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
if (mode === 'select') {
|
||||
findConnectors();
|
||||
}
|
||||
},
|
||||
mouseMove: function mouseMove(opts) {
|
||||
var zoom = svgCanvas.getZoom();
|
||||
// const e = opts.event;
|
||||
var x = opts.mouse_x / zoom;
|
||||
var y = opts.mouse_y / zoom;
|
||||
|
||||
var diffX = x - startX,
|
||||
diffY = y - startY;
|
||||
|
||||
var mode = svgCanvas.getMode();
|
||||
|
||||
if (mode === 'connector' && started) {
|
||||
// const sw = curLine.getAttribute('stroke-width') * 3;
|
||||
// Set start point (adjusts based on bb)
|
||||
var pt = getBBintersect(x, y, elData(curLine, 'start_bb'), getOffset('start', curLine));
|
||||
startX = pt.x;
|
||||
startY = pt.y;
|
||||
|
||||
setPoint(curLine, 0, pt.x, pt.y, true);
|
||||
|
||||
// Set end point
|
||||
setPoint(curLine, 'end', x, y, true);
|
||||
} else if (mode === 'select') {
|
||||
var slen = selElems.length;
|
||||
while (slen--) {
|
||||
var elem = selElems[slen];
|
||||
// Look for selected connector elements
|
||||
if (elem && elData(elem, 'c_start')) {
|
||||
// Remove the "translate" transform given to move
|
||||
svgCanvas.removeFromSelection([elem]);
|
||||
svgCanvas.getTransformList(elem).clear();
|
||||
}
|
||||
}
|
||||
if (connections.length) {
|
||||
updateLine(diffX, diffY);
|
||||
}
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp(opts) {
|
||||
// const zoom = svgCanvas.getZoom();
|
||||
var e = opts.event;
|
||||
// , x = opts.mouse_x / zoom,
|
||||
// , y = opts.mouse_y / zoom,
|
||||
var mouseTarget = e.target;
|
||||
|
||||
if (svgCanvas.getMode() === 'connector') {
|
||||
var fo = $(mouseTarget).closest('foreignObject');
|
||||
if (fo.length) {
|
||||
mouseTarget = fo[0];
|
||||
}
|
||||
|
||||
var parents = $(mouseTarget).parents();
|
||||
|
||||
if (mouseTarget === startElem) {
|
||||
// Start line through click
|
||||
started = true;
|
||||
return {
|
||||
keep: true,
|
||||
element: null,
|
||||
started: started
|
||||
};
|
||||
}
|
||||
if ($.inArray(svgcontent, parents) === -1) {
|
||||
// Not a valid target element, so remove line
|
||||
$(curLine).remove();
|
||||
started = false;
|
||||
return {
|
||||
keep: false,
|
||||
element: null,
|
||||
started: started
|
||||
};
|
||||
}
|
||||
// Valid end element
|
||||
endElem = mouseTarget;
|
||||
|
||||
var startId = startElem.id,
|
||||
endId = endElem.id;
|
||||
var connStr = startId + ' ' + endId;
|
||||
var altStr = endId + ' ' + startId;
|
||||
// Don't create connector if one already exists
|
||||
var dupe = $(svgcontent).find(connSel).filter(function () {
|
||||
var conn = this.getAttributeNS(seNs, 'connector');
|
||||
if (conn === connStr || conn === altStr) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (dupe.length) {
|
||||
$(curLine).remove();
|
||||
return {
|
||||
keep: false,
|
||||
element: null,
|
||||
started: false
|
||||
};
|
||||
}
|
||||
|
||||
var bb = svgCanvas.getStrokedBBox([endElem]);
|
||||
|
||||
var pt = getBBintersect(startX, startY, bb, getOffset('start', curLine));
|
||||
setPoint(curLine, 'end', pt.x, pt.y, true);
|
||||
$(curLine).data('c_start', startId).data('c_end', endId).data('end_bb', bb);
|
||||
seNs = svgCanvas.getEditorNS(true);
|
||||
curLine.setAttributeNS(seNs, 'se:connector', connStr);
|
||||
curLine.setAttribute('class', connSel.substr(1));
|
||||
curLine.setAttribute('opacity', 1);
|
||||
svgCanvas.addToSelection([curLine]);
|
||||
svgCanvas.moveToBottomSelectedElement();
|
||||
selManager.requestSelector(curLine).showGrips(false);
|
||||
started = false;
|
||||
return {
|
||||
keep: true,
|
||||
element: curLine,
|
||||
started: started
|
||||
};
|
||||
}
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// TODO: Find better way to skip operations if no connectors are in use
|
||||
if (!$(svgcontent).find(connSel).length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (svgCanvas.getMode() === 'connector') {
|
||||
svgCanvas.setMode('select');
|
||||
}
|
||||
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && elData(elem, 'c_start')) {
|
||||
selManager.requestSelector(elem).showGrips(false);
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
// TODO: Set up context tools and hide most regular line tools
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
updateConnectors();
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
var elem = opts.elems[0];
|
||||
if (elem && elem.tagName === 'svg' && elem.id === 'svgcontent') {
|
||||
// Update svgcontent (can change on import)
|
||||
svgcontent = elem;
|
||||
init();
|
||||
}
|
||||
|
||||
// Has marker, so change offset
|
||||
if (elem && (elem.getAttribute('marker-start') || elem.getAttribute('marker-mid') || elem.getAttribute('marker-end'))) {
|
||||
var start = elem.getAttribute('marker-start');
|
||||
var mid = elem.getAttribute('marker-mid');
|
||||
var end = elem.getAttribute('marker-end');
|
||||
curLine = elem;
|
||||
$(elem).data('start_off', !!start).data('end_off', !!end);
|
||||
|
||||
if (elem.tagName === 'line' && mid) {
|
||||
// Convert to polyline to accept mid-arrow
|
||||
|
||||
var x1 = Number(elem.getAttribute('x1'));
|
||||
var x2 = Number(elem.getAttribute('x2'));
|
||||
var y1 = Number(elem.getAttribute('y1'));
|
||||
var y2 = Number(elem.getAttribute('y2'));
|
||||
var _elem = elem,
|
||||
id = _elem.id;
|
||||
|
||||
|
||||
var midPt = ' ' + (x1 + x2) / 2 + ',' + (y1 + y2) / 2 + ' ';
|
||||
var pline = addElem({
|
||||
element: 'polyline',
|
||||
attr: {
|
||||
points: x1 + ',' + y1 + midPt + x2 + ',' + y2,
|
||||
stroke: elem.getAttribute('stroke'),
|
||||
'stroke-width': elem.getAttribute('stroke-width'),
|
||||
'marker-mid': mid,
|
||||
fill: 'none',
|
||||
opacity: elem.getAttribute('opacity') || 1
|
||||
}
|
||||
});
|
||||
$(elem).after(pline).remove();
|
||||
svgCanvas.clearSelection();
|
||||
pline.id = id;
|
||||
svgCanvas.addToSelection([pline]);
|
||||
elem = pline;
|
||||
}
|
||||
}
|
||||
// Update line if it's a connector
|
||||
if (elem.getAttribute('class') === connSel.substr(1)) {
|
||||
var _start = getElem(elData(elem, 'c_start'));
|
||||
updateConnectors([_start]);
|
||||
} else {
|
||||
updateConnectors();
|
||||
}
|
||||
},
|
||||
IDsUpdated: function IDsUpdated(input) {
|
||||
var remove = [];
|
||||
input.elems.forEach(function (elem) {
|
||||
if ('se:connector' in elem.attr) {
|
||||
elem.attr['se:connector'] = elem.attr['se:connector'].split(' ').map(function (oldID) {
|
||||
return input.changes[oldID];
|
||||
}).join(' ');
|
||||
|
||||
// Check validity - the field would be something like 'svg_21 svg_22', but
|
||||
// if one end is missing, it would be 'svg_21' and therefore fail this test
|
||||
if (!/. ./.test(elem.attr['se:connector'])) {
|
||||
remove.push(elem.attr.id);
|
||||
}
|
||||
}
|
||||
});
|
||||
return { remove: remove };
|
||||
},
|
||||
toolButtonStateUpdate: function toolButtonStateUpdate(opts) {
|
||||
if (opts.nostroke) {
|
||||
if ($('#mode_connect').hasClass('tool_button_current')) {
|
||||
svgEditor.clickSelect();
|
||||
}
|
||||
}
|
||||
$('#mode_connect').toggleClass('disabled', opts.nostroke);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
127
dist/extensions/ext-eyedropper.js
vendored
Normal file
127
dist/extensions/ext-eyedropper.js
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-eyedropper.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Jeff Schiller
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('eyedropper', function (S) {
|
||||
var $ = jQuery;
|
||||
var ChangeElementCommand = S.ChangeElementCommand,
|
||||
svgCanvas = svgEditor.canvas,
|
||||
addToHistory = function addToHistory(cmd) {
|
||||
svgCanvas.undoMgr.addCommandToHistory(cmd);
|
||||
},
|
||||
currentStyle = {
|
||||
fillPaint: 'red', fillOpacity: 1.0,
|
||||
strokePaint: 'black', strokeOpacity: 1.0,
|
||||
strokeWidth: 5, strokeDashArray: null,
|
||||
opacity: 1.0,
|
||||
strokeLinecap: 'butt',
|
||||
strokeLinejoin: 'miter'
|
||||
};
|
||||
|
||||
function getStyle(opts) {
|
||||
// if we are in eyedropper mode, we don't want to disable the eye-dropper tool
|
||||
var mode = svgCanvas.getMode();
|
||||
if (mode === 'eyedropper') {
|
||||
return;
|
||||
}
|
||||
|
||||
var tool = $('#tool_eyedropper');
|
||||
// enable-eye-dropper if one element is selected
|
||||
var elem = null;
|
||||
if (!opts.multiselected && opts.elems[0] && !['svg', 'g', 'use'].includes(opts.elems[0].nodeName)) {
|
||||
elem = opts.elems[0];
|
||||
tool.removeClass('disabled');
|
||||
// grab the current style
|
||||
currentStyle.fillPaint = elem.getAttribute('fill') || 'black';
|
||||
currentStyle.fillOpacity = elem.getAttribute('fill-opacity') || 1.0;
|
||||
currentStyle.strokePaint = elem.getAttribute('stroke');
|
||||
currentStyle.strokeOpacity = elem.getAttribute('stroke-opacity') || 1.0;
|
||||
currentStyle.strokeWidth = elem.getAttribute('stroke-width');
|
||||
currentStyle.strokeDashArray = elem.getAttribute('stroke-dasharray');
|
||||
currentStyle.strokeLinecap = elem.getAttribute('stroke-linecap');
|
||||
currentStyle.strokeLinejoin = elem.getAttribute('stroke-linejoin');
|
||||
currentStyle.opacity = elem.getAttribute('opacity') || 1.0;
|
||||
// disable eye-dropper tool
|
||||
} else {
|
||||
tool.addClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'eyedropper',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'eyedropper-icon.xml',
|
||||
buttons: [{
|
||||
id: 'tool_eyedropper',
|
||||
type: 'mode',
|
||||
title: 'Eye Dropper Tool',
|
||||
key: 'I',
|
||||
events: {
|
||||
click: function click() {
|
||||
svgCanvas.setMode('eyedropper');
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
// if we have selected an element, grab its paint and enable the eye dropper button
|
||||
selectedChanged: getStyle,
|
||||
elementChanged: getStyle,
|
||||
|
||||
mouseDown: function mouseDown(opts) {
|
||||
var mode = svgCanvas.getMode();
|
||||
if (mode === 'eyedropper') {
|
||||
var e = opts.event;
|
||||
var target = e.target;
|
||||
|
||||
if (!['svg', 'g', 'use'].includes(target.nodeName)) {
|
||||
var changes = {};
|
||||
|
||||
var change = function change(elem, attrname, newvalue) {
|
||||
changes[attrname] = elem.getAttribute(attrname);
|
||||
elem.setAttribute(attrname, newvalue);
|
||||
};
|
||||
|
||||
if (currentStyle.fillPaint) {
|
||||
change(target, 'fill', currentStyle.fillPaint);
|
||||
}
|
||||
if (currentStyle.fillOpacity) {
|
||||
change(target, 'fill-opacity', currentStyle.fillOpacity);
|
||||
}
|
||||
if (currentStyle.strokePaint) {
|
||||
change(target, 'stroke', currentStyle.strokePaint);
|
||||
}
|
||||
if (currentStyle.strokeOpacity) {
|
||||
change(target, 'stroke-opacity', currentStyle.strokeOpacity);
|
||||
}
|
||||
if (currentStyle.strokeWidth) {
|
||||
change(target, 'stroke-width', currentStyle.strokeWidth);
|
||||
}
|
||||
if (currentStyle.strokeDashArray) {
|
||||
change(target, 'stroke-dasharray', currentStyle.strokeDashArray);
|
||||
}
|
||||
if (currentStyle.opacity) {
|
||||
change(target, 'opacity', currentStyle.opacity);
|
||||
}
|
||||
if (currentStyle.strokeLinecap) {
|
||||
change(target, 'stroke-linecap', currentStyle.strokeLinecap);
|
||||
}
|
||||
if (currentStyle.strokeLinejoin) {
|
||||
change(target, 'stroke-linejoin', currentStyle.strokeLinejoin);
|
||||
}
|
||||
|
||||
addToHistory(new ChangeElementCommand(target, changes));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
272
dist/extensions/ext-foreignobject.js
vendored
Normal file
272
dist/extensions/ext-foreignobject.js
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-foreignobject.js
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Jacques Distler
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('foreignObject', function (S) {
|
||||
var text2xml = S.text2xml,
|
||||
NS = S.NS;
|
||||
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var
|
||||
// {svgcontent} = S,
|
||||
// addElem = S.addSvgElementFromJson,
|
||||
svgdoc = S.svgroot.parentNode.ownerDocument;
|
||||
|
||||
var properlySourceSizeTextArea = function properlySourceSizeTextArea() {
|
||||
// TODO: remove magic numbers here and get values from CSS
|
||||
var height = $('#svg_source_container').height() - 80;
|
||||
$('#svg_source_textarea').css('height', height);
|
||||
};
|
||||
|
||||
function showPanel(on) {
|
||||
var fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
fcRules = $('<style id="fc_rules"></style>').appendTo('head');
|
||||
}
|
||||
fcRules.text(!on ? '' : ' #tool_topath { display: none !important; }');
|
||||
$('#foreignObject_panel').toggle(on);
|
||||
}
|
||||
|
||||
function toggleSourceButtons(on) {
|
||||
$('#tool_source_save, #tool_source_cancel').toggle(!on);
|
||||
$('#foreign_save, #foreign_cancel').toggle(on);
|
||||
}
|
||||
|
||||
var selElems = void 0,
|
||||
started = void 0,
|
||||
newFO = void 0,
|
||||
editingforeign = false;
|
||||
|
||||
// Function: setForeignString(xmlString, elt)
|
||||
// This function sets the content of element elt to the input XML.
|
||||
//
|
||||
// Parameters:
|
||||
// xmlString - The XML text.
|
||||
// elt - the parent element to append to
|
||||
//
|
||||
// Returns:
|
||||
// This function returns false if the set was unsuccessful, true otherwise.
|
||||
function setForeignString(xmlString) {
|
||||
var elt = selElems[0];
|
||||
try {
|
||||
// convert string into XML document
|
||||
var newDoc = text2xml('<svg xmlns="' + NS.SVG + '" xmlns:xlink="' + NS.XLINK + '">' + xmlString + '</svg>');
|
||||
// run it through our sanitizer to remove anything we do not support
|
||||
S.sanitizeSvg(newDoc.documentElement);
|
||||
elt.parentNode.replaceChild(svgdoc.importNode(newDoc.documentElement.firstChild, true), elt);
|
||||
S.call('changed', [elt]);
|
||||
svgCanvas.clearSelection();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function showForeignEditor() {
|
||||
var elt = selElems[0];
|
||||
if (!elt || editingforeign) {
|
||||
return;
|
||||
}
|
||||
editingforeign = true;
|
||||
toggleSourceButtons(true);
|
||||
elt.removeAttribute('fill');
|
||||
|
||||
var str = S.svgToString(elt, 0);
|
||||
$('#svg_source_textarea').val(str);
|
||||
$('#svg_source_editor').fadeIn();
|
||||
properlySourceSizeTextArea();
|
||||
$('#svg_source_textarea').focus();
|
||||
}
|
||||
|
||||
function setAttr(attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
S.call('changed', selElems);
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'foreignObject',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'foreignobject-icons.xml',
|
||||
buttons: [{
|
||||
id: 'tool_foreign',
|
||||
type: 'mode',
|
||||
title: 'Foreign Object Tool',
|
||||
events: {
|
||||
click: function click() {
|
||||
svgCanvas.setMode('foreign');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
id: 'edit_foreign',
|
||||
type: 'context',
|
||||
panel: 'foreignObject_panel',
|
||||
title: 'Edit ForeignObject Content',
|
||||
events: {
|
||||
click: function click() {
|
||||
showForeignEditor();
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
context_tools: [{
|
||||
type: 'input',
|
||||
panel: 'foreignObject_panel',
|
||||
title: "Change foreignObject's width",
|
||||
id: 'foreign_width',
|
||||
label: 'w',
|
||||
size: 3,
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('width', this.value);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'foreignObject_panel',
|
||||
title: "Change foreignObject's height",
|
||||
id: 'foreign_height',
|
||||
label: 'h',
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('height', this.value);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'foreignObject_panel',
|
||||
title: "Change foreignObject's font size",
|
||||
id: 'foreign_font_size',
|
||||
label: 'font-size',
|
||||
size: 2,
|
||||
defval: 16,
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('font-size', this.value);
|
||||
}
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('#foreignObject_panel').hide();
|
||||
|
||||
var endChanges = function endChanges() {
|
||||
$('#svg_source_editor').hide();
|
||||
editingforeign = false;
|
||||
$('#svg_source_textarea').blur();
|
||||
toggleSourceButtons(false);
|
||||
};
|
||||
|
||||
// TODO: Needs to be done after orig icon loads
|
||||
setTimeout(function () {
|
||||
// Create source save/cancel buttons
|
||||
/* const save = */$('#tool_source_save').clone().hide().attr('id', 'foreign_save').unbind().appendTo('#tool_source_back').click(function () {
|
||||
if (!editingforeign) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!setForeignString($('#svg_source_textarea').val())) {
|
||||
$.confirm('Errors found. Revert to original?', function (ok) {
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
endChanges();
|
||||
});
|
||||
} else {
|
||||
endChanges();
|
||||
}
|
||||
// setSelectMode();
|
||||
});
|
||||
|
||||
/* const cancel = */$('#tool_source_cancel').clone().hide().attr('id', 'foreign_cancel').unbind().appendTo('#tool_source_back').click(function () {
|
||||
endChanges();
|
||||
});
|
||||
}, 3000);
|
||||
},
|
||||
mouseDown: function mouseDown(opts) {
|
||||
// const e = opts.event;
|
||||
|
||||
if (svgCanvas.getMode() === 'foreign') {
|
||||
started = true;
|
||||
newFO = S.addSvgElementFromJson({
|
||||
element: 'foreignObject',
|
||||
attr: {
|
||||
x: opts.start_x,
|
||||
y: opts.start_y,
|
||||
id: S.getNextId(),
|
||||
'font-size': 16, // cur_text.font_size,
|
||||
width: '48',
|
||||
height: '20',
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
var m = svgdoc.createElementNS(NS.MATH, 'math');
|
||||
m.setAttributeNS(NS.XMLNS, 'xmlns', NS.MATH);
|
||||
m.setAttribute('display', 'inline');
|
||||
var mi = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi.setAttribute('mathvariant', 'normal');
|
||||
mi.textContent = '\u03A6';
|
||||
var mo = svgdoc.createElementNS(NS.MATH, 'mo');
|
||||
mo.textContent = '\u222A';
|
||||
var mi2 = svgdoc.createElementNS(NS.MATH, 'mi');
|
||||
mi2.textContent = '\u2133';
|
||||
m.appendChild(mi);
|
||||
m.appendChild(mo);
|
||||
m.appendChild(mi2);
|
||||
newFO.appendChild(m);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp(opts) {
|
||||
// const e = opts.event;
|
||||
if (svgCanvas.getMode() === 'foreign' && started) {
|
||||
var attrs = $(newFO).attr(['width', 'height']);
|
||||
var keep = attrs.width !== '0' || attrs.height !== '0';
|
||||
svgCanvas.addToSelection([newFO], true);
|
||||
|
||||
return {
|
||||
keep: keep,
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && elem.tagName === 'foreignObject') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
$('#foreign_font_size').val(elem.getAttribute('font-size'));
|
||||
$('#foreign_width').val(elem.getAttribute('width'));
|
||||
$('#foreign_height').val(elem.getAttribute('height'));
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
// const elem = opts.elems[0];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
168
dist/extensions/ext-grid.js
vendored
Normal file
168
dist/extensions/ext-grid.js
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-grid.js
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Redou Mine
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('view_grid', function (_ref) {
|
||||
var NS = _ref.NS,
|
||||
getTypeMap = _ref.getTypeMap;
|
||||
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var svgdoc = document.getElementById('svgcanvas').ownerDocument,
|
||||
assignAttributes = svgCanvas.assignAttributes,
|
||||
hcanvas = document.createElement('canvas'),
|
||||
canvBG = $('#canvasBackground'),
|
||||
units = getTypeMap(),
|
||||
intervals = [0.01, 0.1, 1, 10, 100, 1000];
|
||||
|
||||
var showGrid = svgEditor.curConfig.showGrid || false;
|
||||
|
||||
$(hcanvas).hide().appendTo('body');
|
||||
|
||||
var canvasGrid = svgdoc.createElementNS(NS.SVG, 'svg');
|
||||
assignAttributes(canvasGrid, {
|
||||
id: 'canvasGrid',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
x: 0,
|
||||
y: 0,
|
||||
overflow: 'visible',
|
||||
display: 'none'
|
||||
});
|
||||
canvBG.append(canvasGrid);
|
||||
|
||||
// grid-pattern
|
||||
var gridPattern = svgdoc.createElementNS(NS.SVG, 'pattern');
|
||||
assignAttributes(gridPattern, {
|
||||
id: 'gridpattern',
|
||||
patternUnits: 'userSpaceOnUse',
|
||||
x: 0, // -(value.strokeWidth / 2), // position for strokewidth
|
||||
y: 0, // -(value.strokeWidth / 2), // position for strokewidth
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
|
||||
var gridimg = svgdoc.createElementNS(NS.SVG, 'image');
|
||||
assignAttributes(gridimg, {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
gridPattern.appendChild(gridimg);
|
||||
$('#svgroot defs').append(gridPattern);
|
||||
|
||||
// grid-box
|
||||
var gridBox = svgdoc.createElementNS(NS.SVG, 'rect');
|
||||
assignAttributes(gridBox, {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
x: 0,
|
||||
y: 0,
|
||||
'stroke-width': 0,
|
||||
stroke: 'none',
|
||||
fill: 'url(#gridpattern)',
|
||||
style: 'pointer-events: none; display:visible;'
|
||||
});
|
||||
$('#canvasGrid').append(gridBox);
|
||||
|
||||
function updateGrid(zoom) {
|
||||
// TODO: Try this with <line> elements, then compare performance difference
|
||||
var unit = units[svgEditor.curConfig.baseUnit]; // 1 = 1px
|
||||
var uMulti = unit * zoom;
|
||||
// Calculate the main number interval
|
||||
var rawM = 100 / uMulti;
|
||||
var multi = 1;
|
||||
for (var i = 0; i < intervals.length; i++) {
|
||||
var num = intervals[i];
|
||||
multi = num;
|
||||
if (rawM <= num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
var bigInt = multi * uMulti;
|
||||
|
||||
// Set the canvas size to the width of the container
|
||||
hcanvas.width = bigInt;
|
||||
hcanvas.height = bigInt;
|
||||
var ctx = hcanvas.getContext('2d');
|
||||
var curD = 0.5;
|
||||
var part = bigInt / 10;
|
||||
|
||||
ctx.globalAlpha = 0.2;
|
||||
ctx.strokeStyle = svgEditor.curConfig.gridColor;
|
||||
for (var _i = 1; _i < 10; _i++) {
|
||||
var subD = Math.round(part * _i) + 0.5;
|
||||
// const lineNum = (i % 2)?12:10;
|
||||
var lineNum = 0;
|
||||
ctx.moveTo(subD, bigInt);
|
||||
ctx.lineTo(subD, lineNum);
|
||||
ctx.moveTo(bigInt, subD);
|
||||
ctx.lineTo(lineNum, subD);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.moveTo(curD, bigInt);
|
||||
ctx.lineTo(curD, 0);
|
||||
|
||||
ctx.moveTo(bigInt, curD);
|
||||
ctx.lineTo(0, curD);
|
||||
ctx.stroke();
|
||||
|
||||
var datauri = hcanvas.toDataURL('image/png');
|
||||
gridimg.setAttribute('width', bigInt);
|
||||
gridimg.setAttribute('height', bigInt);
|
||||
gridimg.parentNode.setAttribute('width', bigInt);
|
||||
gridimg.parentNode.setAttribute('height', bigInt);
|
||||
svgCanvas.setHref(gridimg, datauri);
|
||||
}
|
||||
|
||||
function gridUpdate() {
|
||||
if (showGrid) {
|
||||
updateGrid(svgCanvas.getZoom());
|
||||
}
|
||||
$('#canvasGrid').toggle(showGrid);
|
||||
$('#view_grid').toggleClass('push_button_pressed tool_button');
|
||||
}
|
||||
return {
|
||||
name: 'view_grid',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'grid-icon.xml',
|
||||
|
||||
zoomChanged: function zoomChanged(zoom) {
|
||||
if (showGrid) {
|
||||
updateGrid(zoom);
|
||||
}
|
||||
},
|
||||
callback: function callback() {
|
||||
if (showGrid) {
|
||||
gridUpdate();
|
||||
}
|
||||
},
|
||||
|
||||
buttons: [{
|
||||
id: 'view_grid',
|
||||
type: 'context',
|
||||
panel: 'editor_panel',
|
||||
title: 'Show/Hide Grid',
|
||||
events: {
|
||||
click: function click() {
|
||||
svgEditor.curConfig.showGrid = showGrid = !showGrid;
|
||||
gridUpdate();
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
83
dist/extensions/ext-helloworld.js
vendored
Normal file
83
dist/extensions/ext-helloworld.js
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-helloworld.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
This is a very basic SVG-Edit extension. It adds a "Hello World" button in
|
||||
the left panel. Clicking on the button, and then the canvas will show the
|
||||
user the point on the canvas that was clicked on.
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('Hello World', function () {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
return {
|
||||
name: 'Hello World',
|
||||
// For more notes on how to make an icon file, see the source of
|
||||
// the helloworld-icon.xml
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'helloworld-icon.xml',
|
||||
|
||||
// Multiple buttons can be added in this array
|
||||
buttons: [{
|
||||
// Must match the icon ID in helloworld-icon.xml
|
||||
id: 'hello_world',
|
||||
|
||||
// This indicates that the button will be added to the "mode"
|
||||
// button panel on the left side
|
||||
type: 'mode',
|
||||
|
||||
// Tooltip text
|
||||
title: "Say 'Hello World'",
|
||||
|
||||
// Events
|
||||
events: {
|
||||
click: function click() {
|
||||
// The action taken when the button is clicked on.
|
||||
// For "mode" buttons, any other button will
|
||||
// automatically be de-pressed.
|
||||
svgCanvas.setMode('hello_world');
|
||||
}
|
||||
}
|
||||
}],
|
||||
// This is triggered when the main mouse button is pressed down
|
||||
// on the editor canvas (not the tool panels)
|
||||
mouseDown: function mouseDown() {
|
||||
// Check the mode on mousedown
|
||||
if (svgCanvas.getMode() === 'hello_world') {
|
||||
// The returned object must include "started" with
|
||||
// a value of true in order for mouseUp to be triggered
|
||||
return { started: true };
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// This is triggered from anywhere, but "started" must have been set
|
||||
// to true (see above). Note that "opts" is an object with event info
|
||||
mouseUp: function mouseUp(opts) {
|
||||
// Check the mode on mouseup
|
||||
if (svgCanvas.getMode() === 'hello_world') {
|
||||
var zoom = svgCanvas.getZoom();
|
||||
|
||||
// Get the actual coordinate by dividing by the zoom value
|
||||
var x = opts.mouse_x / zoom;
|
||||
var y = opts.mouse_y / zoom;
|
||||
|
||||
var text = 'Hello World!\n\nYou clicked here: ' + x + ', ' + y;
|
||||
|
||||
// Show the text using the custom alert function
|
||||
$.alert(text);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
395
dist/extensions/ext-imagelib.js
vendored
Normal file
395
dist/extensions/ext-imagelib.js
vendored
Normal file
@@ -0,0 +1,395 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-imagelib.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('imagelib', function (_ref) {
|
||||
var decode64 = _ref.decode64;
|
||||
|
||||
var $ = jQuery;
|
||||
var _svgEditor = svgEditor,
|
||||
uiStrings = _svgEditor.uiStrings,
|
||||
svgCanvas = _svgEditor.canvas;
|
||||
|
||||
|
||||
$.extend(uiStrings, {
|
||||
imagelib: {
|
||||
select_lib: 'Select an image library',
|
||||
show_list: 'Show library list',
|
||||
import_single: 'Import single',
|
||||
import_multi: 'Import multiple',
|
||||
open: 'Open as new document'
|
||||
}
|
||||
});
|
||||
|
||||
var modularVersion = !('svgEditor' in window) || !window.svgEditor || window.svgEditor.modules !== false;
|
||||
|
||||
var imgLibs = [{
|
||||
name: 'Demo library (local)',
|
||||
url: svgEditor.curConfig.extPath + 'imagelib/index' + (modularVersion ? '-es' : '') + '.html',
|
||||
description: 'Demonstration library for SVG-edit on this server'
|
||||
}, {
|
||||
name: 'IAN Symbol Libraries',
|
||||
url: 'https://ian.umces.edu/symbols/catalog/svgedit/album_chooser.php',
|
||||
description: 'Free library of illustrations'
|
||||
}, {
|
||||
name: 'Openclipart',
|
||||
url: 'https://openclipart.org/svgedit',
|
||||
description: 'Share and Use Images. Over 50,000 Public Domain SVG Images and Growing.'
|
||||
}];
|
||||
|
||||
function closeBrowser() {
|
||||
$('#imgbrowse_holder').hide();
|
||||
}
|
||||
|
||||
function importImage(url) {
|
||||
var newImage = svgCanvas.addSvgElementFromJson({
|
||||
element: 'image',
|
||||
attr: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
id: svgCanvas.getNextId(),
|
||||
style: 'pointer-events:inherit'
|
||||
}
|
||||
});
|
||||
svgCanvas.clearSelection();
|
||||
svgCanvas.addToSelection([newImage]);
|
||||
svgCanvas.setImageURL(url);
|
||||
}
|
||||
|
||||
var pending = {};
|
||||
|
||||
var mode = 's';
|
||||
var multiArr = [];
|
||||
var transferStopped = false;
|
||||
var preview = void 0,
|
||||
submit = void 0;
|
||||
|
||||
window.addEventListener('message', function (evt) {
|
||||
// Receive `postMessage` data
|
||||
var response = evt.data;
|
||||
|
||||
if (!response || typeof response !== 'string') {
|
||||
// Do nothing
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Todo: This block can be removed (and the above check changed to
|
||||
// insist on an object) if embedAPI moves away from a string to
|
||||
// an object (if IE9 support not needed)
|
||||
response = JSON.parse(response);
|
||||
if (response.namespace !== 'imagelib') {
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hasName = 'name' in response;
|
||||
var hasHref = 'href' in response;
|
||||
|
||||
if (!hasName && transferStopped) {
|
||||
transferStopped = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var id = void 0;
|
||||
if (hasHref) {
|
||||
id = response.href;
|
||||
response = response.data;
|
||||
}
|
||||
|
||||
// Hide possible transfer dialog box
|
||||
$('#dialog_box').hide();
|
||||
var entry = void 0,
|
||||
curMeta = void 0,
|
||||
svgStr = void 0,
|
||||
imgStr = void 0;
|
||||
var type = hasName ? 'meta' : response.charAt(0);
|
||||
switch (type) {
|
||||
case 'meta':
|
||||
{
|
||||
// Metadata
|
||||
transferStopped = false;
|
||||
curMeta = response;
|
||||
|
||||
pending[curMeta.id] = curMeta;
|
||||
|
||||
var name = curMeta.name || 'file';
|
||||
|
||||
var message = uiStrings.notification.retrieving.replace('%s', name);
|
||||
|
||||
if (mode !== 'm') {
|
||||
$.process_cancel(message, function () {
|
||||
transferStopped = true;
|
||||
// Should a message be sent back to the frame?
|
||||
|
||||
$('#dialog_box').hide();
|
||||
});
|
||||
} else {
|
||||
entry = $('<div>' + message + '</div>').data('id', curMeta.id);
|
||||
preview.append(entry);
|
||||
curMeta.entry = entry;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case '<':
|
||||
svgStr = true;
|
||||
break;
|
||||
case 'd':
|
||||
{
|
||||
if (response.startsWith('data:image/svg+xml')) {
|
||||
var pre = 'data:image/svg+xml;base64,';
|
||||
var src = response.substring(pre.length);
|
||||
response = decode64(src);
|
||||
svgStr = true;
|
||||
break;
|
||||
} else if (response.startsWith('data:image/')) {
|
||||
imgStr = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Else fall through
|
||||
default:
|
||||
// TODO: See if there's a way to base64 encode the binary data stream
|
||||
// const str = 'data:;base64,' + svgedit.utilities.encode64(response, true);
|
||||
|
||||
// Assume it's raw image data
|
||||
// importImage(str);
|
||||
|
||||
// Don't give warning as postMessage may have been used by something else
|
||||
if (mode !== 'm') {
|
||||
closeBrowser();
|
||||
} else {
|
||||
pending[id].entry.remove();
|
||||
}
|
||||
// $.alert('Unexpected data was returned: ' + response, function() {
|
||||
// if (mode !== 'm') {
|
||||
// closeBrowser();
|
||||
// } else {
|
||||
// pending[id].entry.remove();
|
||||
// }
|
||||
// });
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case 's':
|
||||
// Import one
|
||||
if (svgStr) {
|
||||
svgCanvas.importSvgString(response);
|
||||
} else if (imgStr) {
|
||||
importImage(response);
|
||||
}
|
||||
closeBrowser();
|
||||
break;
|
||||
case 'm':
|
||||
// Import multiple
|
||||
multiArr.push([svgStr ? 'svg' : 'img', response]);
|
||||
curMeta = pending[id];
|
||||
var title = void 0;
|
||||
if (svgStr) {
|
||||
if (curMeta && curMeta.name) {
|
||||
title = curMeta.name;
|
||||
} else {
|
||||
// Try to find a title
|
||||
var xml = new DOMParser().parseFromString(response, 'text/xml').documentElement;
|
||||
title = $(xml).children('title').first().text() || '(SVG #' + response.length + ')';
|
||||
}
|
||||
if (curMeta) {
|
||||
preview.children().each(function () {
|
||||
if ($(this).data('id') === id) {
|
||||
if (curMeta.preview_url) {
|
||||
$(this).html('<img src="' + curMeta.preview_url + '">' + title);
|
||||
} else {
|
||||
$(this).text(title);
|
||||
}
|
||||
submit.removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
preview.append('<div>' + title + '</div>');
|
||||
submit.removeAttr('disabled');
|
||||
}
|
||||
} else {
|
||||
if (curMeta && curMeta.preview_url) {
|
||||
title = curMeta.name || '';
|
||||
}
|
||||
if (curMeta && curMeta.preview_url) {
|
||||
entry = '<img src="' + curMeta.preview_url + '">' + title;
|
||||
} else {
|
||||
entry = '<img src="' + response + '">';
|
||||
}
|
||||
|
||||
if (curMeta) {
|
||||
preview.children().each(function () {
|
||||
if ($(this).data('id') === id) {
|
||||
$(this).html(entry);
|
||||
submit.removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
preview.append($('<div>').append(entry));
|
||||
submit.removeAttr('disabled');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
// Open
|
||||
if (!svgStr) {
|
||||
break;
|
||||
}
|
||||
svgEditor.openPrep(function (ok) {
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
svgCanvas.clear();
|
||||
svgCanvas.setSvgString(response);
|
||||
// updateCanvas();
|
||||
});
|
||||
closeBrowser();
|
||||
break;
|
||||
}
|
||||
}, true);
|
||||
|
||||
function toggleMulti(show) {
|
||||
$('#lib_framewrap, #imglib_opts').css({ right: show ? 200 : 10 });
|
||||
if (!preview) {
|
||||
preview = $('<div id=imglib_preview>').css({
|
||||
position: 'absolute',
|
||||
top: 45,
|
||||
right: 10,
|
||||
width: 180,
|
||||
bottom: 45,
|
||||
background: '#fff',
|
||||
overflow: 'auto'
|
||||
}).insertAfter('#lib_framewrap');
|
||||
|
||||
submit = $('<button disabled>Import selected</button>').appendTo('#imgbrowse').on('click touchend', function () {
|
||||
$.each(multiArr, function (i) {
|
||||
var type = this[0];
|
||||
var data = this[1];
|
||||
if (type === 'svg') {
|
||||
svgCanvas.importSvgString(data);
|
||||
} else {
|
||||
importImage(data);
|
||||
}
|
||||
svgCanvas.moveSelectedElements(i * 20, i * 20, false);
|
||||
});
|
||||
preview.empty();
|
||||
multiArr = [];
|
||||
$('#imgbrowse_holder').hide();
|
||||
}).css({
|
||||
position: 'absolute',
|
||||
bottom: 10,
|
||||
right: -10
|
||||
});
|
||||
}
|
||||
|
||||
preview.toggle(show);
|
||||
submit.toggle(show);
|
||||
}
|
||||
|
||||
function showBrowser() {
|
||||
var browser = $('#imgbrowse');
|
||||
if (!browser.length) {
|
||||
$('<div id=imgbrowse_holder><div id=imgbrowse class=toolbar_button>' + '</div></div>').insertAfter('#svg_docprops');
|
||||
browser = $('#imgbrowse');
|
||||
|
||||
var allLibs = uiStrings.imagelib.select_lib;
|
||||
|
||||
var libOpts = $('<ul id=imglib_opts>').appendTo(browser);
|
||||
var frame = $('<iframe/>').prependTo(browser).hide().wrap('<div id=lib_framewrap>');
|
||||
|
||||
var header = $('<h1>').prependTo(browser).text(allLibs).css({
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
var cancel = $('<button>' + uiStrings.common.cancel + '</button>').appendTo(browser).on('click touchend', function () {
|
||||
$('#imgbrowse_holder').hide();
|
||||
}).css({
|
||||
position: 'absolute',
|
||||
top: 5,
|
||||
right: -10
|
||||
});
|
||||
|
||||
var leftBlock = $('<span>').css({ position: 'absolute', top: 5, left: 10 }).appendTo(browser);
|
||||
|
||||
var back = $('<button hidden>' + uiStrings.imagelib.show_list + '</button>').appendTo(leftBlock).on('click touchend', function () {
|
||||
frame.attr('src', 'about:blank').hide();
|
||||
libOpts.show();
|
||||
header.text(allLibs);
|
||||
back.hide();
|
||||
}).css({
|
||||
'margin-right': 5
|
||||
}).hide();
|
||||
|
||||
/* const type = */$('<select><option value=s>' + uiStrings.imagelib.import_single + '</option><option value=m>' + uiStrings.imagelib.import_multi + '</option><option value=o>' + uiStrings.imagelib.open + '</option></select>').appendTo(leftBlock).change(function () {
|
||||
mode = $(this).val();
|
||||
switch (mode) {
|
||||
case 's':
|
||||
case 'o':
|
||||
toggleMulti(false);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
// Import multiple
|
||||
toggleMulti(true);
|
||||
break;
|
||||
}
|
||||
}).css({
|
||||
'margin-top': 10
|
||||
});
|
||||
|
||||
cancel.prepend($.getSvgIcon('cancel', true));
|
||||
back.prepend($.getSvgIcon('tool_imagelib', true));
|
||||
|
||||
$.each(imgLibs, function (i, _ref2) {
|
||||
var name = _ref2.name,
|
||||
url = _ref2.url,
|
||||
description = _ref2.description;
|
||||
|
||||
$('<li>').appendTo(libOpts).text(name).on('click touchend', function () {
|
||||
frame.attr('src', url).show();
|
||||
header.text(name);
|
||||
libOpts.hide();
|
||||
back.show();
|
||||
}).append('<span>' + description + '</span>');
|
||||
});
|
||||
} else {
|
||||
$('#imgbrowse_holder').show();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'ext-imagelib.xml',
|
||||
buttons: [{
|
||||
id: 'tool_imagelib',
|
||||
type: 'app_menu', // _flyout
|
||||
position: 4,
|
||||
title: 'Image library',
|
||||
events: {
|
||||
mouseup: showBrowser
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('<style>').text('#imgbrowse_holder {' + 'position: absolute;' + 'top: 0;' + 'left: 0;' + 'width: 100%;' + 'height: 100%;' + 'background-color: rgba(0, 0, 0, .5);' + 'z-index: 5;' + '}' + '#imgbrowse {' + 'position: absolute;' + 'top: 25px;' + 'left: 25px;' + 'right: 25px;' + 'bottom: 25px;' + 'min-width: 300px;' + 'min-height: 200px;' + 'background: #B0B0B0;' + 'border: 1px outset #777;' + '}' + '#imgbrowse h1 {' + 'font-size: 20px;' + 'margin: .4em;' + 'text-align: center;' + '}' + '#lib_framewrap,' + '#imgbrowse > ul {' + 'position: absolute;' + 'top: 45px;' + 'left: 10px;' + 'right: 10px;' + 'bottom: 10px;' + 'background: white;' + 'margin: 0;' + 'padding: 0;' + '}' + '#imgbrowse > ul {' + 'overflow: auto;' + '}' + '#imgbrowse > div {' + 'border: 1px solid #666;' + '}' + '#imglib_preview > div {' + 'padding: 5px;' + 'font-size: 12px;' + '}' + '#imglib_preview img {' + 'display: block;' + 'margin: 0 auto;' + 'max-height: 100px;' + '}' + '#imgbrowse li {' + 'list-style: none;' + 'padding: .5em;' + 'background: #E8E8E8;' + 'border-bottom: 1px solid #B0B0B0;' + 'line-height: 1.2em;' + 'font-style: sans-serif;' + '}' + '#imgbrowse li > span {' + 'color: #666;' + 'font-size: 15px;' + 'display: block;' + '}' + '#imgbrowse li:hover {' + 'background: #FFC;' + 'cursor: pointer;' + '}' + '#imgbrowse iframe {' + 'width: 100%;' + 'height: 100%;' + 'border: 0;' + '}').appendTo('head');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
600
dist/extensions/ext-markers.js
vendored
Normal file
600
dist/extensions/ext-markers.js
vendored
Normal file
@@ -0,0 +1,600 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-markers.js
|
||||
*
|
||||
* Licensed under the Apache License, Version 2
|
||||
*
|
||||
* Copyright(c) 2010 Will Schleter
|
||||
* based on ext-arrows.js by Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
* This extension provides for the addition of markers to the either end
|
||||
* or the middle of a line, polyline, path, polygon.
|
||||
*
|
||||
* Markers may be either a graphic or arbitary text
|
||||
*
|
||||
* to simplify the coding and make the implementation as robust as possible,
|
||||
* markers are not shared - every object has its own set of markers.
|
||||
* this relationship is maintained by a naming convention between the
|
||||
* ids of the markers and the ids of the object
|
||||
*
|
||||
* The following restrictions exist for simplicty of use and programming
|
||||
* objects and their markers to have the same color
|
||||
* marker size is fixed
|
||||
* text marker font, size, and attributes are fixed
|
||||
* an application specific attribute - se_type - is added to each marker element
|
||||
* to store the type of marker
|
||||
*
|
||||
* TODO:
|
||||
* remove some of the restrictions above
|
||||
* add option for keeping text aligned to horizontal
|
||||
* add support for dimension extension lines
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('Markers', function (S) {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var // {svgcontent} = S,
|
||||
addElem = S.addSvgElementFromJson;
|
||||
var mtypes = ['start', 'mid', 'end'];
|
||||
var markerPrefix = 'se_marker_';
|
||||
var idPrefix = 'mkr_';
|
||||
|
||||
// note - to add additional marker types add them below with a unique id
|
||||
// and add the associated icon(s) to marker-icons.svg
|
||||
// the geometry is normallized to a 100x100 box with the origin at lower left
|
||||
// Safari did not like negative values for low left of viewBox
|
||||
// remember that the coordinate system has +y downward
|
||||
var markerTypes = {
|
||||
nomarker: {},
|
||||
leftarrow: { element: 'path', attr: { d: 'M0,50 L100,90 L70,50 L100,10 Z' } },
|
||||
rightarrow: { element: 'path', attr: { d: 'M100,50 L0,90 L30,50 L0,10 Z' } },
|
||||
textmarker: { element: 'text', attr: { x: 0, y: 0, 'stroke-width': 0, stroke: 'none', 'font-size': 75, 'font-family': 'serif', 'text-anchor': 'left',
|
||||
'xml:space': 'preserve' } },
|
||||
forwardslash: { element: 'path', attr: { d: 'M30,100 L70,0' } },
|
||||
reverseslash: { element: 'path', attr: { d: 'M30,0 L70,100' } },
|
||||
verticalslash: { element: 'path', attr: { d: 'M50,0 L50,100' } },
|
||||
box: { element: 'path', attr: { d: 'M20,20 L20,80 L80,80 L80,20 Z' } },
|
||||
star: { element: 'path', attr: { d: 'M10,30 L90,30 L20,90 L50,10 L80,90 Z' } },
|
||||
xmark: { element: 'path', attr: { d: 'M20,80 L80,20 M80,80 L20,20' } },
|
||||
triangle: { element: 'path', attr: { d: 'M10,80 L50,20 L80,80 Z' } },
|
||||
mcircle: { element: 'circle', attr: { r: 30, cx: 50, cy: 50 } }
|
||||
};
|
||||
|
||||
var langList = {
|
||||
en: [{ id: 'start_marker_list', title: 'Select start marker type' }, { id: 'mid_marker_list', title: 'Select mid marker type' }, { id: 'end_marker_list', title: 'Select end marker type' }, { id: 'nomarker', title: 'No Marker' }, { id: 'leftarrow', title: 'Left Arrow' }, { id: 'rightarrow', title: 'Right Arrow' }, { id: 'textmarker', title: 'Text Marker' }, { id: 'forwardslash', title: 'Forward Slash' }, { id: 'reverseslash', title: 'Reverse Slash' }, { id: 'verticalslash', title: 'Vertical Slash' }, { id: 'box', title: 'Box' }, { id: 'star', title: 'Star' }, { id: 'xmark', title: 'X' }, { id: 'triangle', title: 'Triangle' }, { id: 'mcircle', title: 'Circle' }, { id: 'leftarrow_o', title: 'Open Left Arrow' }, { id: 'rightarrow_o', title: 'Open Right Arrow' }, { id: 'box_o', title: 'Open Box' }, { id: 'star_o', title: 'Open Star' }, { id: 'triangle_o', title: 'Open Triangle' }, { id: 'mcircle_o', title: 'Open Circle' }]
|
||||
};
|
||||
|
||||
// duplicate shapes to support unfilled (open) marker types with an _o suffix
|
||||
$.each(['leftarrow', 'rightarrow', 'box', 'star', 'mcircle', 'triangle'], function (i, v) {
|
||||
markerTypes[v + '_o'] = markerTypes[v];
|
||||
});
|
||||
|
||||
// elem = a graphic element will have an attribute like marker-start
|
||||
// attr - marker-start, marker-mid, or marker-end
|
||||
// returns the marker element that is linked to the graphic element
|
||||
function getLinked(elem, attr) {
|
||||
var str = elem.getAttribute(attr);
|
||||
if (!str) {
|
||||
return null;
|
||||
}
|
||||
var m = str.match(/\(#(.*)\)/);
|
||||
if (!m || m.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
return S.getElem(m[1]);
|
||||
}
|
||||
|
||||
function setIcon(pos, id) {
|
||||
if (id.substr(0, 1) !== '\\') {
|
||||
id = '\\textmarker';
|
||||
}
|
||||
var ci = '#' + idPrefix + pos + '_' + id.substr(1);
|
||||
svgEditor.setIcon('#cur_' + pos + '_marker_list', $(ci).children());
|
||||
$(ci).addClass('current').siblings().removeClass('current');
|
||||
}
|
||||
|
||||
var selElems = void 0;
|
||||
// toggles context tool panel off/on
|
||||
// sets the controls with the selected element's settings
|
||||
function showPanel(on) {
|
||||
$('#marker_panel').toggle(on);
|
||||
|
||||
if (on) {
|
||||
var el = selElems[0];
|
||||
|
||||
var val = void 0,
|
||||
ci = void 0;
|
||||
$.each(mtypes, function (i, pos) {
|
||||
var m = getLinked(el, 'marker-' + pos);
|
||||
var txtbox = $('#' + pos + '_marker');
|
||||
if (!m) {
|
||||
val = '\\nomarker';
|
||||
ci = val;
|
||||
txtbox.hide(); // hide text box
|
||||
} else {
|
||||
if (!m.attributes.se_type) {
|
||||
return;
|
||||
} // not created by this extension
|
||||
val = '\\' + m.attributes.se_type.textContent;
|
||||
ci = val;
|
||||
if (val === '\\textmarker') {
|
||||
val = m.lastChild.textContent;
|
||||
// txtbox.show(); // show text box
|
||||
} else {
|
||||
txtbox.hide(); // hide text box
|
||||
}
|
||||
}
|
||||
txtbox.val(val);
|
||||
setIcon(pos, ci);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function addMarker(id, val) {
|
||||
var txtBoxBg = '#ffffff';
|
||||
var txtBoxBorder = 'none';
|
||||
var txtBoxStrokeWidth = 0;
|
||||
|
||||
var marker = S.getElem(id);
|
||||
if (marker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (val === '' || val === '\\nomarker') {
|
||||
return;
|
||||
}
|
||||
|
||||
var el = selElems[0];
|
||||
var color = el.getAttribute('stroke');
|
||||
// NOTE: Safari didn't like a negative value in viewBox
|
||||
// so we use a standardized 0 0 100 100
|
||||
// with 50 50 being mapped to the marker position
|
||||
var strokeWidth = 10;
|
||||
var refX = 50;
|
||||
var refY = 50;
|
||||
var viewBox = '0 0 100 100';
|
||||
var markerWidth = 5;
|
||||
var markerHeight = 5;
|
||||
var seType = void 0;
|
||||
if (val.substr(0, 1) === '\\') {
|
||||
seType = val.substr(1);
|
||||
} else {
|
||||
seType = 'textmarker';
|
||||
}
|
||||
|
||||
if (!markerTypes[seType]) {
|
||||
return;
|
||||
} // an unknown type!
|
||||
|
||||
// create a generic marker
|
||||
marker = addElem({
|
||||
element: 'marker',
|
||||
attr: {
|
||||
id: id,
|
||||
markerUnits: 'strokeWidth',
|
||||
orient: 'auto',
|
||||
style: 'pointer-events:none',
|
||||
se_type: seType
|
||||
}
|
||||
});
|
||||
|
||||
if (seType !== 'textmarker') {
|
||||
var mel = addElem(markerTypes[seType]);
|
||||
var fillcolor = seType.substr(-2) === '_o' ? 'none' : color;
|
||||
|
||||
mel.setAttribute('fill', fillcolor);
|
||||
mel.setAttribute('stroke', color);
|
||||
mel.setAttribute('stroke-width', strokeWidth);
|
||||
marker.appendChild(mel);
|
||||
} else {
|
||||
var text = addElem(markerTypes[seType]);
|
||||
// have to add text to get bounding box
|
||||
text.textContent = val;
|
||||
var tb = text.getBBox();
|
||||
// alert(tb.x + ' ' + tb.y + ' ' + tb.width + ' ' + tb.height);
|
||||
var pad = 1;
|
||||
var bb = tb;
|
||||
bb.x = 0;
|
||||
bb.y = 0;
|
||||
bb.width += pad * 2;
|
||||
bb.height += pad * 2;
|
||||
// shift text according to its size
|
||||
text.setAttribute('x', pad);
|
||||
text.setAttribute('y', bb.height - pad - tb.height / 4); // kludge?
|
||||
text.setAttribute('fill', color);
|
||||
refX = bb.width / 2 + pad;
|
||||
refY = bb.height / 2 + pad;
|
||||
viewBox = bb.x + ' ' + bb.y + ' ' + bb.width + ' ' + bb.height;
|
||||
markerWidth = bb.width / 10;
|
||||
markerHeight = bb.height / 10;
|
||||
|
||||
var box = addElem({
|
||||
element: 'rect',
|
||||
attr: {
|
||||
x: bb.x,
|
||||
y: bb.y,
|
||||
width: bb.width,
|
||||
height: bb.height,
|
||||
fill: txtBoxBg,
|
||||
stroke: txtBoxBorder,
|
||||
'stroke-width': txtBoxStrokeWidth
|
||||
}
|
||||
});
|
||||
marker.setAttribute('orient', 0);
|
||||
marker.appendChild(box);
|
||||
marker.appendChild(text);
|
||||
}
|
||||
|
||||
marker.setAttribute('viewBox', viewBox);
|
||||
marker.setAttribute('markerWidth', markerWidth);
|
||||
marker.setAttribute('markerHeight', markerHeight);
|
||||
marker.setAttribute('refX', refX);
|
||||
marker.setAttribute('refY', refY);
|
||||
S.findDefs().appendChild(marker);
|
||||
|
||||
return marker;
|
||||
}
|
||||
|
||||
function convertline(elem) {
|
||||
// this routine came from the connectors extension
|
||||
// it is needed because midpoint markers don't work with line elements
|
||||
if (!(elem.tagName === 'line')) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
// Convert to polyline to accept mid-arrow
|
||||
|
||||
var x1 = Number(elem.getAttribute('x1'));
|
||||
var x2 = Number(elem.getAttribute('x2'));
|
||||
var y1 = Number(elem.getAttribute('y1'));
|
||||
var y2 = Number(elem.getAttribute('y2'));
|
||||
var id = elem.id;
|
||||
|
||||
|
||||
var midPt = ' ' + (x1 + x2) / 2 + ',' + (y1 + y2) / 2 + ' ';
|
||||
var pline = addElem({
|
||||
element: 'polyline',
|
||||
attr: {
|
||||
points: x1 + ',' + y1 + midPt + x2 + ',' + y2,
|
||||
stroke: elem.getAttribute('stroke'),
|
||||
'stroke-width': elem.getAttribute('stroke-width'),
|
||||
fill: 'none',
|
||||
opacity: elem.getAttribute('opacity') || 1
|
||||
}
|
||||
});
|
||||
$.each(mtypes, function (i, pos) {
|
||||
// get any existing marker definitions
|
||||
var nam = 'marker-' + pos;
|
||||
var m = elem.getAttribute(nam);
|
||||
if (m) {
|
||||
pline.setAttribute(nam, elem.getAttribute(nam));
|
||||
}
|
||||
});
|
||||
|
||||
var batchCmd = new S.BatchCommand();
|
||||
batchCmd.addSubCommand(new S.RemoveElementCommand(elem, elem.parentNode));
|
||||
batchCmd.addSubCommand(new S.InsertElementCommand(pline));
|
||||
|
||||
$(elem).after(pline).remove();
|
||||
svgCanvas.clearSelection();
|
||||
pline.id = id;
|
||||
svgCanvas.addToSelection([pline]);
|
||||
S.addCommandToHistory(batchCmd);
|
||||
return pline;
|
||||
}
|
||||
|
||||
function setMarker() {
|
||||
var poslist = { start_marker: 'start', mid_marker: 'mid', end_marker: 'end' };
|
||||
var pos = poslist[this.id];
|
||||
var markerName = 'marker-' + pos;
|
||||
var el = selElems[0];
|
||||
var marker = getLinked(el, markerName);
|
||||
if (marker) {
|
||||
$(marker).remove();
|
||||
}
|
||||
el.removeAttribute(markerName);
|
||||
var val = this.value;
|
||||
if (val === '') {
|
||||
val = '\\nomarker';
|
||||
}
|
||||
if (val === '\\nomarker') {
|
||||
setIcon(pos, val);
|
||||
S.call('changed', selElems);
|
||||
return;
|
||||
}
|
||||
// Set marker on element
|
||||
var id = markerPrefix + pos + '_' + el.id;
|
||||
addMarker(id, val);
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')');
|
||||
if (el.tagName === 'line' && pos === 'mid') {
|
||||
el = convertline(el);
|
||||
}
|
||||
S.call('changed', selElems);
|
||||
setIcon(pos, val);
|
||||
}
|
||||
|
||||
// called when the main system modifies an object
|
||||
// this routine changes the associated markers to be the same color
|
||||
function colorChanged(elem) {
|
||||
var color = elem.getAttribute('stroke');
|
||||
|
||||
$.each(mtypes, function (i, pos) {
|
||||
var marker = getLinked(elem, 'marker-' + pos);
|
||||
if (!marker) {
|
||||
return;
|
||||
}
|
||||
if (!marker.attributes.se_type) {
|
||||
return;
|
||||
} // not created by this extension
|
||||
var ch = marker.lastElementChild;
|
||||
if (!ch) {
|
||||
return;
|
||||
}
|
||||
var curfill = ch.getAttribute('fill');
|
||||
var curstroke = ch.getAttribute('stroke');
|
||||
if (curfill && curfill !== 'none') {
|
||||
ch.setAttribute('fill', color);
|
||||
}
|
||||
if (curstroke && curstroke !== 'none') {
|
||||
ch.setAttribute('stroke', color);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// called when the main system creates or modifies an object
|
||||
// primary purpose is create new markers for cloned objects
|
||||
function updateReferences(el) {
|
||||
$.each(mtypes, function (i, pos) {
|
||||
var id = markerPrefix + pos + '_' + el.id;
|
||||
var markerName = 'marker-' + pos;
|
||||
var marker = getLinked(el, markerName);
|
||||
if (!marker || !marker.attributes.se_type) {
|
||||
return;
|
||||
} // not created by this extension
|
||||
var url = el.getAttribute(markerName);
|
||||
if (url) {
|
||||
var len = el.id.length;
|
||||
var linkid = url.substr(-len - 1, len);
|
||||
if (el.id !== linkid) {
|
||||
var val = $('#' + pos + '_marker').attr('value');
|
||||
addMarker(id, val);
|
||||
svgCanvas.changeSelectedAttribute(markerName, 'url(#' + id + ')');
|
||||
if (el.tagName === 'line' && pos === 'mid') {
|
||||
el = convertline(el);
|
||||
}
|
||||
S.call('changed', selElems);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// simulate a change event a text box that stores the current element's marker type
|
||||
function triggerTextEntry(pos, val) {
|
||||
$('#' + pos + '_marker').val(val);
|
||||
$('#' + pos + '_marker').change();
|
||||
// const txtbox = $('#'+pos+'_marker');
|
||||
// if (val.substr(0,1)=='\\') {txtbox.hide();}
|
||||
// else {txtbox.show();}
|
||||
}
|
||||
|
||||
function showTextPrompt(pos) {
|
||||
var def = $('#' + pos + '_marker').val();
|
||||
if (def.substr(0, 1) === '\\') {
|
||||
def = '';
|
||||
}
|
||||
$.prompt('Enter text for ' + pos + ' marker', def, function (txt) {
|
||||
if (txt) {
|
||||
triggerTextEntry(pos, txt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
function setMarkerSet(obj) {
|
||||
const parts = this.id.split('_');
|
||||
const set = parts[2];
|
||||
switch (set) {
|
||||
case 'off':
|
||||
triggerTextEntry('start','\\nomarker');
|
||||
triggerTextEntry('mid','\\nomarker');
|
||||
triggerTextEntry('end','\\nomarker');
|
||||
break;
|
||||
case 'dimension':
|
||||
triggerTextEntry('start','\\leftarrow');
|
||||
triggerTextEntry('end','\\rightarrow');
|
||||
showTextPrompt('mid');
|
||||
break;
|
||||
case 'label':
|
||||
triggerTextEntry('mid','\\nomarker');
|
||||
triggerTextEntry('end','\\rightarrow');
|
||||
showTextPrompt('start');
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// callback function for a toolbar button click
|
||||
function setArrowFromButton(obj) {
|
||||
var parts = this.id.split('_');
|
||||
var pos = parts[1];
|
||||
var val = parts[2];
|
||||
if (parts[3]) {
|
||||
val += '_' + parts[3];
|
||||
}
|
||||
|
||||
if (val !== 'textmarker') {
|
||||
triggerTextEntry(pos, '\\' + val);
|
||||
} else {
|
||||
showTextPrompt(pos);
|
||||
}
|
||||
}
|
||||
|
||||
function getTitle() {
|
||||
var lang = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'en';
|
||||
var id = arguments[1];
|
||||
|
||||
var list = langList[lang];
|
||||
for (var i in list) {
|
||||
if (list.hasOwnProperty(i) && list[i].id === id) {
|
||||
return list[i].title;
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// build the toolbar button array from the marker definitions
|
||||
function buildButtonList(lang) {
|
||||
var buttons = [];
|
||||
// const i = 0;
|
||||
/*
|
||||
buttons.push({
|
||||
id: idPrefix + 'markers_off',
|
||||
title: 'Turn off all markers',
|
||||
type: 'context',
|
||||
events: { click: setMarkerSet },
|
||||
panel: 'marker_panel'
|
||||
});
|
||||
buttons.push({
|
||||
id: idPrefix + 'markers_dimension',
|
||||
title: 'Dimension',
|
||||
type: 'context',
|
||||
events: { click: setMarkerSet },
|
||||
panel: 'marker_panel'
|
||||
});
|
||||
buttons.push({
|
||||
id: idPrefix + 'markers_label',
|
||||
title: 'Label',
|
||||
type: 'context',
|
||||
events: { click: setMarkerSet },
|
||||
panel: 'marker_panel'
|
||||
});
|
||||
*/
|
||||
$.each(mtypes, function (k, pos) {
|
||||
var listname = pos + '_marker_list';
|
||||
var def = true;
|
||||
$.each(markerTypes, function (id, v) {
|
||||
var title = getTitle(lang, String(id));
|
||||
buttons.push({
|
||||
id: idPrefix + pos + '_' + id,
|
||||
svgicon: id,
|
||||
title: title,
|
||||
type: 'context',
|
||||
events: { click: setArrowFromButton },
|
||||
panel: 'marker_panel',
|
||||
list: listname,
|
||||
isDefault: def
|
||||
});
|
||||
def = false;
|
||||
});
|
||||
});
|
||||
return buttons;
|
||||
}
|
||||
|
||||
var currentLang = void 0;
|
||||
var ret = {
|
||||
name: 'Markers',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'markers-icons.xml',
|
||||
callback: function callback() {
|
||||
$('#marker_panel').addClass('toolset').hide();
|
||||
},
|
||||
addLangData: function addLangData(lang) {
|
||||
currentLang = lang;
|
||||
return { data: langList[lang] };
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// Use this to update the current selected elements
|
||||
// console.log('selectChanged',opts);
|
||||
selElems = opts.elems;
|
||||
|
||||
var markerElems = ['line', 'path', 'polyline', 'polygon'];
|
||||
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && markerElems.includes(elem.tagName)) {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
// console.log('elementChanged',opts);
|
||||
var elem = opts.elems[0];
|
||||
if (elem && (elem.getAttribute('marker-start') || elem.getAttribute('marker-mid') || elem.getAttribute('marker-end'))) {
|
||||
colorChanged(elem);
|
||||
updateReferences(elem);
|
||||
}
|
||||
// changing_flag = false; // Not apparently in use
|
||||
}
|
||||
};
|
||||
// Todo: Check if the lang will be available in time
|
||||
Object.defineProperties(ret, {
|
||||
buttons: {
|
||||
get: function get() {
|
||||
return buildButtonList(currentLang);
|
||||
}
|
||||
},
|
||||
context_tools: {
|
||||
get: function get() {
|
||||
return [{
|
||||
type: 'input',
|
||||
panel: 'marker_panel',
|
||||
title: 'Start marker',
|
||||
id: 'start_marker',
|
||||
label: 's',
|
||||
size: 3,
|
||||
events: { change: setMarker }
|
||||
}, {
|
||||
type: 'button-select',
|
||||
panel: 'marker_panel',
|
||||
title: getTitle(currentLang, 'start_marker_list'),
|
||||
id: 'start_marker_list',
|
||||
colnum: 3,
|
||||
events: { change: setArrowFromButton }
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'marker_panel',
|
||||
title: 'Middle marker',
|
||||
id: 'mid_marker',
|
||||
label: 'm',
|
||||
defval: '',
|
||||
size: 3,
|
||||
events: { change: setMarker }
|
||||
}, {
|
||||
type: 'button-select',
|
||||
panel: 'marker_panel',
|
||||
title: getTitle(currentLang, 'mid_marker_list'),
|
||||
id: 'mid_marker_list',
|
||||
colnum: 3,
|
||||
events: { change: setArrowFromButton }
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'marker_panel',
|
||||
title: 'End marker',
|
||||
id: 'end_marker',
|
||||
label: 'e',
|
||||
size: 3,
|
||||
events: { change: setMarker }
|
||||
}, {
|
||||
type: 'button-select',
|
||||
panel: 'marker_panel',
|
||||
title: getTitle(currentLang, 'end_marker_list'),
|
||||
id: 'end_marker_list',
|
||||
colnum: 3,
|
||||
events: { change: setArrowFromButton }
|
||||
}];
|
||||
}
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
});
|
||||
|
||||
}());
|
||||
208
dist/extensions/ext-mathjax.js
vendored
Normal file
208
dist/extensions/ext-mathjax.js
vendored
Normal file
@@ -0,0 +1,208 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery, MathJax */
|
||||
/*
|
||||
* ext-mathjax.js
|
||||
*
|
||||
* Licensed under the Apache License
|
||||
*
|
||||
* Copyright(c) 2013 Jo Segaert
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('mathjax', function () {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
|
||||
// Configuration of the MathJax extention.
|
||||
|
||||
// This will be added to the head tag before MathJax is loaded.
|
||||
/* mathjaxConfiguration = '<script type="text/x-mathjax-config">\
|
||||
MathJax.Hub.Config({\
|
||||
extensions: ["tex2jax.js"],\
|
||||
jax: ["input/TeX","output/SVG"],\
|
||||
showProcessingMessages: true,\
|
||||
showMathMenu: false,\
|
||||
showMathMenuMSIE: false,\
|
||||
errorSettings: {\
|
||||
message: ["[Math Processing Error]"],\
|
||||
style: {color: "#CC0000", "font-style":"italic"}\
|
||||
},\
|
||||
elements: [],\
|
||||
tex2jax: {\
|
||||
ignoreClass: "tex2jax_ignore2", processClass: "tex2jax_process2",\
|
||||
},\
|
||||
TeX: {\
|
||||
extensions: ["AMSmath.js","AMSsymbols.js","noErrors.js","noUndefined.js"]\
|
||||
},\
|
||||
"SVG": {\
|
||||
}\
|
||||
});\
|
||||
</script>', */
|
||||
// mathjaxSrc = 'http://cdn.mathjax.org/mathjax/latest/MathJax.js',
|
||||
// Had been on https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_SVG.js
|
||||
// Obtained Text-AMS-MML_SVG.js from https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.3/config/TeX-AMS-MML_SVG.js
|
||||
var mathjaxSrcSecure = 'mathjax/MathJax.js?config=TeX-AMS-MML_SVG.js',
|
||||
_svgEditor = svgEditor,
|
||||
uiStrings = _svgEditor.uiStrings;
|
||||
|
||||
var math = void 0,
|
||||
locationX = void 0,
|
||||
locationY = void 0,
|
||||
mathjaxLoaded = false;
|
||||
|
||||
// TODO: Implement language support. Move these uiStrings to the locale files and the code to the langReady callback.
|
||||
$.extend(uiStrings, {
|
||||
mathjax: {
|
||||
embed_svg: 'Save as mathematics',
|
||||
embed_mathml: 'Save as figure',
|
||||
svg_save_warning: 'The math will be transformed into a figure is manipulatable like everything else. You will not be able to manipulate the TeX-code anymore. ',
|
||||
mathml_save_warning: 'Advised. The math will be saved as a figure.',
|
||||
title: 'Mathematics code editor'
|
||||
}
|
||||
});
|
||||
|
||||
function saveMath() {
|
||||
var code = $('#mathjax_code_textarea').val();
|
||||
// displaystyle to force MathJax NOT to use the inline style. Because it is
|
||||
// less fancy!
|
||||
MathJax.Hub.queue.Push(['Text', math, '\\displaystyle{' + code + '}']);
|
||||
|
||||
/*
|
||||
* The MathJax library doesn't want to bloat your webpage so it creates
|
||||
* every symbol (glymph) you need only once. These are saved in a <svg> on
|
||||
* the top of your html document, just under the body tag. Each glymph has
|
||||
* its unique id and is saved as a <path> in the <defs> tag of the <svg>
|
||||
*
|
||||
* Then when the symbols are needed in the rest of your html document they
|
||||
* are refferd to by a <use> tag.
|
||||
* Because of bug 1076 we can't just grab the defs tag on the top and add it
|
||||
* to your formula's <svg> and copy the lot. So we have to replace each
|
||||
* <use> tag by it's <path>.
|
||||
*/
|
||||
MathJax.Hub.queue.Push(function () {
|
||||
var mathjaxMath = $('.MathJax_SVG');
|
||||
var svg = $(mathjaxMath.html());
|
||||
svg.find('use').each(function () {
|
||||
// TODO: find a less pragmatic and more elegant solution to this.
|
||||
var id = $(this).attr('href') ? $(this).attr('href').slice(1) // Works in Chrome.
|
||||
: $(this).attr('xlink:href').slice(1); // Works in Firefox.
|
||||
var glymph = $('#' + id).clone().removeAttr('id');
|
||||
var x = $(this).attr('x');
|
||||
var y = $(this).attr('y');
|
||||
var transform = $(this).attr('transform');
|
||||
if (transform && (x || y)) {
|
||||
glymph.attr('transform', transform + ' translate(' + x + ',' + y + ')');
|
||||
} else if (transform) {
|
||||
glymph.attr('transform', transform);
|
||||
} else if (x || y) {
|
||||
glymph.attr('transform', 'translate(' + x + ',' + y + ')');
|
||||
}
|
||||
$(this).replaceWith(glymph);
|
||||
});
|
||||
// Remove the style tag because it interferes with SVG-Edit.
|
||||
svg.removeAttr('style');
|
||||
svg.attr('xmlns', 'http://www.w3.org/2000/svg');
|
||||
svgCanvas.importSvgString($('<div>').append(svg.clone()).html(), true);
|
||||
svgCanvas.ungroupSelectedElement();
|
||||
// TODO: To undo the adding of the Formula you now have to undo twice.
|
||||
// This should only be once!
|
||||
svgCanvas.moveSelectedElements(locationX, locationY, true);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'MathJax',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'mathjax-icons.xml',
|
||||
buttons: [{
|
||||
id: 'tool_mathjax',
|
||||
type: 'mode',
|
||||
title: 'Add Mathematics',
|
||||
events: {
|
||||
click: function click() {
|
||||
// Only load Mathjax when needed, we don't want to strain Svg-Edit any more.
|
||||
// From this point on it is very probable that it will be needed, so load it.
|
||||
if (mathjaxLoaded === false) {
|
||||
$('<div id="mathjax">' + '<!-- Here is where MathJax creates the math -->' + '<div id="mathjax_creator" class="tex2jax_process" style="display:none">' + '$${}$$' + '</div>' + '<div id="mathjax_overlay"></div>' + '<div id="mathjax_container">' + '<div id="tool_mathjax_back" class="toolbar_button">' + '<button id="tool_mathjax_save">OK</button>' + '<button id="tool_mathjax_cancel">Cancel</button>' + '</div>' + '<fieldset>' + '<legend id="mathjax_legend">Mathematics Editor</legend>' + '<label>' + '<span id="mathjax_explication">Please type your mathematics in ' + '<a href="https://en.wikipedia.org/wiki/Help:Displaying_a_formula" target="_blank">TeX</a> code.</span></label>' + '<textarea id="mathjax_code_textarea" spellcheck="false"></textarea>' + '</fieldset>' + '</div>' + '</div>').insertAfter('#svg_prefs').hide();
|
||||
|
||||
// Make the MathEditor draggable.
|
||||
$('#mathjax_container').draggable({ cancel: 'button,fieldset', containment: 'window' });
|
||||
|
||||
// Add functionality and picture to cancel button.
|
||||
$('#tool_mathjax_cancel').prepend($.getSvgIcon('cancel', true)).on('click touched', function () {
|
||||
$('#mathjax').hide();
|
||||
});
|
||||
|
||||
// Add functionality and picture to the save button.
|
||||
$('#tool_mathjax_save').prepend($.getSvgIcon('ok', true)).on('click touched', function () {
|
||||
saveMath();
|
||||
$('#mathjax').hide();
|
||||
});
|
||||
|
||||
// MathJax preprocessing has to ignore most of the page.
|
||||
$('body').addClass('tex2jax_ignore');
|
||||
|
||||
// Now get (and run) the MathJax Library.
|
||||
// Todo: insert script with modules once widely supported
|
||||
// and if MathJax (and its `TeX-AMS-MML_SVG.js` dependency) ends up
|
||||
// providing an ES6 module export: https://github.com/mathjax/MathJax/issues/1998
|
||||
/*
|
||||
const s = document.createElement('script');
|
||||
const modularVersion = !('svgEditor' in window) ||
|
||||
!window.svgEditor ||
|
||||
window.svgEditor.modules !== false;
|
||||
if (modularVersion) {
|
||||
s.type = 'module'; // Make this the default when widely supported
|
||||
}
|
||||
s.src = curConfig.extPath + mathjaxSrcSecure;
|
||||
// See `executeAfterLoads` in `svgutils.js`
|
||||
*/
|
||||
$.getScript(mathjaxSrcSecure).done(function (script, textStatus) {
|
||||
// When MathJax is loaded get the div where the math will be rendered.
|
||||
MathJax.Hub.queue.Push(function () {
|
||||
math = MathJax.Hub.getAllJax('#mathjax_creator')[0];
|
||||
console.log(math);
|
||||
mathjaxLoaded = true;
|
||||
console.log('MathJax Loaded');
|
||||
});
|
||||
})
|
||||
// If it fails.
|
||||
.fail(function () {
|
||||
console.log('Failed loadeing MathJax.');
|
||||
$.alert('Failed loading MathJax. You will not be able to change the mathematics.');
|
||||
});
|
||||
}
|
||||
// Set the mode.
|
||||
svgCanvas.setMode('mathjax');
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
mouseDown: function mouseDown() {
|
||||
if (svgCanvas.getMode() === 'mathjax') {
|
||||
return { started: true };
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp(opts) {
|
||||
if (svgCanvas.getMode() === 'mathjax') {
|
||||
// Get the coordinates from your mouse.
|
||||
var zoom = svgCanvas.getZoom();
|
||||
// Get the actual coordinate by dividing by the zoom value
|
||||
locationX = opts.mouse_x / zoom;
|
||||
locationY = opts.mouse_y / zoom;
|
||||
|
||||
$('#mathjax').show();
|
||||
return { started: false }; // Otherwise the last selected object dissapears.
|
||||
}
|
||||
},
|
||||
callback: function callback() {
|
||||
$('<style>').text('#mathjax fieldset{' + 'padding: 5px;' + 'margin: 5px;' + 'border: 1px solid #DDD;' + '}' + '#mathjax label{' + 'display: block;' + 'margin: .5em;' + '}' + '#mathjax legend {' + 'max-width:195px;' + '}' + '#mathjax_overlay {' + 'position: absolute;' + 'top: 0;' + 'left: 0;' + 'right: 0;' + 'bottom: 0;' + 'background-color: black;' + 'opacity: 0.6;' + 'z-index: 20000;' + '}' + '#mathjax_container {' + 'position: absolute;' + 'top: 50px;' + 'padding: 10px;' + 'background-color: #B0B0B0;' + 'border: 1px outset #777;' + 'opacity: 1.0;' + 'font-family: Verdana, Helvetica, sans-serif;' + 'font-size: .8em;' + 'z-index: 20001;' + '}' + '#tool_mathjax_back {' + 'margin-left: 1em;' + 'overflow: auto;' + '}' + '#mathjax_legend{' + 'font-weight: bold;' + 'font-size:1.1em;' + '}' + '#mathjax_code_textarea {\\n' + 'margin: 5px .7em;' + 'overflow: hidden;' + 'width: 416px;' + 'display: block;' + 'height: 100px;' + '}').appendTo('head');
|
||||
|
||||
// Add the MathJax configuration.
|
||||
// $(mathjaxConfiguration).appendTo('head');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
152
dist/extensions/ext-overview_window.js
vendored
Normal file
152
dist/extensions/ext-overview_window.js
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-overview_window.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2013 James Sacksteder
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('overview_window', function (_ref) {
|
||||
var isChrome = _ref.isChrome,
|
||||
isIE = _ref.isIE;
|
||||
|
||||
var $ = jQuery;
|
||||
var overviewWindowGlobals = {};
|
||||
// Disabled in Chrome 48-, see https://github.com/SVG-Edit/svgedit/issues/26 and
|
||||
// https://code.google.com/p/chromium/issues/detail?id=565120.
|
||||
if (isChrome()) {
|
||||
var verIndex = navigator.userAgent.indexOf('Chrome/') + 7;
|
||||
var chromeVersion = parseInt(navigator.userAgent.substring(verIndex), 10);
|
||||
if (chromeVersion < 49) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Define and insert the base html element.
|
||||
var propsWindowHtml = '<div id="overview_window_content_pane" style="width:100%; word-wrap:break-word; display:inline-block; margin-top:20px;">' + '<div id="overview_window_content" style="position:relative; left:12px; top:0px;">' + '<div style="background-color:#A0A0A0; display:inline-block; overflow:visible;">' + '<svg id="overviewMiniView" width="150" height="100" x="0" y="0" viewBox="0 0 4800 3600" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">' + '<use x="0" y="0" xlink:href="#svgroot"> </use>' + '</svg>' + '<div id="overview_window_view_box" style="min-width:50px; min-height:50px; position:absolute; top:30px; left:30px; z-index:5; background-color:rgba(255,0,102,0.3);">' + '</div>' + '</div>' + '</div>' + '</div>';
|
||||
$('#sidepanels').append(propsWindowHtml);
|
||||
|
||||
// Define dynamic animation of the view box.
|
||||
var updateViewBox = function updateViewBox() {
|
||||
var portHeight = parseFloat($('#workarea').css('height'));
|
||||
var portWidth = parseFloat($('#workarea').css('width'));
|
||||
var portX = $('#workarea').scrollLeft();
|
||||
var portY = $('#workarea').scrollTop();
|
||||
var windowWidth = parseFloat($('#svgcanvas').css('width'));
|
||||
var windowHeight = parseFloat($('#svgcanvas').css('height'));
|
||||
var overviewWidth = $('#overviewMiniView').attr('width');
|
||||
var overviewHeight = $('#overviewMiniView').attr('height');
|
||||
|
||||
var viewBoxX = portX / windowWidth * overviewWidth;
|
||||
var viewBoxY = portY / windowHeight * overviewHeight;
|
||||
var viewBoxWidth = portWidth / windowWidth * overviewWidth;
|
||||
var viewBoxHeight = portHeight / windowHeight * overviewHeight;
|
||||
|
||||
$('#overview_window_view_box').css('min-width', viewBoxWidth + 'px');
|
||||
$('#overview_window_view_box').css('min-height', viewBoxHeight + 'px');
|
||||
$('#overview_window_view_box').css('top', viewBoxY + 'px');
|
||||
$('#overview_window_view_box').css('left', viewBoxX + 'px');
|
||||
};
|
||||
$('#workarea').scroll(function () {
|
||||
if (!overviewWindowGlobals.viewBoxDragging) {
|
||||
updateViewBox();
|
||||
}
|
||||
});
|
||||
$('#workarea').resize(updateViewBox);
|
||||
updateViewBox();
|
||||
|
||||
// Compensate for changes in zoom and canvas size.
|
||||
var updateViewDimensions = function updateViewDimensions() {
|
||||
var viewWidth = $('#svgroot').attr('width');
|
||||
var viewHeight = $('#svgroot').attr('height');
|
||||
|
||||
var viewX = 640;
|
||||
var viewY = 480;
|
||||
if (isIE()) {
|
||||
// This has only been tested with Firefox 10 and IE 9 (without chrome frame).
|
||||
// I am not sure if if is Firefox or IE that is being non compliant here.
|
||||
// Either way the one that is noncompliant may become more compliant later.
|
||||
// TAG:HACK
|
||||
// TAG:VERSION_DEPENDENT
|
||||
// TAG:BROWSER_SNIFFING
|
||||
viewX = 0;
|
||||
viewY = 0;
|
||||
}
|
||||
|
||||
var svgWidthOld = $('#overviewMiniView').attr('width');
|
||||
var svgHeightNew = viewHeight / viewWidth * svgWidthOld;
|
||||
$('#overviewMiniView').attr('viewBox', viewX + ' ' + viewY + ' ' + viewWidth + ' ' + viewHeight);
|
||||
$('#overviewMiniView').attr('height', svgHeightNew);
|
||||
updateViewBox();
|
||||
};
|
||||
updateViewDimensions();
|
||||
|
||||
// Set up the overview window as a controller for the view port.
|
||||
overviewWindowGlobals.viewBoxDragging = false;
|
||||
var updateViewPortFromViewBox = function updateViewPortFromViewBox() {
|
||||
var windowWidth = parseFloat($('#svgcanvas').css('width'));
|
||||
var windowHeight = parseFloat($('#svgcanvas').css('height'));
|
||||
var overviewWidth = $('#overviewMiniView').attr('width');
|
||||
var overviewHeight = $('#overviewMiniView').attr('height');
|
||||
var viewBoxX = parseFloat($('#overview_window_view_box').css('left'));
|
||||
var viewBoxY = parseFloat($('#overview_window_view_box').css('top'));
|
||||
|
||||
var portX = viewBoxX / overviewWidth * windowWidth;
|
||||
var portY = viewBoxY / overviewHeight * windowHeight;
|
||||
|
||||
$('#workarea').scrollLeft(portX);
|
||||
$('#workarea').scrollTop(portY);
|
||||
};
|
||||
$('#overview_window_view_box').draggable({
|
||||
containment: 'parent',
|
||||
drag: updateViewPortFromViewBox,
|
||||
start: function start() {
|
||||
overviewWindowGlobals.viewBoxDragging = true;
|
||||
},
|
||||
stop: function stop() {
|
||||
overviewWindowGlobals.viewBoxDragging = false;
|
||||
}
|
||||
});
|
||||
$('#overviewMiniView').click(function (evt) {
|
||||
// Firefox doesn't support evt.offsetX and evt.offsetY.
|
||||
var mouseX = evt.offsetX || evt.originalEvent.layerX;
|
||||
var mouseY = evt.offsetY || evt.originalEvent.layerY;
|
||||
var overviewWidth = $('#overviewMiniView').attr('width');
|
||||
var overviewHeight = $('#overviewMiniView').attr('height');
|
||||
var viewBoxWidth = parseFloat($('#overview_window_view_box').css('min-width'));
|
||||
var viewBoxHeight = parseFloat($('#overview_window_view_box').css('min-height'));
|
||||
|
||||
var viewBoxX = mouseX - 0.5 * viewBoxWidth;
|
||||
var viewBoxY = mouseY - 0.5 * viewBoxHeight;
|
||||
// deal with constraints
|
||||
if (viewBoxX < 0) {
|
||||
viewBoxX = 0;
|
||||
}
|
||||
if (viewBoxY < 0) {
|
||||
viewBoxY = 0;
|
||||
}
|
||||
if (viewBoxX + viewBoxWidth > overviewWidth) {
|
||||
viewBoxX = overviewWidth - viewBoxWidth;
|
||||
}
|
||||
if (viewBoxY + viewBoxHeight > overviewHeight) {
|
||||
viewBoxY = overviewHeight - viewBoxHeight;
|
||||
}
|
||||
|
||||
$('#overview_window_view_box').css('top', viewBoxY + 'px');
|
||||
$('#overview_window_view_box').css('left', viewBoxX + 'px');
|
||||
updateViewPortFromViewBox();
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'overview window',
|
||||
canvasUpdated: updateViewDimensions,
|
||||
workareaResized: updateViewBox
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
50
dist/extensions/ext-panning.js
vendored
Normal file
50
dist/extensions/ext-panning.js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* ext-panning.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2013 Luis Aguirre
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
This is a very basic SVG-Edit extension to let tablet/mobile devices panning without problem
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('ext-panning', function () {
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
return {
|
||||
name: 'Extension Panning',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'ext-panning.xml',
|
||||
buttons: [{
|
||||
id: 'ext-panning',
|
||||
type: 'mode',
|
||||
title: 'Panning',
|
||||
events: {
|
||||
click: function click() {
|
||||
svgCanvas.setMode('ext-panning');
|
||||
}
|
||||
}
|
||||
}],
|
||||
mouseDown: function mouseDown() {
|
||||
if (svgCanvas.getMode() === 'ext-panning') {
|
||||
svgEditor.setPanning(true);
|
||||
return { started: true };
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp() {
|
||||
if (svgCanvas.getMode() === 'ext-panning') {
|
||||
svgEditor.setPanning(false);
|
||||
return {
|
||||
keep: false,
|
||||
element: null
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
28
dist/extensions/ext-php_savefile.js
vendored
Normal file
28
dist/extensions/ext-php_savefile.js
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
// TODO: Might add support for "exportImage" custom
|
||||
// handler as in "ext-server_opensave.js" (and in savefile.php)
|
||||
|
||||
svgEditor.addExtension('php_savefile', {
|
||||
callback: function callback() {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
function getFileNameFromTitle() {
|
||||
var title = svgCanvas.getDocumentTitle();
|
||||
return title.trim();
|
||||
}
|
||||
var saveSvgAction = svgEditor.curConfig.extPath + 'savefile.php';
|
||||
svgEditor.setCustomHandlers({
|
||||
save: function save(win, data) {
|
||||
var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + data,
|
||||
filename = getFileNameFromTitle();
|
||||
|
||||
$.post(saveSvgAction, { output_svg: svg, filename: filename });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
275
dist/extensions/ext-polygon.js
vendored
Normal file
275
dist/extensions/ext-polygon.js
vendored
Normal file
@@ -0,0 +1,275 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-polygon.js
|
||||
*
|
||||
*
|
||||
* Copyright(c) 2010 CloudCanvas, Inc.
|
||||
* All rights reserved
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('polygon', function (S) {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var selElems = void 0,
|
||||
|
||||
// svgdoc = S.svgroot.parentNode.ownerDocument,
|
||||
// newFOG, newFOGParent, newDef, newImageName, newMaskID, modeChangeG,
|
||||
// edg = 0,
|
||||
// undoCommand = 'Not image';
|
||||
started = void 0,
|
||||
newFO = void 0;
|
||||
|
||||
// const ccZoom;
|
||||
// const wEl, hEl;
|
||||
// const wOffset, hOffset;
|
||||
// const ccRBG;
|
||||
// const ccOpacity;
|
||||
// const brushW, brushH;
|
||||
|
||||
// const ccDebug = document.getElementById('debugpanel');
|
||||
|
||||
/* const properlySourceSizeTextArea = function(){
|
||||
// TODO: remove magic numbers here and get values from CSS
|
||||
const height = $('#svg_source_container').height() - 80;
|
||||
$('#svg_source_textarea').css('height', height);
|
||||
}; */
|
||||
function showPanel(on) {
|
||||
var fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
fcRules = $('<style id="fc_rules"></style>').appendTo('head');
|
||||
}
|
||||
fcRules.text(!on ? '' : ' #tool_topath { display: none !important; }');
|
||||
$('#polygon_panel').toggle(on);
|
||||
}
|
||||
|
||||
/*
|
||||
function toggleSourceButtons(on){
|
||||
$('#tool_source_save, #tool_source_cancel').toggle(!on);
|
||||
$('#polygon_save, #polygon_cancel').toggle(on);
|
||||
}
|
||||
*/
|
||||
|
||||
function setAttr(attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
S.call('changed', selElems);
|
||||
}
|
||||
|
||||
function cot(n) {
|
||||
return 1 / Math.tan(n);
|
||||
}
|
||||
|
||||
function sec(n) {
|
||||
return 1 / Math.cos(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtained from http://code.google.com/p/passenger-top/source/browse/instiki/public/svg-edit/editor/extensions/ext-itex.js?r=3
|
||||
* This function sets the content of of the currently-selected foreignObject element,
|
||||
* based on the itex contained in string.
|
||||
* @param {string} tex The itex text.
|
||||
* @returns This function returns false if the set was unsuccessful, true otherwise.
|
||||
*/
|
||||
/*
|
||||
function setItexString(tex) {
|
||||
const mathns = 'http://www.w3.org/1998/Math/MathML',
|
||||
xmlnsns = 'http://www.w3.org/2000/xmlns/',
|
||||
ajaxEndpoint = '../../itex';
|
||||
const elt = selElems[0];
|
||||
try {
|
||||
const math = svgdoc.createElementNS(mathns, 'math');
|
||||
math.setAttributeNS(xmlnsns, 'xmlns', mathns);
|
||||
math.setAttribute('display', 'inline');
|
||||
const semantics = document.createElementNS(mathns, 'semantics');
|
||||
const annotation = document.createElementNS(mathns, 'annotation');
|
||||
annotation.setAttribute('encoding', 'application/x-tex');
|
||||
annotation.textContent = tex;
|
||||
const mrow = document.createElementNS(mathns, 'mrow');
|
||||
semantics.appendChild(mrow);
|
||||
semantics.appendChild(annotation);
|
||||
math.appendChild(semantics);
|
||||
// make an AJAX request to the server, to get the MathML
|
||||
$.post(ajaxEndpoint, {tex, display: 'inline'}, function(data){
|
||||
const children = data.documentElement.childNodes;
|
||||
while (children.length > 0) {
|
||||
mrow.appendChild(svgdoc.adoptNode(children[0], true));
|
||||
}
|
||||
S.sanitizeSvg(math);
|
||||
S.call('changed', [elt]);
|
||||
});
|
||||
elt.replaceChild(math, elt.firstChild);
|
||||
S.call('changed', [elt]);
|
||||
svgCanvas.clearSelection();
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
return {
|
||||
name: 'polygon',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'polygon-icons.svg',
|
||||
buttons: [{
|
||||
id: 'tool_polygon',
|
||||
type: 'mode',
|
||||
title: 'Polygon Tool',
|
||||
position: 11,
|
||||
events: {
|
||||
click: function click() {
|
||||
svgCanvas.setMode('polygon');
|
||||
showPanel(true);
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
context_tools: [{
|
||||
type: 'input',
|
||||
panel: 'polygon_panel',
|
||||
title: 'Number of Sides',
|
||||
id: 'polySides',
|
||||
label: 'sides',
|
||||
size: 3,
|
||||
defval: 5,
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('sides', this.value);
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
callback: function callback() {
|
||||
$('#polygon_panel').hide();
|
||||
|
||||
// TODO: Needs to be done after orig icon loads
|
||||
setTimeout(function () {
|
||||
// Create source save/cancel buttons
|
||||
/* const save = */$('#tool_source_save').clone().hide().attr('id', 'polygon_save').unbind().appendTo('#tool_source_back').click(function () {
|
||||
{
|
||||
return;
|
||||
}
|
||||
// }
|
||||
// setSelectMode();
|
||||
});
|
||||
|
||||
/* const cancel = */$('#tool_source_cancel').clone().hide().attr('id', 'polygon_cancel').unbind().appendTo('#tool_source_back').click(function () {
|
||||
});
|
||||
}, 3000);
|
||||
},
|
||||
mouseDown: function mouseDown(opts) {
|
||||
// const e = opts.event;
|
||||
var rgb = svgCanvas.getColor('fill');
|
||||
// const ccRgbEl = rgb.substring(1, rgb.length);
|
||||
var sRgb = svgCanvas.getColor('stroke');
|
||||
// ccSRgbEl = sRgb.substring(1, rgb.length);
|
||||
var sWidth = svgCanvas.getStrokeWidth();
|
||||
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
started = true;
|
||||
|
||||
newFO = S.addSvgElementFromJson({
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: S.getNextId(),
|
||||
shape: 'regularPoly',
|
||||
sides: document.getElementById('polySides').value,
|
||||
orient: 'x',
|
||||
edge: 0,
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseMove: function mouseMove(opts) {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
// const e = opts.event;
|
||||
var c = $(newFO).attr(['cx', 'cy', 'sides', 'orient', 'fill', 'strokecolor', 'strokeWidth']);
|
||||
var x = opts.mouse_x;
|
||||
var y = opts.mouse_y;
|
||||
var cx = c.cx,
|
||||
cy = c.cy,
|
||||
fill = c.fill,
|
||||
strokecolor = c.strokecolor,
|
||||
strokeWidth = c.strokeWidth,
|
||||
sides = c.sides,
|
||||
edg = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5;
|
||||
|
||||
newFO.setAttributeNS(null, 'edge', edg);
|
||||
|
||||
var inradius = edg / 2 * cot(Math.PI / sides);
|
||||
var circumradius = inradius * sec(Math.PI / sides);
|
||||
var points = '';
|
||||
for (var s = 0; sides >= s; s++) {
|
||||
var angle = 2.0 * Math.PI * s / sides;
|
||||
x = circumradius * Math.cos(angle) + cx;
|
||||
y = circumradius * Math.sin(angle) + cy;
|
||||
|
||||
points += x + ',' + y + ' ';
|
||||
}
|
||||
|
||||
// const poly = newFO.createElementNS(NS.SVG, 'polygon');
|
||||
newFO.setAttributeNS(null, 'points', points);
|
||||
newFO.setAttributeNS(null, 'fill', fill);
|
||||
newFO.setAttributeNS(null, 'stroke', strokecolor);
|
||||
newFO.setAttributeNS(null, 'stroke-width', strokeWidth);
|
||||
// newFO.setAttributeNS(null, 'transform', 'rotate(-90)');
|
||||
// const shape = newFO.getAttributeNS(null, 'shape');
|
||||
// newFO.appendChild(poly);
|
||||
// DrawPoly(cx, cy, sides, edg, orient);
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp(opts) {
|
||||
if (svgCanvas.getMode() === 'polygon') {
|
||||
var attrs = $(newFO).attr('edge');
|
||||
var keep = attrs.edge !== '0';
|
||||
// svgCanvas.addToSelection([newFO], true);
|
||||
return {
|
||||
keep: keep,
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && elem.getAttributeNS(null, 'shape') === 'regularPoly') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
$('#polySides').val(elem.getAttribute('sides'));
|
||||
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
// const elem = opts.elems[0];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
63
dist/extensions/ext-server_moinsave.js
vendored
Normal file
63
dist/extensions/ext-server_moinsave.js
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-server_moinsave.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
* 2011 MoinMoin:ReimarBauer
|
||||
* adopted for moinmoins item storage. it sends in one post png and svg data
|
||||
* (I agree to dual license my work to additional GPLv2 or later)
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('server_opensave', {
|
||||
callback: function callback(_ref) {
|
||||
var canvg = _ref.canvg,
|
||||
encode64 = _ref.encode64,
|
||||
buildCanvgCallback = _ref.buildCanvgCallback;
|
||||
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
var saveSvgAction = '/+modify';
|
||||
|
||||
// Create upload target (hidden iframe)
|
||||
/* const target = */$('<iframe name="output_frame" src="#"/>').hide().appendTo('body');
|
||||
|
||||
svgEditor.setCustomHandlers({
|
||||
save: function save(win, data) {
|
||||
var svg = '<?xml version="1.0"?>\n' + data;
|
||||
var qstr = $.param.querystring();
|
||||
var name = qstr.substr(9).split('/+get/')[1];
|
||||
var svgData = encode64(svg);
|
||||
if (!$('#export_canvas').length) {
|
||||
$('<canvas>', { id: 'export_canvas' }).hide().appendTo('body');
|
||||
}
|
||||
var c = $('#export_canvas')[0];
|
||||
c.width = svgCanvas.contentW;
|
||||
c.height = svgCanvas.contentH;
|
||||
buildCanvgCallback(function () {
|
||||
canvg(c, svg, {
|
||||
renderCallback: function renderCallback() {
|
||||
var datauri = c.toDataURL('image/png');
|
||||
// const {uiStrings} = svgEditor;
|
||||
var pngData = encode64(datauri); // Brett: This encoding seems unnecessary
|
||||
/* const form = */$('<form>').attr({
|
||||
method: 'post',
|
||||
action: saveSvgAction + '/' + name,
|
||||
target: 'output_frame'
|
||||
}).append('<input type="hidden" name="png_data" value="' + pngData + '">').append('<input type="hidden" name="filepath" value="' + svgData + '">').append('<input type="hidden" name="filename" value="' + 'drawing.svg">').append('<input type="hidden" name="contenttype" value="application/x-svgdraw">').appendTo('body').submit().remove();
|
||||
}
|
||||
});
|
||||
})();
|
||||
alert('Saved! Return to Item View!');
|
||||
top.window.location = '/' + name;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
232
dist/extensions/ext-server_opensave.js
vendored
Normal file
232
dist/extensions/ext-server_opensave.js
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-server_opensave.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('server_opensave', {
|
||||
callback: function callback(_ref) {
|
||||
var canvg = _ref.canvg,
|
||||
decode64 = _ref.decode64,
|
||||
encode64 = _ref.encode64,
|
||||
buildCanvgCallback = _ref.buildCanvgCallback;
|
||||
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
function getFileNameFromTitle() {
|
||||
var title = svgCanvas.getDocumentTitle();
|
||||
// We convert (to underscore) only those disallowed Win7 file name characters
|
||||
return title.trim().replace(/[/\\:*?"<>|]/g, '_');
|
||||
}
|
||||
function xhtmlEscape(str) {
|
||||
return str.replace(/&(?!amp;)/g, '&').replace(/"/g, '"').replace(/</g, '<'); // < is actually disallowed above anyways
|
||||
}
|
||||
function clientDownloadSupport(filename, suffix, uri) {
|
||||
var support = $('<a>')[0].download === '';
|
||||
var a = void 0;
|
||||
if (support) {
|
||||
a = $('<a>hidden</a>').attr({
|
||||
download: (filename || 'image') + suffix,
|
||||
href: uri
|
||||
}).css('display', 'none').appendTo('body');
|
||||
a[0].click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
var saveSvgAction = svgEditor.curConfig.extPath + 'filesave.php',
|
||||
saveImgAction = svgEditor.curConfig.extPath + 'filesave.php';
|
||||
// Create upload target (hidden iframe)
|
||||
|
||||
var cancelled = false;
|
||||
|
||||
$('<iframe name="output_frame" src="#"/>').hide().appendTo('body');
|
||||
svgEditor.setCustomHandlers({
|
||||
save: function save(win, data) {
|
||||
var svg = '<?xml version="1.0" encoding="UTF-8"?>\n' + data,
|
||||
// Firefox doesn't seem to know it is UTF-8 (no matter whether we use or skip the clientDownload code) despite the Content-Disposition header containing UTF-8, but adding the encoding works
|
||||
filename = getFileNameFromTitle();
|
||||
|
||||
if (clientDownloadSupport(filename, '.svg', 'data:image/svg+xml;charset=UTF-8;base64,' + encode64(svg))) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('<form>').attr({
|
||||
method: 'post',
|
||||
action: saveSvgAction,
|
||||
target: 'output_frame'
|
||||
}).append('<input type="hidden" name="output_svg" value="' + xhtmlEscape(svg) + '">').append('<input type="hidden" name="filename" value="' + xhtmlEscape(filename) + '">').appendTo('body').submit().remove();
|
||||
},
|
||||
exportPDF: function exportPDF(win, data) {
|
||||
var filename = getFileNameFromTitle(),
|
||||
datauri = data.dataurlstring;
|
||||
if (clientDownloadSupport(filename, '.pdf', datauri)) {
|
||||
return;
|
||||
}
|
||||
$('<form>').attr({
|
||||
method: 'post',
|
||||
action: saveImgAction,
|
||||
target: 'output_frame'
|
||||
}).append('<input type="hidden" name="output_img" value="' + datauri + '">').append('<input type="hidden" name="mime" value="application/pdf">').append('<input type="hidden" name="filename" value="' + xhtmlEscape(filename) + '">').appendTo('body').submit().remove();
|
||||
},
|
||||
|
||||
// Todo: Integrate this extension with a new built-in exportWindowType, "download"
|
||||
exportImage: function exportImage(win, data) {
|
||||
var issues = data.issues,
|
||||
mimeType = data.mimeType,
|
||||
quality = data.quality;
|
||||
|
||||
|
||||
if (!$('#export_canvas').length) {
|
||||
$('<canvas>', { id: 'export_canvas' }).hide().appendTo('body');
|
||||
}
|
||||
var c = $('#export_canvas')[0];
|
||||
|
||||
c.width = svgCanvas.contentW;
|
||||
c.height = svgCanvas.contentH;
|
||||
buildCanvgCallback(function () {
|
||||
canvg(c, data.svg, {
|
||||
renderCallback: function renderCallback() {
|
||||
var datauri = quality ? c.toDataURL(mimeType, quality) : c.toDataURL(mimeType);
|
||||
// {uiStrings} = svgEditor;
|
||||
|
||||
// Check if there are issues
|
||||
var pre = void 0,
|
||||
note = '';
|
||||
if (issues.length) {
|
||||
pre = '\n \u2022 ';
|
||||
note += '\n\n' + pre + issues.join(pre);
|
||||
}
|
||||
|
||||
if (note.length) {
|
||||
alert(note);
|
||||
}
|
||||
|
||||
var filename = getFileNameFromTitle();
|
||||
var suffix = '.' + data.type.toLowerCase();
|
||||
|
||||
if (clientDownloadSupport(filename, suffix, datauri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('<form>').attr({
|
||||
method: 'post',
|
||||
action: saveImgAction,
|
||||
target: 'output_frame'
|
||||
}).append('<input type="hidden" name="output_img" value="' + datauri + '">').append('<input type="hidden" name="mime" value="' + mimeType + '">').append('<input type="hidden" name="filename" value="' + xhtmlEscape(filename) + '">').appendTo('body').submit().remove();
|
||||
}
|
||||
});
|
||||
})();
|
||||
}
|
||||
});
|
||||
|
||||
// Do nothing if client support is found
|
||||
if (window.FileReader) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Change these to appropriate script file
|
||||
var openSvgAction = svgEditor.curConfig.extPath + 'fileopen.php?type=load_svg';
|
||||
var importSvgAction = svgEditor.curConfig.extPath + 'fileopen.php?type=import_svg';
|
||||
var importImgAction = svgEditor.curConfig.extPath + 'fileopen.php?type=import_img';
|
||||
|
||||
// Set up function for PHP uploader to use
|
||||
svgEditor.processFile = function (str64, type) {
|
||||
var xmlstr = void 0;
|
||||
if (cancelled) {
|
||||
cancelled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
$('#dialog_box').hide();
|
||||
|
||||
if (type !== 'import_img') {
|
||||
xmlstr = decode64(str64);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'load_svg':
|
||||
svgCanvas.clear();
|
||||
svgCanvas.setSvgString(xmlstr);
|
||||
svgEditor.updateCanvas();
|
||||
break;
|
||||
case 'import_svg':
|
||||
svgCanvas.importSvgString(xmlstr);
|
||||
svgEditor.updateCanvas();
|
||||
break;
|
||||
case 'import_img':
|
||||
svgCanvas.setGoodImage(str64);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Create upload form
|
||||
var openSvgForm = $('<form>');
|
||||
openSvgForm.attr({
|
||||
enctype: 'multipart/form-data',
|
||||
method: 'post',
|
||||
action: openSvgAction,
|
||||
target: 'output_frame'
|
||||
});
|
||||
|
||||
// Create import form
|
||||
var importSvgForm = openSvgForm.clone().attr('action', importSvgAction);
|
||||
|
||||
// Create image form
|
||||
var importImgForm = openSvgForm.clone().attr('action', importImgAction);
|
||||
|
||||
// It appears necessary to rebuild this input every time a file is
|
||||
// selected so the same file can be picked and the change event can fire.
|
||||
function rebuildInput(form) {
|
||||
form.empty();
|
||||
var inp = $('<input type="file" name="svg_file">').appendTo(form);
|
||||
|
||||
function submit() {
|
||||
// This submits the form, which returns the file data using svgEditor.processFile()
|
||||
form.submit();
|
||||
|
||||
rebuildInput(form);
|
||||
$.process_cancel('Uploading...', function () {
|
||||
cancelled = true;
|
||||
$('#dialog_box').hide();
|
||||
});
|
||||
}
|
||||
|
||||
if (form[0] === openSvgForm[0]) {
|
||||
inp.change(function () {
|
||||
// This takes care of the "are you sure" dialog box
|
||||
svgEditor.openPrep(function (ok) {
|
||||
if (!ok) {
|
||||
rebuildInput(form);
|
||||
return;
|
||||
}
|
||||
submit();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
inp.change(function () {
|
||||
// This submits the form, which returns the file data using svgEditor.processFile()
|
||||
submit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create the input elements
|
||||
rebuildInput(openSvgForm);
|
||||
rebuildInput(importSvgForm);
|
||||
rebuildInput(importImgForm);
|
||||
|
||||
// Add forms to buttons
|
||||
$('#tool_open').show().prepend(openSvgForm);
|
||||
$('#tool_import').show().prepend(importSvgForm);
|
||||
$('#tool_image').prepend(importImgForm);
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
349
dist/extensions/ext-shapes.js
vendored
Normal file
349
dist/extensions/ext-shapes.js
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-shapes.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2010 Christian Tzurcanu
|
||||
* Copyright(c) 2010 Alexis Deveria
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('shapes', function () {
|
||||
var $ = jQuery;
|
||||
var canv = svgEditor.canvas;
|
||||
var svgroot = canv.getRootElem();
|
||||
var lastBBox = {};
|
||||
|
||||
// This populates the category list
|
||||
var categories = {
|
||||
basic: 'Basic',
|
||||
object: 'Objects',
|
||||
symbol: 'Symbols',
|
||||
arrow: 'Arrows',
|
||||
flowchart: 'Flowchart',
|
||||
animal: 'Animals',
|
||||
game: 'Cards & Chess',
|
||||
dialog_balloon: 'Dialog balloons',
|
||||
electronics: 'Electronics',
|
||||
math: 'Mathematical',
|
||||
music: 'Music',
|
||||
misc: 'Miscellaneous',
|
||||
raphael_1: 'raphaeljs.com set 1',
|
||||
raphael_2: 'raphaeljs.com set 2'
|
||||
};
|
||||
|
||||
var library = {
|
||||
basic: {
|
||||
data: {
|
||||
heart: 'm150,73c61,-175 300,0 0,225c-300,-225 -61,-400 0,-225z',
|
||||
frame: 'm0,0l300,0l0,300l-300,0zm35,-265l0,230l230,0l0,-230z',
|
||||
donut: 'm1,150l0,0c0,-82.29042 66.70958,-149 149,-149l0,0c39.51724,0 77.41599,15.69816 105.35889,43.64108c27.94293,27.94293 43.64111,65.84165 43.64111,105.35892l0,0c0,82.29041 -66.70958,149 -149,149l0,0c-82.29041,0 -149,-66.70959 -149,-149zm74.5,0l0,0c0,41.1452 33.35481,74.5 74.5,74.5c41.14522,0 74.5,-33.3548 74.5,-74.5c0,-41.1452 -33.3548,-74.5 -74.5,-74.5l0,0c-41.14519,0 -74.5,33.35481 -74.5,74.5z',
|
||||
triangle: 'm1,280.375l149,-260.75l149,260.75z',
|
||||
right_triangle: 'm1,299l0,-298l298,298z',
|
||||
diamond: 'm1,150l149,-149l149,149l-149,149l-149,-149z',
|
||||
pentagon: 'm1.00035,116.97758l148.99963,-108.4053l148.99998,108.4053l-56.91267,175.4042l-184.1741,0l-56.91284,-175.4042z',
|
||||
hexagon: 'm1,149.99944l63.85715,-127.71428l170.28572,0l63.85713,127.71428l-63.85713,127.71428l-170.28572,0l-63.85715,-127.71428z',
|
||||
septagon1: 'm0.99917,191.06511l29.51249,-127.7108l119.48833,-56.83673l119.48836,56.83673l29.51303,127.7108l-82.69087,102.41679l-132.62103,0l-82.69031,-102.41679z',
|
||||
heptagon: 'm1,88.28171l87.28172,-87.28171l123.43653,0l87.28172,87.28171l0,123.43654l-87.28172,87.28172l-123.43653,0l-87.28172,-87.28172l0,-123.43654z',
|
||||
decagon: 'm1,150.00093l28.45646,-88.40318l74.49956,-54.63682l92.08794,0l74.50002,54.63682l28.45599,88.40318l-28.45599,88.40318l-74.50002,54.63681l-92.08794,0l-74.49956,-54.63681l-28.45646,-88.40318z',
|
||||
dodecagon: 'm1,110.07421l39.92579,-69.14842l69.14842,-39.92579l79.85159,0l69.14842,39.92579l39.92578,69.14842l0,79.85159l-39.92578,69.14842l-69.14842,39.92578l-79.85159,0l-69.14842,-39.92578l-39.92579,-69.14842l0,-79.85159z',
|
||||
star_points_5: 'm1,116.58409l113.82668,0l35.17332,-108.13487l35.17334,108.13487l113.82666,0l-92.08755,66.83026l35.17514,108.13487l-92.08759,-66.83208l-92.08757,66.83208l35.17515,-108.13487l-92.08758,-66.83026z',
|
||||
trapezoid: 'm1,299l55.875,-298l186.25001,0l55.87498,298z',
|
||||
arrow_up: 'm1.49805,149.64304l148.50121,-148.00241l148.50121,148.00241l-74.25061,0l0,148.71457l-148.5012,0l0,-148.71457z',
|
||||
vertical_scrool: 'm37.375,261.625l0,-242.9375l0,0c0,-10.32083 8.36669,-18.6875 18.6875,-18.6875l224.25,0c10.32083,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36667,18.6875 -18.6875,18.6875l-18.6875,0l0,242.9375c0,10.32083 -8.36668,18.6875 -18.6875,18.6875l-224.25,0l0,0c-10.32083,0 -18.6875,-8.36667 -18.6875,-18.6875c0,-10.32083 8.36667,-18.6875 18.6875,-18.6875zm37.375,-261.625l0,0c10.32081,0 18.6875,8.36667 18.6875,18.6875c0,10.32081 -8.36669,18.6875 -18.6875,18.6875c-5.1604,0 -9.34375,-4.18335 -9.34375,-9.34375c0,-5.16041 4.18335,-9.34375 9.34375,-9.34375l18.6875,0m186.875,18.6875l-205.5625,0m-37.375,224.25l0,0c5.1604,0 9.34375,4.18335 9.34375,9.34375c0,5.1604 -4.18335,9.34375 -9.34375,9.34375l18.6875,0m-18.6875,18.6875l0,0c10.32081,0 18.6875,-8.36667 18.6875,-18.6875l0,-18.6875',
|
||||
smiley: 'm68.49886,214.78838q81.06408,55.67332 161.93891,0m-144.36983,-109.9558c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57948,6.97517 15.57948,15.57949c0,8.60431 -6.97517,15.57947 -15.57948,15.57947c-8.60431,0 -15.57948,-6.97516 -15.57948,-15.57947m95.83109,0c0,-8.60432 6.97517,-15.57949 15.57948,-15.57949c8.60431,0 15.57947,6.97517 15.57947,15.57949c0,8.60431 -6.97516,15.57947 -15.57947,15.57947c-8.60429,0 -15.57948,-6.97516 -15.57948,-15.57947m-181.89903,44.73038l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296zm0,0l0,0c0,-82.60133 66.96162,-149.56296 149.56296,-149.56296c82.60135,0 149.56296,66.96162 149.56296,149.56296c0,82.60135 -66.96161,149.56296 -149.56296,149.56296c-82.60133,0 -149.56296,-66.96161 -149.56296,-149.56296z',
|
||||
left_braket: 'm174.24565,298.5c-13.39009,0 -24.24489,-1.80908 -24.24489,-4.04065l0,-140.4187c0,-2.23158 -10.85481,-4.04065 -24.2449,-4.04065l0,0c13.39009,0 24.2449,-1.80907 24.2449,-4.04065l0,-140.4187l0,0c0,-2.23159 10.8548,-4.04066 24.24489,-4.04066',
|
||||
uml_actor: 'm40.5,100l219,0m-108.99991,94.00006l107,105m-107.00009,-106.00006l-100,106m99.5,-231l0,125m33.24219,-158.75781c0,18.35916 -14.88303,33.24219 -33.24219,33.24219c-18.35916,0 -33.2422,-14.88303 -33.2422,-33.24219c0.00002,-18.35915 14.88304,-33.24219 33.2422,-33.24219c18.35916,0 33.24219,14.88304 33.24219,33.24219z',
|
||||
dialog_balloon_1: 'm0.99786,35.96579l0,0c0,-19.31077 15.28761,-34.96524 34.14583,-34.96524l15.52084,0l0,0l74.50001,0l139.68748,0c9.05606,0 17.74118,3.68382 24.14478,10.24108c6.40356,6.55726 10.00107,15.45081 10.00107,24.72416l0,87.41311l0,0l0,52.44785l0,0c0,19.31078 -15.2876,34.96524 -34.14584,34.96524l-139.68748,0l-97.32507,88.90848l22.82506,-88.90848l-15.52084,0c-18.85822,0 -34.14583,-15.65446 -34.14583,-34.96524l0,0l0,-52.44785l0,0z',
|
||||
cloud: 'm182.05086,34.31005c-0.64743,0.02048 -1.27309,0.07504 -1.92319,0.13979c-10.40161,1.03605 -19.58215,7.63722 -24.24597,17.4734l-2.47269,7.44367c0.53346,-2.57959 1.35258,-5.08134 2.47269,-7.44367c-8.31731,-8.61741 -19.99149,-12.59487 -31.52664,-10.72866c-11.53516,1.8662 -21.55294,9.3505 -27.02773,20.19925c-15.45544,-9.51897 -34.72095,-8.94245 -49.62526,1.50272c-14.90431,10.44516 -22.84828,28.93916 -20.43393,47.59753l1.57977,7.58346c-0.71388,-2.48442 -1.24701,-5.01186 -1.57977,-7.58346l-0.2404,0.69894c-12.95573,1.4119 -23.58103,11.46413 -26.34088,24.91708c-2.75985,13.45294 2.9789,27.25658 14.21789,34.21291l17.54914,4.26352c-6.1277,0.50439 -12.24542,-0.9808 -17.54914,-4.26352c-8.66903,9.71078 -10.6639,24.08736 -4.94535,35.96027c5.71854,11.87289 17.93128,18.70935 30.53069,17.15887l7.65843,-2.02692c-2.46413,1.0314 -5.02329,1.70264 -7.65843,2.02692c7.15259,13.16728 19.01251,22.77237 32.93468,26.5945c13.92217,3.82214 28.70987,1.56322 41.03957,-6.25546c10.05858,15.86252 27.91113,24.19412 45.81322,21.38742c17.90208,-2.8067 32.66954,-16.26563 37.91438,-34.52742l1.82016,-10.20447c-0.27254,3.46677 -0.86394,6.87508 -1.82016,10.20447c12.31329,8.07489 27.80199,8.52994 40.52443,1.18819c12.72244,-7.34175 20.6609,-21.34155 20.77736,-36.58929l-4.56108,-22.7823l-17.96776,-15.41455c13.89359,8.70317 22.6528,21.96329 22.52884,38.19685c16.5202,0.17313 30.55292,-13.98268 36.84976,-30.22897c6.29684,-16.24631 3.91486,-34.76801 -6.2504,-48.68089c4.21637,-10.35873 3.96622,-22.14172 -0.68683,-32.29084c-4.65308,-10.14912 -13.23602,-17.69244 -23.55914,-20.65356c-2.31018,-13.45141 -11.83276,-24.27162 -24.41768,-27.81765c-12.58492,-3.54603 -25.98557,0.82654 -34.41142,11.25287l-5.11707,8.63186c1.30753,-3.12148 3.01521,-6.03101 5.11707,-8.63186c-5.93959,-8.19432 -15.2556,-12.8181 -24.96718,-12.51096z',
|
||||
cylinder: 'm299.0007,83.77844c0,18.28676 -66.70958,33.11111 -149.00002,33.11111m149.00002,-33.11111l0,0c0,18.28676 -66.70958,33.11111 -149.00002,33.11111c-82.29041,0 -148.99997,-14.82432 -148.99997,-33.11111m0,0l0,0c0,-18.28674 66.70956,-33.1111 148.99997,-33.1111c82.29044,0 149.00002,14.82436 149.00002,33.1111l0,132.44449c0,18.28674 -66.70958,33.11105 -149.00002,33.11105c-82.29041,0 -148.99997,-14.82431 -148.99997,-33.11105z',
|
||||
arrow_u_turn: 'm1.00059,299.00055l0,-167.62497l0,0c0,-72.00411 58.37087,-130.37499 130.375,-130.37499l0,0l0,0c34.57759,0 67.73898,13.7359 92.18906,38.18595c24.45006,24.45005 38.18593,57.61144 38.18593,92.18904l0,18.625l37.24997,0l-74.49995,74.50002l-74.50002,-74.50002l37.25,0l0,-18.625c0,-30.8589 -25.0161,-55.87498 -55.87498,-55.87498l0,0l0,0c-30.85892,0 -55.875,25.01608 -55.875,55.87498l0,167.62497z',
|
||||
arrow_left_up: 'm0.99865,224.5l74.50004,-74.5l0,37.25l111.74991,0l0,-111.75l-37.25,0l74.5,-74.5l74.5,74.5l-37.25,0l0,186.25l-186.24989,0l0,37.25l-74.50005,-74.5z',
|
||||
maximize: 'm1.00037,150.34581l55.30305,-55.30267l0,27.65093l22.17356,0l0,-44.21833l44.21825,0l0,-22.17357l-27.65095,0l55.30267,-55.30292l55.3035,55.30292l-27.65175,0l0,22.17357l44.21835,0l0,44.21833l22.17357,0l0,-27.65093l55.30345,55.30267l-55.30345,55.3035l0,-27.65175l-22.17357,0l0,44.21834l-44.21835,0l0,22.17355l27.65175,0l-55.3035,55.30348l-55.30267,-55.30348l27.65095,0l0,-22.17355l-44.21825,0l0,-44.21834l-22.17356,0l0,27.65175l-55.30305,-55.3035z',
|
||||
cross: 'm0.99844,99.71339l98.71494,0l0,-98.71495l101.26279,0l0,98.71495l98.71495,0l0,101.2628l-98.71495,0l0,98.71494l-101.26279,0l0,-98.71494l-98.71494,0z',
|
||||
plaque: 'm-0.00197,49.94376l0,0c27.5829,0 49.94327,-22.36036 49.94327,-49.94327l199.76709,0l0,0c0,27.5829 22.36037,49.94327 49.94325,49.94327l0,199.7671l0,0c-27.58289,0 -49.94325,22.36034 -49.94325,49.94325l-199.76709,0c0,-27.58292 -22.36037,-49.94325 -49.94327,-49.94325z',
|
||||
page: 'm249.3298,298.99744l9.9335,-39.73413l39.73413,-9.93355l-49.66763,49.66768l-248.33237,0l0,-298.00001l298.00001,0l0,248.33234'
|
||||
|
||||
},
|
||||
buttons: []
|
||||
}
|
||||
};
|
||||
|
||||
var modeId = 'shapelib';
|
||||
var startClientPos = {};
|
||||
|
||||
var currentD = void 0,
|
||||
curShapeId = void 0,
|
||||
curShape = void 0,
|
||||
startX = void 0,
|
||||
startY = void 0;
|
||||
var curLib = library.basic;
|
||||
|
||||
function loadIcons() {
|
||||
$('#shape_buttons').empty().append(curLib.buttons);
|
||||
}
|
||||
|
||||
function makeButtons(cat, shapes) {
|
||||
var size = curLib.size || 300;
|
||||
var fill = curLib.fill || false;
|
||||
var off = size * 0.05;
|
||||
var vb = [-off, -off, size + off * 2, size + off * 2].join(' ');
|
||||
var stroke = fill ? 0 : size / 30;
|
||||
var shapeIcon = new DOMParser().parseFromString('<svg xmlns="http://www.w3.org/2000/svg"><svg viewBox="' + vb + '"><path fill="' + (fill ? '#333' : 'none') + '" stroke="#000" stroke-width="' + stroke + '" /></svg></svg>', 'text/xml');
|
||||
|
||||
var width = 24;
|
||||
var height = 24;
|
||||
shapeIcon.documentElement.setAttribute('width', width);
|
||||
shapeIcon.documentElement.setAttribute('height', height);
|
||||
var svgElem = $(document.importNode(shapeIcon.documentElement, true));
|
||||
|
||||
var data = shapes.data;
|
||||
|
||||
|
||||
curLib.buttons = [];
|
||||
for (var id in data) {
|
||||
var pathD = data[id];
|
||||
var icon = svgElem.clone();
|
||||
icon.find('path').attr('d', pathD);
|
||||
|
||||
var iconBtn = icon.wrap('<div class="tool_button">').parent().attr({
|
||||
id: modeId + '_' + id,
|
||||
title: id
|
||||
});
|
||||
// Store for later use
|
||||
curLib.buttons.push(iconBtn[0]);
|
||||
}
|
||||
}
|
||||
|
||||
function loadLibrary(catId) {
|
||||
var lib = library[catId];
|
||||
|
||||
if (!lib) {
|
||||
$('#shape_buttons').html('Loading...');
|
||||
$.getJSON(svgEditor.curConfig.extPath + 'shapelib/' + catId + '.json', function (result) {
|
||||
curLib = library[catId] = {
|
||||
data: result.data,
|
||||
size: result.size,
|
||||
fill: result.fill
|
||||
};
|
||||
makeButtons(catId, result);
|
||||
loadIcons();
|
||||
});
|
||||
return;
|
||||
}
|
||||
curLib = lib;
|
||||
if (!lib.buttons.length) {
|
||||
makeButtons(catId, lib);
|
||||
}
|
||||
loadIcons();
|
||||
}
|
||||
|
||||
return {
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'ext-shapes.xml',
|
||||
buttons: [{
|
||||
id: 'tool_shapelib',
|
||||
type: 'mode_flyout', // _flyout
|
||||
position: 6,
|
||||
title: 'Shape library',
|
||||
events: {
|
||||
click: function click() {
|
||||
canv.setMode(modeId);
|
||||
}
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('<style>').text('#shape_buttons {' + 'overflow: auto;' + 'width: 180px;' + 'max-height: 300px;' + 'display: table-cell;' + 'vertical-align: middle;' + '}' + '#shape_cats {' + 'min-width: 110px;' + 'display: table-cell;' + 'vertical-align: middle;' + 'height: 300px;' + '}' + '#shape_cats > div {' + 'line-height: 1em;' + 'padding: .5em;' + 'border:1px solid #B0B0B0;' + 'background: #E8E8E8;' + 'margin-bottom: -1px;' + '}' + '#shape_cats div:hover {' + 'background: #FFFFCC;' + '}' + '#shape_cats div.current {' + 'font-weight: bold;' + '}').appendTo('head');
|
||||
|
||||
var btnDiv = $('<div id="shape_buttons">');
|
||||
$('#tools_shapelib > *').wrapAll(btnDiv);
|
||||
|
||||
var shower = $('#tools_shapelib_show');
|
||||
|
||||
loadLibrary('basic');
|
||||
|
||||
// Do mouseup on parent element rather than each button
|
||||
$('#shape_buttons').mouseup(function (evt) {
|
||||
var btn = $(evt.target).closest('div.tool_button');
|
||||
|
||||
if (!btn.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var copy = btn.children().clone();
|
||||
shower.children(':not(.flyout_arrow_horiz)').remove();
|
||||
shower.append(copy).attr('data-curopt', '#' + btn[0].id) // This sets the current mode
|
||||
.mouseup();
|
||||
canv.setMode(modeId);
|
||||
|
||||
curShapeId = btn[0].id.substr((modeId + '_').length);
|
||||
currentD = curLib.data[curShapeId];
|
||||
|
||||
$('.tools_flyout').fadeOut();
|
||||
});
|
||||
|
||||
var shapeCats = $('<div id="shape_cats">');
|
||||
|
||||
var catStr = '';
|
||||
$.each(categories, function (id, label) {
|
||||
catStr += '<div data-cat=' + id + '>' + label + '</div>';
|
||||
});
|
||||
|
||||
shapeCats.html(catStr).children().bind('mouseup', function () {
|
||||
var catlink = $(this);
|
||||
catlink.siblings().removeClass('current');
|
||||
catlink.addClass('current');
|
||||
|
||||
loadLibrary(catlink.attr('data-cat'));
|
||||
// Get stuff
|
||||
return false;
|
||||
});
|
||||
|
||||
shapeCats.children().eq(0).addClass('current');
|
||||
|
||||
$('#tools_shapelib').append(shapeCats);
|
||||
|
||||
shower.mouseup(function () {
|
||||
canv.setMode(currentD ? modeId : 'select');
|
||||
});
|
||||
$('#tool_shapelib').remove();
|
||||
|
||||
var h = $('#tools_shapelib').height();
|
||||
$('#tools_shapelib').css({
|
||||
'margin-top': -(h / 2 - 15),
|
||||
'margin-left': 3
|
||||
});
|
||||
},
|
||||
mouseDown: function mouseDown(opts) {
|
||||
var mode = canv.getMode();
|
||||
if (mode !== modeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
startX = opts.start_x;
|
||||
var x = startX;
|
||||
startY = opts.start_y;
|
||||
var y = startY;
|
||||
var curStyle = canv.getStyle();
|
||||
|
||||
startClientPos.x = opts.event.clientX;
|
||||
startClientPos.y = opts.event.clientY;
|
||||
|
||||
curShape = canv.addSvgElementFromJson({
|
||||
element: 'path',
|
||||
curStyles: true,
|
||||
attr: {
|
||||
d: currentD,
|
||||
id: canv.getNextId(),
|
||||
opacity: curStyle.opacity / 2,
|
||||
style: 'pointer-events:none'
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure shape uses absolute values
|
||||
if (/[a-z]/.test(currentD)) {
|
||||
currentD = curLib.data[curShapeId] = canv.pathActions.convertPath(curShape);
|
||||
curShape.setAttribute('d', currentD);
|
||||
canv.pathActions.fixEnd(curShape);
|
||||
}
|
||||
curShape.setAttribute('transform', 'translate(' + x + ',' + y + ') scale(0.005) translate(' + -x + ',' + -y + ')');
|
||||
|
||||
canv.recalculateDimensions(curShape);
|
||||
|
||||
/* const tlist = */canv.getTransformList(curShape);
|
||||
|
||||
lastBBox = curShape.getBBox();
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
},
|
||||
mouseMove: function mouseMove(opts) {
|
||||
var mode = canv.getMode();
|
||||
if (mode !== modeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var zoom = canv.getZoom();
|
||||
var evt = opts.event;
|
||||
|
||||
var x = opts.mouse_x / zoom;
|
||||
var y = opts.mouse_y / zoom;
|
||||
|
||||
var tlist = canv.getTransformList(curShape),
|
||||
box = curShape.getBBox(),
|
||||
left = box.x,
|
||||
top = box.y;
|
||||
// {width, height} = box,
|
||||
// const dx = (x - startX), dy = (y - startY);
|
||||
|
||||
var newbox = {
|
||||
x: Math.min(startX, x),
|
||||
y: Math.min(startY, y),
|
||||
width: Math.abs(x - startX),
|
||||
height: Math.abs(y - startY)
|
||||
};
|
||||
|
||||
/*
|
||||
// This is currently serving no purpose, so commenting out
|
||||
let sy = height ? (height + dy) / height : 1,
|
||||
sx = width ? (width + dx) / width : 1;
|
||||
*/
|
||||
|
||||
var sx = newbox.width / lastBBox.width || 1;
|
||||
var sy = newbox.height / lastBBox.height || 1;
|
||||
|
||||
// Not perfect, but mostly works...
|
||||
var tx = 0;
|
||||
if (x < startX) {
|
||||
tx = lastBBox.width;
|
||||
}
|
||||
var ty = 0;
|
||||
if (y < startY) {
|
||||
ty = lastBBox.height;
|
||||
}
|
||||
|
||||
// update the transform list with translate,scale,translate
|
||||
var translateOrigin = svgroot.createSVGTransform(),
|
||||
scale = svgroot.createSVGTransform(),
|
||||
translateBack = svgroot.createSVGTransform();
|
||||
|
||||
translateOrigin.setTranslate(-(left + tx), -(top + ty));
|
||||
if (!evt.shiftKey) {
|
||||
var max = Math.min(Math.abs(sx), Math.abs(sy));
|
||||
|
||||
sx = max * (sx < 0 ? -1 : 1);
|
||||
sy = max * (sy < 0 ? -1 : 1);
|
||||
}
|
||||
scale.setScale(sx, sy);
|
||||
|
||||
translateBack.setTranslate(left + tx, top + ty);
|
||||
tlist.appendItem(translateBack);
|
||||
tlist.appendItem(scale);
|
||||
tlist.appendItem(translateOrigin);
|
||||
|
||||
canv.recalculateDimensions(curShape);
|
||||
|
||||
lastBBox = curShape.getBBox();
|
||||
},
|
||||
mouseUp: function mouseUp(opts) {
|
||||
var mode = canv.getMode();
|
||||
if (mode !== modeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var keepObject = opts.event.clientX !== startClientPos.x && opts.event.clientY !== startClientPos.y;
|
||||
|
||||
return {
|
||||
keep: keepObject,
|
||||
element: curShape,
|
||||
started: false
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
246
dist/extensions/ext-star.js
vendored
Normal file
246
dist/extensions/ext-star.js
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/* globals jQuery */
|
||||
/*
|
||||
* ext-star.js
|
||||
*
|
||||
*
|
||||
* Copyright(c) 2010 CloudCanvas, Inc.
|
||||
* All rights reserved
|
||||
*
|
||||
*/
|
||||
|
||||
svgEditor.addExtension('star', function (S) {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
|
||||
var // {svgcontent} = S,
|
||||
selElems = void 0,
|
||||
|
||||
// editingitex = false,
|
||||
// svgdoc = S.svgroot.parentNode.ownerDocument,
|
||||
started = void 0,
|
||||
newFO = void 0;
|
||||
// edg = 0,
|
||||
// newFOG, newFOGParent, newDef, newImageName, newMaskID,
|
||||
// undoCommand = 'Not image',
|
||||
// modeChangeG, ccZoom, wEl, hEl, wOffset, hOffset, ccRgbEl, brushW, brushH;
|
||||
|
||||
function showPanel(on) {
|
||||
var fcRules = $('#fc_rules');
|
||||
if (!fcRules.length) {
|
||||
fcRules = $('<style id="fc_rules"></style>').appendTo('head');
|
||||
}
|
||||
fcRules.text(!on ? '' : ' #tool_topath { display: none !important; }');
|
||||
$('#star_panel').toggle(on);
|
||||
}
|
||||
|
||||
/*
|
||||
function toggleSourceButtons(on){
|
||||
$('#star_save, #star_cancel').toggle(on);
|
||||
}
|
||||
*/
|
||||
|
||||
function setAttr(attr, val) {
|
||||
svgCanvas.changeSelectedAttribute(attr, val);
|
||||
S.call('changed', selElems);
|
||||
}
|
||||
|
||||
/*
|
||||
function cot(n){
|
||||
return 1 / Math.tan(n);
|
||||
}
|
||||
function sec(n){
|
||||
return 1 / Math.cos(n);
|
||||
}
|
||||
*/
|
||||
|
||||
return {
|
||||
name: 'star',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'star-icons.svg',
|
||||
buttons: [{
|
||||
id: 'tool_star',
|
||||
type: 'mode',
|
||||
title: 'Star Tool',
|
||||
position: 12,
|
||||
events: {
|
||||
click: function click() {
|
||||
showPanel(true);
|
||||
svgCanvas.setMode('star');
|
||||
}
|
||||
}
|
||||
}],
|
||||
|
||||
context_tools: [{
|
||||
type: 'input',
|
||||
panel: 'star_panel',
|
||||
title: 'Number of Sides',
|
||||
id: 'starNumPoints',
|
||||
label: 'points',
|
||||
size: 3,
|
||||
defval: 5,
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('point', this.value);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'star_panel',
|
||||
title: 'Pointiness',
|
||||
id: 'starRadiusMulitplier',
|
||||
label: 'Pointiness',
|
||||
size: 3,
|
||||
defval: 2.5
|
||||
}, {
|
||||
type: 'input',
|
||||
panel: 'star_panel',
|
||||
title: 'Twists the star',
|
||||
id: 'radialShift',
|
||||
label: 'Radial Shift',
|
||||
size: 3,
|
||||
defval: 0,
|
||||
events: {
|
||||
change: function change() {
|
||||
setAttr('radialshift', this.value);
|
||||
}
|
||||
}
|
||||
}],
|
||||
callback: function callback() {
|
||||
$('#star_panel').hide();
|
||||
// const endChanges = function(){};
|
||||
},
|
||||
mouseDown: function mouseDown(opts) {
|
||||
var rgb = svgCanvas.getColor('fill');
|
||||
// const ccRgbEl = rgb.substring(1, rgb.length);
|
||||
var sRgb = svgCanvas.getColor('stroke');
|
||||
// const ccSRgbEl = sRgb.substring(1, rgb.length);
|
||||
var sWidth = svgCanvas.getStrokeWidth();
|
||||
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
started = true;
|
||||
|
||||
newFO = S.addSvgElementFromJson({
|
||||
element: 'polygon',
|
||||
attr: {
|
||||
cx: opts.start_x,
|
||||
cy: opts.start_y,
|
||||
id: S.getNextId(),
|
||||
shape: 'star',
|
||||
point: document.getElementById('starNumPoints').value,
|
||||
r: 0,
|
||||
radialshift: document.getElementById('radialShift').value,
|
||||
r2: 0,
|
||||
orient: 'point',
|
||||
fill: rgb,
|
||||
strokecolor: sRgb,
|
||||
strokeWidth: sWidth
|
||||
}
|
||||
});
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseMove: function mouseMove(opts) {
|
||||
if (!started) {
|
||||
return;
|
||||
}
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
var c = $(newFO).attr(['cx', 'cy', 'point', 'orient', 'fill', 'strokecolor', 'strokeWidth', 'radialshift']);
|
||||
|
||||
var x = opts.mouse_x;
|
||||
var y = opts.mouse_y;
|
||||
var cx = c.cx,
|
||||
cy = c.cy,
|
||||
fill = c.fill,
|
||||
strokecolor = c.strokecolor,
|
||||
strokeWidth = c.strokeWidth,
|
||||
radialShift = c.radialShift,
|
||||
point = c.point,
|
||||
orient = c.orient,
|
||||
circumradius = Math.sqrt((x - cx) * (x - cx) + (y - cy) * (y - cy)) / 1.5,
|
||||
inradius = circumradius / document.getElementById('starRadiusMulitplier').value;
|
||||
|
||||
newFO.setAttributeNS(null, 'r', circumradius);
|
||||
newFO.setAttributeNS(null, 'r2', inradius);
|
||||
|
||||
var polyPoints = '';
|
||||
for (var s = 0; point >= s; s++) {
|
||||
var angle = 2.0 * Math.PI * (s / point);
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
}
|
||||
|
||||
x = circumradius * Math.cos(angle) + cx;
|
||||
y = circumradius * Math.sin(angle) + cy;
|
||||
|
||||
polyPoints += x + ',' + y + ' ';
|
||||
|
||||
if (inradius != null) {
|
||||
angle = 2.0 * Math.PI * (s / point) + Math.PI / point;
|
||||
if (orient === 'point') {
|
||||
angle -= Math.PI / 2;
|
||||
} else if (orient === 'edge') {
|
||||
angle = angle + Math.PI / point - Math.PI / 2;
|
||||
}
|
||||
angle += radialShift;
|
||||
|
||||
x = inradius * Math.cos(angle) + cx;
|
||||
y = inradius * Math.sin(angle) + cy;
|
||||
|
||||
polyPoints += x + ',' + y + ' ';
|
||||
}
|
||||
}
|
||||
newFO.setAttributeNS(null, 'points', polyPoints);
|
||||
newFO.setAttributeNS(null, 'fill', fill);
|
||||
newFO.setAttributeNS(null, 'stroke', strokecolor);
|
||||
newFO.setAttributeNS(null, 'stroke-width', strokeWidth);
|
||||
/* const shape = */newFO.getAttributeNS(null, 'shape');
|
||||
|
||||
return {
|
||||
started: true
|
||||
};
|
||||
}
|
||||
},
|
||||
mouseUp: function mouseUp() {
|
||||
if (svgCanvas.getMode() === 'star') {
|
||||
var attrs = $(newFO).attr(['r']);
|
||||
// svgCanvas.addToSelection([newFO], true);
|
||||
return {
|
||||
keep: attrs.r !== '0',
|
||||
element: newFO
|
||||
};
|
||||
}
|
||||
},
|
||||
selectedChanged: function selectedChanged(opts) {
|
||||
// Use this to update the current selected elements
|
||||
selElems = opts.elems;
|
||||
|
||||
var i = selElems.length;
|
||||
while (i--) {
|
||||
var elem = selElems[i];
|
||||
if (elem && elem.getAttributeNS(null, 'shape') === 'star') {
|
||||
if (opts.selectedElement && !opts.multiselected) {
|
||||
// $('#starRadiusMulitplier').val(elem.getAttribute('r2'));
|
||||
$('#starNumPoints').val(elem.getAttribute('point'));
|
||||
$('#radialShift').val(elem.getAttribute('radialshift'));
|
||||
showPanel(true);
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
} else {
|
||||
showPanel(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
elementChanged: function elementChanged(opts) {
|
||||
// const elem = opts.elems[0];
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
274
dist/extensions/ext-storage.js
vendored
Normal file
274
dist/extensions/ext-storage.js
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var confirmSetStorage = {
|
||||
en: {
|
||||
message: 'By default and where supported, SVG-Edit can store your editor ' + 'preferences and SVG content locally on your machine so you do not ' + 'need to add these back each time you load SVG-Edit. If, for privacy ' + 'reasons, you do not wish to store this information on your machine, ' + 'you can change away from the default option below.',
|
||||
storagePrefsAndContent: 'Store preferences and SVG content locally',
|
||||
storagePrefsOnly: 'Only store preferences locally',
|
||||
storagePrefs: 'Store preferences locally',
|
||||
storageNoPrefsOrContent: 'Do not store my preferences or SVG content locally',
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: 'If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again.'
|
||||
},
|
||||
de: {
|
||||
message: 'Standardmäßig kann SVG-Edit Ihre Editor-Einstellungen ' + 'und die SVG-Inhalte lokal auf Ihrem Gerät abspeichern. So brauchen Sie ' + 'nicht jedes Mal die SVG neu laden. Falls Sie aus Datenschutzgründen ' + 'dies nicht wollen, ' + 'können Sie die Standardeinstellung im Folgenden ändern.',
|
||||
storagePrefsAndContent: 'Store preferences and SVG content locally',
|
||||
storagePrefsOnly: 'Only store preferences locally',
|
||||
storagePrefs: 'Store preferences locally',
|
||||
storageNoPrefsOrContent: 'Do not store my preferences or SVG content locally',
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: 'If you choose to opt out of storage while remembering this choice, the URL will change so as to avoid asking again.'
|
||||
},
|
||||
fr: {
|
||||
message: "Par défaut et si supporté, SVG-Edit peut stocker les préférences de l'éditeur " + "et le contenu SVG localement sur votre machine de sorte que vous n'ayez pas besoin de les " + 'rajouter chaque fois que vous chargez SVG-Edit. Si, pour des raisons de confidentialité, ' + 'vous ne souhaitez pas stocker ces données sur votre machine, vous pouvez changer ce ' + 'comportement ci-dessous.',
|
||||
storagePrefsAndContent: 'Store preferences and SVG content locally',
|
||||
storagePrefsOnly: 'Only store preferences locally',
|
||||
storagePrefs: 'Store preferences locally',
|
||||
storageNoPrefsOrContent: 'Do not store my preferences or SVG content locally',
|
||||
storageNoPrefs: 'Do not store my preferences locally',
|
||||
rememberLabel: 'Remember this choice?',
|
||||
rememberTooltip: "Si vous choisissez de désactiver le stockage en mémorisant le choix, l'URL va changer afin que la question ne vous soit plus reposée."
|
||||
}
|
||||
};
|
||||
|
||||
/* globals jQuery */
|
||||
|
||||
svgEditor.addExtension('storage', function () {
|
||||
var $ = jQuery;
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
|
||||
// We could empty any already-set data for users when they decline storage,
|
||||
// but it would be a risk for users who wanted to store but accidentally
|
||||
// said "no"; instead, we'll let those who already set it, delete it themselves;
|
||||
// to change, set the "emptyStorageOnDecline" config setting to true
|
||||
// in svgedit-config-iife.js/svgedit-config-es.js.
|
||||
var _svgEditor$curConfig = svgEditor.curConfig,
|
||||
emptyStorageOnDecline = _svgEditor$curConfig.emptyStorageOnDecline,
|
||||
noStorageOnLoad = _svgEditor$curConfig.noStorageOnLoad,
|
||||
forceStorage = _svgEditor$curConfig.forceStorage;
|
||||
var _svgEditor = svgEditor,
|
||||
storage = _svgEditor.storage;
|
||||
|
||||
|
||||
function replaceStoragePrompt(val) {
|
||||
val = val ? 'storagePrompt=' + val : '';
|
||||
var loc = top.location; // Allow this to work with the embedded editor as well
|
||||
if (loc.href.includes('storagePrompt=')) {
|
||||
loc.href = loc.href.replace(/([&?])storagePrompt=[^&]*(&?)/, function (n0, n1, amp) {
|
||||
return (val ? n1 : '') + val + (!val && amp ? n1 : amp || '');
|
||||
});
|
||||
} else {
|
||||
loc.href += (loc.href.includes('?') ? '&' : '?') + val;
|
||||
}
|
||||
}
|
||||
function setSVGContentStorage(val) {
|
||||
if (storage) {
|
||||
var name = 'svgedit-' + svgEditor.curConfig.canvasName;
|
||||
if (!val) {
|
||||
storage.removeItem(name);
|
||||
} else {
|
||||
storage.setItem(name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function expireCookie(cookie) {
|
||||
document.cookie = encodeURIComponent(cookie) + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
||||
}
|
||||
|
||||
function removeStoragePrefCookie() {
|
||||
expireCookie('store');
|
||||
}
|
||||
|
||||
function emptyStorage() {
|
||||
setSVGContentStorage('');
|
||||
for (var name in svgEditor.curPrefs) {
|
||||
if (svgEditor.curPrefs.hasOwnProperty(name)) {
|
||||
name = 'svg-edit-' + name;
|
||||
if (storage) {
|
||||
storage.removeItem(name);
|
||||
}
|
||||
expireCookie(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// emptyStorage();
|
||||
|
||||
/**
|
||||
* Listen for unloading: If and only if opted in by the user, set the content
|
||||
* document and preferences into storage:
|
||||
* 1. Prevent save warnings (since we're automatically saving unsaved
|
||||
* content into storage)
|
||||
* 2. Use localStorage to set SVG contents (potentially too large to allow in cookies)
|
||||
* 3. Use localStorage (where available) or cookies to set preferences.
|
||||
*/
|
||||
function setupBeforeUnloadListener() {
|
||||
window.addEventListener('beforeunload', function (e) {
|
||||
// Don't save anything unless the user opted in to storage
|
||||
if (!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)) {
|
||||
return;
|
||||
}
|
||||
if (document.cookie.match(/(?:^|;\s*)store=prefsAndContent/)) {
|
||||
setSVGContentStorage(svgCanvas.getSvgString());
|
||||
}
|
||||
|
||||
svgEditor.setConfig({ no_save_warning: true }); // No need for explicit saving at all once storage is on
|
||||
// svgEditor.showSaveWarning = false;
|
||||
|
||||
var _svgEditor2 = svgEditor,
|
||||
curPrefs = _svgEditor2.curPrefs;
|
||||
|
||||
|
||||
for (var key in curPrefs) {
|
||||
if (curPrefs.hasOwnProperty(key)) {
|
||||
// It's our own config, so we don't need to iterate up the prototype chain
|
||||
var val = curPrefs[key];
|
||||
var store = val !== undefined;
|
||||
key = 'svg-edit-' + key;
|
||||
if (!store) {
|
||||
continue;
|
||||
}
|
||||
if (storage) {
|
||||
storage.setItem(key, val);
|
||||
} else if (window.widget) {
|
||||
window.widget.setPreferenceForKey(val, key);
|
||||
} else {
|
||||
val = encodeURIComponent(val);
|
||||
document.cookie = encodeURIComponent(key) + '=' + val + '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
var loaded = false;
|
||||
return {
|
||||
name: 'storage',
|
||||
langReady: function langReady(data) {
|
||||
// const {uiStrings: {confirmSetStorage}} = data, // No need to store as dialog should only run once
|
||||
var lang = data.lang;
|
||||
|
||||
var _$$deparam$querystrin = $.deparam.querystring(true),
|
||||
storagePrompt = _$$deparam$querystrin.storagePrompt;
|
||||
|
||||
var _confirmSetStorage$la = confirmSetStorage[lang],
|
||||
message = _confirmSetStorage$la.message,
|
||||
storagePrefsAndContent = _confirmSetStorage$la.storagePrefsAndContent,
|
||||
storagePrefsOnly = _confirmSetStorage$la.storagePrefsOnly,
|
||||
storagePrefs = _confirmSetStorage$la.storagePrefs,
|
||||
storageNoPrefsOrContent = _confirmSetStorage$la.storageNoPrefsOrContent,
|
||||
storageNoPrefs = _confirmSetStorage$la.storageNoPrefs,
|
||||
rememberLabel = _confirmSetStorage$la.rememberLabel,
|
||||
rememberTooltip = _confirmSetStorage$la.rememberTooltip;
|
||||
|
||||
// No need to run this one-time dialog again just because the user
|
||||
// changes the language
|
||||
|
||||
if (loaded) {
|
||||
return;
|
||||
}
|
||||
loaded = true;
|
||||
|
||||
// Note that the following can load even if "noStorageOnLoad" is
|
||||
// set to false; to avoid any chance of storage, avoid this
|
||||
// extension! (and to avoid using any prior storage, set the
|
||||
// config option "noStorageOnLoad" to true).
|
||||
if (!forceStorage && (
|
||||
// If the URL has been explicitly set to always prompt the
|
||||
// user (e.g., so one can be pointed to a URL where one
|
||||
// can alter one's settings, say to prevent future storage)...
|
||||
storagePrompt === true ||
|
||||
// ...or...if the URL at least doesn't explicitly prevent a
|
||||
// storage prompt (as we use for users who
|
||||
// don't want to set cookies at all but who don't want
|
||||
// continual prompts about it)...
|
||||
storagePrompt !== false &&
|
||||
// ...and this user hasn't previously indicated a desire for storage
|
||||
!document.cookie.match(/(?:^|;\s*)store=(?:prefsAndContent|prefsOnly)/)
|
||||
// ...then show the storage prompt.
|
||||
)) {
|
||||
var options = [];
|
||||
if (storage) {
|
||||
options.unshift({ value: 'prefsAndContent', text: storagePrefsAndContent }, { value: 'prefsOnly', text: storagePrefsOnly }, { value: 'noPrefsOrContent', text: storageNoPrefsOrContent });
|
||||
} else {
|
||||
options.unshift({ value: 'prefsOnly', text: storagePrefs }, { value: 'noPrefsOrContent', text: storageNoPrefs });
|
||||
}
|
||||
|
||||
// Hack to temporarily provide a wide and high enough dialog
|
||||
var oldContainerWidth = $('#dialog_container')[0].style.width,
|
||||
oldContainerMarginLeft = $('#dialog_container')[0].style.marginLeft,
|
||||
oldContentHeight = $('#dialog_content')[0].style.height,
|
||||
oldContainerHeight = $('#dialog_container')[0].style.height;
|
||||
$('#dialog_content')[0].style.height = '120px';
|
||||
$('#dialog_container')[0].style.height = '170px';
|
||||
$('#dialog_container')[0].style.width = '800px';
|
||||
$('#dialog_container')[0].style.marginLeft = '-400px';
|
||||
|
||||
// Open select-with-checkbox dialog
|
||||
// From svg-editor.js
|
||||
$.select(message, options, function (pref, checked) {
|
||||
if (pref && pref !== 'noPrefsOrContent') {
|
||||
// Regardless of whether the user opted
|
||||
// to remember the choice (and move to a URL which won't
|
||||
// ask them again), we have to assume the user
|
||||
// doesn't even want to remember their not wanting
|
||||
// storage, so we don't set the cookie or continue on with
|
||||
// setting storage on beforeunload
|
||||
document.cookie = 'store=' + encodeURIComponent(pref) + '; expires=Fri, 31 Dec 9999 23:59:59 GMT'; // 'prefsAndContent' | 'prefsOnly'
|
||||
// If the URL was configured to always insist on a prompt, if
|
||||
// the user does indicate a wish to store their info, we
|
||||
// don't want ask them again upon page refresh so move
|
||||
// them instead to a URL which does not always prompt
|
||||
if (storagePrompt === true && checked) {
|
||||
replaceStoragePrompt();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// The user does not wish storage (or cancelled, which we treat equivalently)
|
||||
removeStoragePrefCookie();
|
||||
if (pref && // If the user explicitly expresses wish for no storage
|
||||
emptyStorageOnDecline) {
|
||||
emptyStorage();
|
||||
}
|
||||
if (pref && checked) {
|
||||
// Open a URL which won't set storage and won't prompt user about storage
|
||||
replaceStoragePrompt('false');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset width/height of dialog (e.g., for use by Export)
|
||||
$('#dialog_container')[0].style.width = oldContainerWidth;
|
||||
$('#dialog_container')[0].style.marginLeft = oldContainerMarginLeft;
|
||||
$('#dialog_content')[0].style.height = oldContentHeight;
|
||||
$('#dialog_container')[0].style.height = oldContainerHeight;
|
||||
|
||||
// It should be enough to (conditionally) add to storage on
|
||||
// beforeunload, but if we wished to update immediately,
|
||||
// we might wish to try setting:
|
||||
// svgEditor.setConfig({noStorageOnLoad: true});
|
||||
// and then call:
|
||||
// svgEditor.loadContentAndPrefs();
|
||||
|
||||
// We don't check for noStorageOnLoad here because
|
||||
// the prompt gives the user the option to store data
|
||||
setupBeforeUnloadListener();
|
||||
|
||||
svgEditor.storagePromptClosed = true;
|
||||
}, null, null, {
|
||||
label: rememberLabel,
|
||||
checked: false,
|
||||
tooltip: rememberTooltip
|
||||
});
|
||||
} else if (!noStorageOnLoad || forceStorage) {
|
||||
setupBeforeUnloadListener();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}());
|
||||
70
dist/extensions/ext-webappfind.js
vendored
Normal file
70
dist/extensions/ext-webappfind.js
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
Depends on Firefox add-on and executables from https://github.com/brettz9/webappfind
|
||||
|
||||
Todos:
|
||||
1. See WebAppFind Readme for SVG-related todos
|
||||
*/
|
||||
(function () {
|
||||
var saveMessage = 'webapp-save',
|
||||
readMessage = 'webapp-read',
|
||||
excludedMessages = [readMessage, saveMessage];
|
||||
var pathID = void 0;
|
||||
|
||||
// Todo: Update to new API once released
|
||||
window.addEventListener('message', function (e) {
|
||||
if (e.origin !== window.location.origin || // PRIVACY AND SECURITY! (for viewing and saving, respectively)
|
||||
!Array.isArray(e.data) || excludedMessages.includes(e.data[0]) // Validate format and avoid our post below
|
||||
) {
|
||||
return;
|
||||
}
|
||||
var messageType = e.data[0];
|
||||
var svgString = void 0;
|
||||
switch (messageType) {
|
||||
case 'webapp-view':
|
||||
// Populate the contents
|
||||
pathID = e.data[1];
|
||||
|
||||
svgString = e.data[2];
|
||||
svgEditor.loadFromString(svgString);
|
||||
|
||||
/* if ($('#tool_save_file')) {
|
||||
$('#tool_save_file').disabled = false;
|
||||
} */
|
||||
break;
|
||||
case 'webapp-save-end':
|
||||
alert('save complete for pathID ' + e.data[1] + '!');
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unexpected mode');
|
||||
}
|
||||
}, false);
|
||||
|
||||
window.postMessage([readMessage], window.location.origin !== 'null' ? window.location.origin : '*'); // Avoid "null" string error for file: protocol (even though file protocol not currently supported by add-on)
|
||||
|
||||
svgEditor.addExtension('WebAppFind', function () {
|
||||
return {
|
||||
name: 'WebAppFind',
|
||||
svgicons: svgEditor.curConfig.extIconsPath + 'webappfind-icon.svg',
|
||||
buttons: [{
|
||||
id: 'webappfind_save', //
|
||||
type: 'app_menu',
|
||||
title: 'Save Image back to Disk',
|
||||
position: 4, // Before 0-based index position 4 (after the regular "Save Image (S)")
|
||||
events: {
|
||||
click: function click() {
|
||||
if (!pathID) {
|
||||
// Not ready yet as haven't received first payload
|
||||
return;
|
||||
}
|
||||
window.postMessage([saveMessage, pathID, svgEditor.canvas.getSvgString()], window.location.origin);
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
}());
|
||||
54
dist/extensions/ext-xdomain-messaging.js
vendored
Normal file
54
dist/extensions/ext-xdomain-messaging.js
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||
return typeof obj;
|
||||
} : function (obj) {
|
||||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||
};
|
||||
|
||||
/**
|
||||
* Should not be needed for same domain control (just call via child frame),
|
||||
* but an API common for cross-domain and same domain use can be found
|
||||
* in embedapi.js with a demo at embedapi.html
|
||||
*/
|
||||
svgEditor.addExtension('xdomain-messaging', function () {
|
||||
var svgCanvas = svgEditor.canvas;
|
||||
try {
|
||||
window.addEventListener('message', function (e) {
|
||||
// We accept and post strings for the sake of IE9 support
|
||||
if (typeof e.data !== 'string' || e.data.charAt() === '|') {
|
||||
return;
|
||||
}
|
||||
var data = JSON.parse(e.data);
|
||||
if (!data || (typeof data === 'undefined' ? 'undefined' : _typeof(data)) !== 'object' || data.namespace !== 'svgCanvas') {
|
||||
return;
|
||||
}
|
||||
// The default is not to allow any origins, including even the same domain or if run on a file:// URL
|
||||
// See config-sample.js for an example of how to configure
|
||||
var allowedOrigins = svgEditor.curConfig.allowedOrigins;
|
||||
|
||||
if (!allowedOrigins.includes('*') && !allowedOrigins.includes(e.origin)) {
|
||||
return;
|
||||
}
|
||||
var cbid = data.id;
|
||||
var name = data.name,
|
||||
args = data.args;
|
||||
|
||||
var message = {
|
||||
namespace: 'svg-edit',
|
||||
id: cbid
|
||||
};
|
||||
try {
|
||||
message.result = svgCanvas[name].apply(svgCanvas, args);
|
||||
} catch (err) {
|
||||
message.error = err.message;
|
||||
}
|
||||
e.source.postMessage(JSON.stringify(message), '*');
|
||||
}, false);
|
||||
} catch (err) {
|
||||
console.log('Error with xdomain message listener: ' + err);
|
||||
}
|
||||
});
|
||||
|
||||
}());
|
||||
67
dist/extensions/imagelib/index.js
vendored
Normal file
67
dist/extensions/imagelib/index.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var _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;
|
||||
};
|
||||
|
||||
/* globals jQuery */
|
||||
var $ = jQuery;
|
||||
$('a').click(function () {
|
||||
var href = this.href;
|
||||
|
||||
var target = window.parent;
|
||||
var post = function post(message) {
|
||||
// Todo: Make origin customizable as set by opening window
|
||||
// Todo: If dropping IE9, avoid stringifying
|
||||
target.postMessage(JSON.stringify(_extends({
|
||||
namespace: 'imagelib'
|
||||
}, message)), '*');
|
||||
};
|
||||
// Convert Non-SVG images to data URL first
|
||||
// (this could also have been done server-side by the library)
|
||||
// Send metadata (also indicates file is about to be sent)
|
||||
post({
|
||||
name: $(this).text(),
|
||||
id: href
|
||||
});
|
||||
if (!this.href.includes('.svg')) {
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = this.width;
|
||||
canvas.height = this.height;
|
||||
// load the raster image into the canvas
|
||||
canvas.getContext('2d').drawImage(this, 0, 0);
|
||||
// retrieve the data: URL
|
||||
var data = void 0;
|
||||
try {
|
||||
data = canvas.toDataURL();
|
||||
} catch (err) {
|
||||
// This fails in Firefox with file:// URLs :(
|
||||
alert('Data URL conversion failed: ' + err);
|
||||
data = '';
|
||||
}
|
||||
post({ href: href, data: data });
|
||||
};
|
||||
img.src = href;
|
||||
} else {
|
||||
// Do ajax request for image's href value
|
||||
$.get(href, function (data) {
|
||||
post({ href: href, data: data });
|
||||
}, 'html'); // 'html' is necessary to keep returned data as a string
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
Reference in New Issue
Block a user