master
Mark MacKay 2021-01-16 20:49:34 -06:00
parent d2fc40b9e4
commit 0e35cdaa04
23 changed files with 1073 additions and 806 deletions

View File

@ -246,7 +246,7 @@ html, body {
z-index: 2;
}
#tools_top {
#panels {
position: absolute;
width: 160px;
height: 100%;
@ -258,7 +258,7 @@ html, body {
padding: 0 0 0 15px;
}
.touch #tools_top {
.touch #panels {
top: 30px;
}
@ -299,15 +299,15 @@ div#font-selector .font-item:hover {
background-color: #eee;
}
#tools_top #marker_panel * {
#panels #marker_panel * {
float: left;
}
#tools_top #marker_panel h4 {
#panels #marker_panel h4 {
float: none;
}
#tools_top #marker_panel .dropdown .icon_label {
#panels #marker_panel .dropdown .icon_label {
width: 36px;
height: 20px;
margin-top: 2px;
@ -315,17 +315,17 @@ div#font-selector .font-item:hover {
text-align: center;
}
#tools_top #marker_panel .dropdown button {
#panels #marker_panel .dropdown button {
margin-top: 2px;
}
#tools_top #marker_panel #marker_panel_title {
#panels #marker_panel #marker_panel_title {
float: none;
color: #fff;
margin-bottom: 3px;
}
#tools_top #marker_panel .dropdown .icon_label img {
#panels #marker_panel .dropdown .icon_label img {
float: none;
}
@ -536,27 +536,27 @@ box-shadow: inset 0 3px 10px rgba(255, 255, 255, 0.1),
}
#tools_top h4 {
#panels h4 {
color: #fff;
font-weight: normal;
margin: 0;
padding: 10px 0 5px 0;
}
#tools_top .dropdown .icon_label {
#panels .dropdown .icon_label {
border: 1px solid transparent;
height: auto;
}
#tools_top.multiselected #align_tools {
#panels.multiselected #align_tools {
display: none;
}
#tools_top.multiselected #multiselected_panel {
#panels.multiselected #multiselected_panel {
display: block !important;
}
#tools_top.multiselected #multiselected_panel .hidable {
#panels.multiselected #multiselected_panel .hidable {
display: none;
}

View File

@ -118,21 +118,25 @@
</div>
</div>
<div id="tools_top" class="tools_panel">
<div id="panels" class="tools_panel">
<div id="canvas_panel" class="context_panel">
<h4 class="clearfix">Canvas</h4>
<label data-title="Change canvas width">
<input size="3" id="canvas_width" type="text" pattern="[0-9]*" />
<span class="icon_label">Width</span>
</label>
<label data-title="Change canvas height">
<input id="canvas_height" size="3" type="text" pattern="[0-9]*" />
<span class="icon_label">Height</span>
</label>
<div class="draginput">
<label data-title="Change canvas width">
<input size="3" id="canvas_width" type="text" pattern="[0-9]*" value="800" />
<span class="icon_label">Width</span>
</label>
</div>
<div class="draginput">
<label data-title="Change canvas height">
<input id="canvas_height" size="3" type="text" pattern="[0-9]*" value="600" />
<span class="icon_label">Height</span>
</label>
</div>
<label data-title="Change canvas color" class="draginput">
<span>Color</span>
@ -514,31 +518,31 @@
<div id="tools_left" class="tools_panel">
<div class="tool_button" id="tool_select" title="Select Tool [V]">
<div class="tool_button" id="tool_select" data-mode="select" title="Select Tool [V]">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M17.15 20.76l-2.94 1.5-3.68-6-4.41 3V1.24l12.5 12.01-4.41 1.5 2.94 6z"/>
</svg>
</div>
<div class="tool_button" id="tool_fhpath" title="Pencil Tool [P]">
<div class="tool_button" id="tool_fhpath" data-mode="fhpath" title="Pencil Tool [P]">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" style="transform: scale(-1,1)"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" /></svg>
</div>
<div class="tool_button" id="tool_line" title="Line Tool [L]">
<div class="tool_button" id="tool_line" data-mode="line" title="Line Tool [L]">
<svg viewBox="0 0 27 27" xmlns="http://www.w3.org/2000/svg" width="24" height="24" >
<path d="M 3 1 L 26 24 L 24 26 L 1 3 L 3 1 Z"></path>
</svg>
</div>
<div class="tool_button" id="tool_rect" title="Square/Rect Tool [R]">
<div class="tool_button" id="tool_rect" data-mode="rect" title="Square/Rect Tool [R]">
<svg viewBox="0 0 27 27" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path d="M 0 8 L 0 24 L 24 24 L 25 8 L 0 8 Z"/>
</svg>
</div>
<div class="tool_button" id="tool_ellipse" title="Ellipse/Circle Tool [C]">
<div class="tool_button" id="tool_ellipse" data-mode="ellipse" title="Ellipse/Circle Tool [C]">
<svg viewBox="0 0 27 27" xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<ellipse cx="13" cy="13" rx="13" ry="9"></ellipse>
</svg>
</div>
<div class="tool_button" title="Shape Tool [C]" id="tool_shapelib">
<div class="tool_button" id="tool_shapelib" data-mode="shapelib" title="Shape Tool [C]" >
<svg xmlns="http://www.w3.org/2000/svg" height="27" width="27" viewBox="0 0 24 24" >
<polygon points="14.43,10 12,2 9.57,10 2,10 8.18,14.41 5.83,22 12,17.31 18.18,22 15.83,14.41 22,10"/>
</svg>
@ -547,20 +551,20 @@
</div>
</div>
<div class="tool_button" id="tool_path" title="Path Tool [P]">
<div class="tool_button" id="tool_path" data-mode="path" title="Path Tool [P]">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27" width="27" height="27">
<path d="M12.2 1.9c0-.36.86 0 .86 0V14a1.3 1.3 0 10.88 0V1.9s.87-.36.87 0c0 6.81 5.22 11.68 5.22 11.68l-3.25 8.2h-6.55l-3.26-8.2s5.22-4.87 5.22-11.68zM7.83 25.26v-2.61h11.32v2.6H7.84z"/>
</svg>
</div>
<div class="tool_button" id="tool_text" title="Text Tool [T]">
<div class="tool_button" id="tool_text" data-mode="text" title="Text Tool [T]">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="2 2 20 20" width="27" height="27"><path d="M5 4v3h5.5v12h3V7H19V4z"/></svg>
</div>
<div class="tool_button" id="tool_zoom" title="Zoom Tool [Z]">
<div class="tool_button" id="tool_zoom" data-mode="zoom" title="Zoom Tool [Z]">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="2 2 20 20" width="27"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
</div>
<div class="tool_button" id="tool_eyedropper" title="Eyedropper Tool [Z]">
<div class="tool_button" id="tool_eyedropper" data-mode="eyedropper" title="Eyedropper Tool [Z]">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="2 2 20 20" width="27" style="transform: scale(-1, 1)"><path d="M20.71 5.63l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-3.12 3.12-1.93-1.91-1.41 1.41 1.42 1.42L3 16.25V21h4.75l8.92-8.92 1.42 1.42 1.41-1.41-1.92-1.92 3.12-3.12c.4-.4.4-1.03.01-1.42zM6.92 19L5 17.08l8.06-8.06 1.92 1.92L6.92 19z"/></svg>
</div>
@ -663,30 +667,43 @@
<div id="shape_buttons"></div>
</div>
<!-- build:js all.js -->
<script type="text/javascript" src="js/lib/jquery-3.5.1.min.js"></script>
<script type="text/javascript" src="js/lib/pathseg.js"></script>
<script type="text/javascript" src="js/lib/touch.js"></script>
<script type="text/javascript" src="js/lib/jquery.hotkeys.min.js"></script>
<script type="text/javascript" src="js/lib/jquery.jgraduate.js"></script>
<script type="text/javascript" src="js/lib/jquery.contextMenu.js"></script>
<script type="text/javascript" src="js/lib/jquery-ui-1.8.17.custom.min.js"></script>
<script type="text/javascript" src="js/fonts.js"></script>
<script type="text/javascript" src="js/browser.js"></script>
<script type="text/javascript" src="js/svgtransformlist.js"></script>
<script type="text/javascript" src="js/math.js"></script>
<script type="text/javascript" src="js/units.js"></script>
<script type="text/javascript" src="js/svgutils.js"></script>
<script type="text/javascript" src="js/jquery.attr.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/dao.js"></script>
<script type="text/javascript" src="js/state.js"></script>
<script type="text/javascript" src="js/sanitize.js"></script>
<script type="text/javascript" src="js/browser.js"></script>
<script type="text/javascript" src="js/svgutils.js"></script>
<script type="text/javascript" src="js/history.js"></script>
<script type="text/javascript" src="js/select.js"></script>
<script type="text/javascript" src="js/draw.js"></script>
<script type="text/javascript" src="js/path.js"></script>
<script type="text/javascript" src="js/dialog.js"></script>
<script type="text/javascript" src="js/sanitize.js"></script>
<script type="text/javascript" src="js/units.js"></script>
<script type="text/javascript" src="js/math.js"></script>
<script type="text/javascript" src="js/translate.js"></script>
<script type="text/javascript" src="js/svgtransformlist.js"></script>
<script type="text/javascript" src="js/draw.js"></script>
<script type="text/javascript" src="js/svgcanvas.js"></script>
<script type="text/javascript" src="js/rulers.js"></script>
<script type="text/javascript" src="js/method-draw.js"></script>
<script type="text/javascript" src="js/editor.js"></script>
<script type="text/javascript" src="js/Rulers.js"></script>
<script type="text/javascript" src="js/Toolbar.js"></script>
<script type="text/javascript" src="js/Menu.js"></script>
<script type="text/javascript" src="js/start.js"></script>
<script type="text/javascript" src="js/fonts.js"></script>
<script type="text/javascript" src="js/dialog.js"></script>
<script type="text/javascript" src="js/contextChanged.js"></script>
<script type="text/javascript" src="js/zoomChanged.js"></script>
<script type="text/javascript" src="js/exportHandler.js"></script>
<script type="text/javascript" src="js/elementChanged.js"></script>
<script type="text/javascript" src="js/elementTransition.js"></script>
<script type="text/javascript" src="js/bindCanvas.js"></script>
<script type="text/javascript" src="js/palette.js"></script>
<script type="text/javascript" src="js/lib/jquery-draginput.js"></script>
<script type="text/javascript" src="js/lib/contextmenu.js"></script>

47
src/js/Menu.js Normal file
View File

@ -0,0 +1,47 @@
MD.Menu = function(){
// top dropdown menus
$('.menu_title')
.on('mousedown', function() {
$("#tools_shapelib").hide()
$("#menu_bar").toggleClass('active');
$('.menu').removeClass('open');
$(this).parent().addClass('open');
})
.on('mouseover', function() {
$('.menu').removeClass('open');
$(this).parent().addClass('open');
});
function blink(e) {
e.target.style.background = "#fff";
setTimeout(function(){e.target.style.background = "#ddd";}, 50);
setTimeout(function(){e.target.style.background = "#fff";}, 150);
setTimeout(function(){e.target.style.background = "#ddd";}, 200);
setTimeout(function(){e.target.style.background = "";}, 200);
setTimeout(function(){$('#menu_bar').removeClass('active')}, 250);
return false;
}
function close(e){
if (e.target.nodeName && e.target.nodeName.toLowerCase() === "input") return false;
if (!$(e.target).hasClass("menu_title") && !$(e.target).parent().hasClass("menu_title")) {
if(!$(e.target).hasClass("disabled") && $(e.target).hasClass("menu_item")) blinker(e)
else $('#menu_bar').removeClass('active')
}
}
function flash($menu){
var menu_title = $menu.prev();
menu_title.css({
"background": "white",
"color": "black"
});
setTimeout(function(){menu_title.removeAttr("style")}, 200);
}
$('.menu_item').on('click', blink);
$("body").on('mousedown', close);
this.flash = flash;
}

17
src/js/Toolbar.js Normal file
View File

@ -0,0 +1,17 @@
MD.Toolbar = function(){
// tools left
$("#tools_left .tool_button").on("click", function(){
const mode = this.getAttribute("data-mode");
state.set("canvasMode", mode)
setMode(mode);
});
function setMode(mode) {
$(".tool_button").removeClass("current");
$("#tool_" + mode).addClass("current");
svgCanvas.setMode(mode);
}
this.setMode = setMode;
}

8
src/js/bindCanvas.js Normal file
View File

@ -0,0 +1,8 @@
// bind the selected event to our function that handles updates to the UI
svgCanvas.bind("selected", editor.selectedChanged);
svgCanvas.bind("transition", elementTransition);
//svgCanvas.bind("changed", elementChanged);
svgCanvas.bind("exported", exportHandler);
svgCanvas.bind("zoomed", zoomChanged);
svgCanvas.bind("contextset", contextChanged);
svgCanvas.textActions.setInputElem($("#text")[0]);

25
src/js/contextChanged.js Normal file
View File

@ -0,0 +1,25 @@
var contextChanged = function(win, context) {
var link_str = '';
if(context) {
var str = '';
link_str = '<a href="#" data-root="y">' + svgCanvas.getCurrentDrawing().getCurrentLayerName() + '</a>';
$(context).parentsUntil('#svgcontent > g').addBack().each(function() {
if(this.id) {
str += ' > ' + this.id;
if(this !== context) {
link_str += ' > <a href="#">' + this.id + '</a>';
} else {
link_str += ' > ' + this.id;
}
}
});
cur_context = str;
} else {
cur_context = null;
}
$('#cur_context_panel').toggle(!!context).html(link_str);
}

147
src/js/dao.js Normal file
View File

@ -0,0 +1,147 @@
const dao = [
// public, appears in builder
{
name: "canvasId",
label: "Canvas ID",
type: "id",
default: "",
private: false,
save: true
},
{
name: "canvasTitle",
label: "Canvas Title",
type: "string",
default: "",
private: false,
save: true
},
{
name: "canvasSize",
label: "Canvas Size",
type: "array",
default: [800, 600],
private: false,
save: true
},
{
name: "canvasRulers",
label: "Canvas Rulers",
type: "boolean",
default: true,
private: false,
save: true
},
{
name: "canvasContent",
label: "Canvas Content",
type: "string",
default: "<svg xmlns='http://www.w3.org/2000/svg' width='800' height='600' viewBox='0 0 800 600'></svg>",
private: true,
save: true
},
{
name: "canvasMode",
label: "Canvas Mode",
type: "string",
default: "select",
private: true,
save: true
},
{
name: "canvasFill",
label: "Canvas Fill",
type: "object",
default: {color: 'fff', opacity: 1},
private: true,
save: true
},
{
name: "canvasStroke",
label: "Canvas Stroke",
type: "object",
default: {width: 1.5, color: '000', opacity: 1},
private: true,
save: true
},
{
name: "canvasCreationDate",
label: "Canvas Creation Date",
type: "string",
default: new Date().toString(),
private: true,
save: false
},
// When this page was created
{
name: "canvasLastModified",
label: "Canvas Last Modified",
type: "string",
default: new Date().toString(),
private: true,
save: false
},
// system level fields
{
name: "darkmode",
label: "Dark Mode",
type: "boolean",
default: true,
private: true,
save: true,
},
// english or spanish, set by the browser
{
name: "language",
label: "Language",
type: "string",
default: null,
private: true,
save: true,
},
// if it is the first time visitor we onboard them
{
name: "visited",
label: "Has visited before",
type: "boolean",
default: false,
private: true,
save: true,
},
// how many seconds have passed since the user moved the mouse
// mostly for hiding UI, but we could also use it for exercises
// where we want to force keyboard usage to edit, for example
{
name: "mouseIdle",
label: "Mouse Idle",
type: "number",
default: false,
private: true,
save: false,
},
];
dao.forEach(thing => {
thing.clean = function(value){
if (thing.type === "number") return isNaN(value) ? 0 : parseInt(value, 10);
if (thing.type === "string") return value || "";
if (thing.type === "boolean") return value === "true" || value === true ? true : false;
if (thing.type === "url") return value || "";
if (thing.type === "id") return value || 0;
if (thing.type === "array") return typeof value === "object" ? value : value ? value.split(",") : [];
if (thing.type === "object") return typeof value === "object" ? value : value ? JSON.parse(value) : {};
else throw "type " + thing.type + " does not exist";
}
});

264
src/js/editor.js Normal file
View File

@ -0,0 +1,264 @@
const MD = {};
MD.Editor = function(){
// called when we've selected a different element
function selectedChanged(window,elems) {
const mode = svgCanvas.getMode();
if(mode === "select") editor.toolbar.setMode("select");
if (mode === "pathedit") return updateContextPanel();
elems = elems.filter(Boolean);
$('.context_panel').hide();
$('#panels').toggleClass("multiselected", elems.length > 1);
$('#multiselected_panel').toggle(elems.length > 1);
$('#canvas_panel').toggle(!elems.length);
// multiselected, nothing else needs to be done
if (elems.length > 1) return;
const selectedElement = elems[0];
const tagName = selectedElement.tagName;
$("#" + tagName + "_panel").show();
$('#stroke_width').val(selectedElement.getAttribute("stroke-width") || 0);
var dash = selectedElement.getAttribute("stroke-dasharray") || "none"
$('option', '#stroke_style').removeAttr('selected');
$('#stroke_style option[value="'+ dash +'"]').attr("selected", "selected");
$('#stroke_style').trigger('change');
$.fn.dragInput.updateCursor($('#stroke_width')[0])
$.fn.dragInput.updateCursor($('#blur')[0])
updateContextPanel(elems);
};
function updateContextPanel(elems) {
var elem = elems[0];
// If element has just been deleted, consider it null
if(elem != null && !elem.parentNode) elem = null;
const multiselected = elems.length > 1;
var currentLayerName = svgCanvas.getCurrentDrawing().getCurrentLayerName();
var currentMode = svgCanvas.getMode();
var unit = 'px';
var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false;
if (is_node) {
$('.context_panel').hide();
$('#path_node_panel').show();
$('#stroke_panel').hide();
var point = path.getNodePoint();
$('#tool_add_subpath').removeClass('push_button_pressed').addClass('tool_button');
$('#tool_node_delete').toggleClass('disabled', !path.canDeleteNodes);
if(point) {
var seg_type = $('#seg_type');
if(unit) {
point.x = svgedit.units.convertUnit(point.x);
point.y = svgedit.units.convertUnit(point.y);
}
$('#path_node_x').val(Math.round(point.x));
$('#path_node_y').val(Math.round(point.y));
if(point.type) {
seg_type.val(point.type).removeAttr('disabled');
$("#seg_type_label").html(point.type == 4 ? "Straight" : "Curve")
} else {
seg_type.val(4).attr('disabled','disabled');
}
}
$("#panels").removeClass("multiselected")
$("#stroke_panel").hide();
$("#canvas_panel").hide();
return;
}
var menu_items = $('#cmenu_canvas li');
$('.context_panel').hide();
$('.menu_item', '#edit_menu').addClass('disabled');
$('.menu_item', '#object_menu').addClass('disabled');
//hack to show the proper multialign box
if (multiselected) {
multiselected = multiselected.filter(Boolean);
elem = (svgCanvas.elementsAreSame(multiselected)) ? multiselected[0] : null
if (elem) $("#panels").addClass("multiselected")
}
if (!elem && !multiselected) {
$("#panels").removeClass("multiselected")
$("#stroke_panel").hide();
$("#canvas_panel").show();
}
if (elem != null) {
$("#stroke_panel").show();
var elname = elem.nodeName;
var angle = svgCanvas.getRotationAngle(elem);
$('#angle').val(Math.round(angle));
var blurval = svgCanvas.getBlur(elem);
$('#blur').val(blurval);
if(!is_node && currentMode != 'pathedit') {
$('#selected_panel').show();
$('.action_selected').removeClass('disabled');
// Elements in this array already have coord fields
var x, y
if(['g', 'polyline', 'path'].indexOf(elname) >= 0) {
var bb = svgCanvas.getStrokedBBox([elem]);
if(bb) {
x = bb.x;
y = bb.y;
}
}
if(unit) {
x = svgedit.units.convertUnit(x);
y = svgedit.units.convertUnit(y);
}
$("#" + elname +"_x").val(Math.round(x))
$("#" + elname +"_y").val(Math.round(y))
if (elname === "polyline") {
//we're acting as if polylines were paths
$("#path_x").val(Math.round(x))
$("#path_y").val(Math.round(y))
}
// Elements in this array cannot be converted to a path
var no_path = ['image', 'text', 'path', 'g', 'use'].indexOf(elname) == -1;
if (no_path) $('.action_path_convert_selected').removeClass('disabled');
if (elname === "path") $('.action_path_selected').removeClass('disabled');
}
var link_href = null;
if (el_name === 'a') {
link_href = svgCanvas.getHref(elem);
$('#g_panel').show();
}
if(elem.parentNode.tagName === 'a') {
if(!$(elem).siblings().length) {
$('#a_panel').show();
link_href = svgCanvas.getHref(elem.parentNode);
}
}
// Hide/show the make_link buttons
$('#tool_make_link, #tool_make_link').toggle(!link_href);
if(link_href) {
$('#link_url').val(link_href);
}
// update contextual tools here
var panels = {
g: [],
a: [],
rect: ['rx','width','height', 'x', 'y'],
image: ['width','height', 'x', 'y'],
circle: ['cx','cy','r'],
ellipse: ['cx','cy','rx','ry'],
line: ['x1','y1','x2','y2'],
text: ['x', 'y'],
'use': [],
path : []
};
var el_name = elem.tagName;
if($(elem).data('gsvg')) {
$('#g_panel').show();
}
if (el_name == "path" || el_name == "polyline") {
$('#path_panel').show();
}
if(panels[el_name]) {
var cur_panel = panels[el_name];
$('#' + el_name + '_panel').show();
// corner radius has to live in a different panel
// because otherwise it changes the position of the
// of the elements
if(el_name == "rect") $("#cornerRadiusLabel").show()
else $("#cornerRadiusLabel").hide()
$.each(cur_panel, function(i, item) {
var attrVal = elem.getAttribute(item);
//update the draginput cursors
var name_item = document.getElementById(el_name + '_' + item);
name_item.value = Math.round(attrVal) || 0;
if (name_item.getAttribute("data-cursor") === "true") {
$.fn.dragInput.updateCursor(name_item );
}
});
if(el_name == 'text') {
var font_family = elem.getAttribute("font-family");
var cleanFontFamily = font_family.split(",")[0].replace(/'/g, "");
var select = document.getElementById("font_family_dropdown");
$('#text_panel').css("display", "inline");
$('#tool_italic').toggleClass('active', svgCanvas.getItalic())
$('#tool_bold').toggleClass('active', svgCanvas.getBold())
$('#tool_italic').toggleClass('disabled', fonts[cleanFontFamily] ? !fonts[cleanFontFamily]["Italic"] : false)
$('#tool_bold').toggleClass('disabled', fonts[cleanFontFamily] ? !fonts[cleanFontFamily]["Bold"] : false);
$('#font_family').val(font_family);
$(select).find(`option[value=${font_family}]`).prop("selected", true);
$('#font_size').val(elem.getAttribute("font-size"));
$('#text').val(elem.textContent);
$('#preview_font').text(cleanFontFamily).css('font-family', font_family);
} // text
else if(el_name == 'image') {
setImageURL(svgCanvas.getHref(elem));
} // image
else if(el_name === 'g' || el_name === 'use') {
$('#container_panel').show();
$('.action_group_selected').removeClass('disabled');
var title = svgCanvas.getTitle();
}
}
menu_items[(el_name === 'g' ? 'en':'dis') + 'ableContextMenuItems']('#ungroup');
menu_items[((el_name === 'g' || !multiselected) ? 'dis':'en') + 'ableContextMenuItems']('#group');
}
if (multiselected) {
$('#multiselected_panel').show();
$('.action_multi_selected').removeClass('disabled');
menu_items
.enableContextMenuItems('#group')
.disableContextMenuItems('#ungroup');
}
if (!elem) {
menu_items.disableContextMenuItems('#delete,#cut,#copy,#ungroup,#move_front,#move_up,#move_down,#move_back');
}
// update history buttons
if (svgCanvas.undoMgr.getUndoStackSize() > 0) {
$('#tool_undo').removeClass( 'disabled');
}
else {
$('#tool_undo').addClass( 'disabled');
}
if (svgCanvas.undoMgr.getRedoStackSize() > 0) {
$('#tool_redo').removeClass( 'disabled');
}
else {
$('#tool_redo').addClass( 'disabled');
}
svgCanvas.addedNew = false;
if ( (elem && !is_node) || multiselected) {
// update the selected elements' layer
$('#selLayerNames').removeAttr('disabled').val(currentLayerName);
// Enable regular menu options
$("#cmenu_canvas").enableContextMenuItems('#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back');
}
};
this.selectedChanged = selectedChanged;
this.updateContextPanel = updateContextPanel;
}

43
src/js/elementChanged.js Normal file
View File

@ -0,0 +1,43 @@
// called when any element has changed
var elementChanged = function(window,elems) {
var mode = svgCanvas.getMode();
if(mode === "select") {
setSelectMode();
}
for (var i = 0; i < elems.length; ++i) {
var elem = elems[i];
// if the element changed was the svg, then it could be a resolution change
if (elem && elem.tagName === "svg") {
//populateLayers();
updateCanvas();
}
// Update selectedElement if element is no longer part of the image.
// This occurs for the text elements in Firefox
else if(elem && selectedElement && selectedElement.parentNode == null) {
// || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why
selectedElement = elem;
}
}
// we update the contextual panel with potentially new
// positional/sizing information (we DON'T want to update the
// toolbar here as that creates an infinite loop)
// also this updates the history buttons
// we tell it to skip focusing the text control if the
// text element was previously in focus
updateContextPanel();
// In the event a gradient was flipped:
if(selectedElement && mode === "select") {
Editor.paintBox.fill.update();
Editor.paintBox.stroke.update();
}
svgCanvas.runExtensions("elementChanged", {
elems: elems
});
};

View File

@ -0,0 +1,24 @@
// Call when part of element is in process of changing, generally
// on mousemove actions like rotate, move, etc.
var elementTransition = function(window,elems) {
var mode = svgCanvas.getMode();
var elem = elems[0];
if(!elem) return;
const multiselected = (elems.length >= 2 && elems[1] != null) ? elems : null;
// Only updating fields for single elements for now
if(!multiselected) {
switch ( mode ) {
case "rotate":
var ang = svgCanvas.getRotationAngle(elem);
$('#angle').val(Math.round(ang));
rotateCursor(ang);
$('#tool_reorient').toggleClass('disabled', ang == 0);
break;
}
}
svgCanvas.runExtensions("elementTransition", {
elems: elems
});
};

32
src/js/exportHandler.js Normal file
View File

@ -0,0 +1,32 @@
var exportHandler = function(window, data) {
var issues = data.issues;
if(!$('#export_canvas').length) {
$('<canvas>', {id: 'export_canvas'}).hide().appendTo('body');
}
var c = $('#export_canvas')[0];
c.width = svgCanvas.contentW;
c.height = svgCanvas.contentH;
canvg(c, data.svg, {renderCallback: function() {
var datauri = c.toDataURL('image/png');
if (!datauri) return false;
var filename = "Method Draw Image";
var type = 'image/png';
var file = svgedit.utilities.dataURItoBlob(datauri, type);
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}});
};

View File

@ -1,3 +1,6 @@
var methodDraw = {};
methodDraw.addExtension = function(){}
/*
* ext-eyedropper.js
*

60
src/js/jquery.attr.js Normal file
View File

@ -0,0 +1,60 @@
(function() {
// This fixes $(...).attr() to work as expected with SVG elements.
// Does not currently use *AttributeNS() since we rarely need that.
// See http://api.jquery.com/attr/ for basic documentation of .attr()
// Additional functionality:
// - When getting attributes, a string that's a number is return as type number.
// - If an array is supplied as first parameter, multiple values are returned
// as an object with values for each given attributes
var proxied = jQuery.fn.attr, svgns = "http://www.w3.org/2000/svg";
jQuery.fn.attr = function(key, value) {
var len = this.length;
if(!len) return proxied.apply(this, arguments);
for(var i=0; i<len; i++) {
var elem = this[i];
// set/get SVG attribute
if(elem.namespaceURI === svgns) {
// Setting attribute
if(value !== undefined) {
elem.setAttribute(key, value);
} else if($.isArray(key)) {
// Getting attributes from array
var j = key.length, obj = {};
while(j--) {
var aname = key[j];
var attr = elem.getAttribute(aname);
// This returns a number when appropriate
if(attr || attr === "0") {
attr = isNaN(attr)?attr:attr-0;
}
obj[aname] = attr;
}
return obj;
} else if(typeof key === "object") {
// Setting attributes form object
for(var v in key) {
elem.setAttribute(v, key[v]);
}
// Getting attribute
} else {
var attr = elem.getAttribute(key);
if(attr || attr === "0") {
attr = isNaN(attr)?attr:attr-0;
}
return attr;
}
} else {
return proxied.apply(this, arguments);
}
}
return this;
};
}());

View File

@ -53,14 +53,11 @@ var svgedit = svgedit || {};
var menuItemIsValid = function(menuItem) {
return menuItem && menuItem.id && menuItem.label && menuItem.action && typeof menuItem.action == 'function';
}
// Defer injection to wait out initial menu processing. This probably goes away once all context
// menu behavior is brought here.
methodDraw.ready(function() {
for (menuItem in contextMenuExtensions) {
injectExtendedContextMenuItemIntoDom(contextMenuExtensions[menuItem]);
}
});
for (menuItem in contextMenuExtensions) {
injectExtendedContextMenuItemIntoDom(contextMenuExtensions[menuItem]);
}
svgedit.contextmenu.resetCustomMenus = function(){self.contextMenuExtensions = {}}
svgedit.contextmenu.add = addContextMenuItem;
svgedit.contextmenu.hasCustomHandler = hasCustomHandler;

View File

@ -1,5 +1,4 @@
window.methodDraw = function() {
var svgCanvas;
var Editor = {};
var is_ready = false;
var curConfig = {
@ -41,7 +40,7 @@ window.methodDraw = function() {
$("#canvas_height").val(curConfig.dimensions[1]);
Editor.canvas = svgCanvas = new $.SvgCanvas(document.getElementById("svgcanvas"), curConfig);
Editor.canvas = svgCanvas;
Editor.paintBox = {fill: null, stroke:null, canvas:null};
var isMac = (navigator.platform.indexOf("Mac") >= 0),
isWebkit = (navigator.userAgent.indexOf("AppleWebKit") >= 0),
@ -101,196 +100,6 @@ window.methodDraw = function() {
var preferences = false;
var cur_context = '';
var saveHandler = function(window,svg) {
};
var exportHandler = function(window, data) {
var issues = data.issues;
if(!$('#export_canvas').length) {
$('<canvas>', {id: 'export_canvas'}).hide().appendTo('body');
}
var c = $('#export_canvas')[0];
c.width = svgCanvas.contentW;
c.height = svgCanvas.contentH;
canvg(c, data.svg, {renderCallback: function() {
var datauri = c.toDataURL('image/png');
if (!datauri) return false;
var filename = "Method Draw Image";
var type = 'image/png';
var file = svgedit.utilities.dataURItoBlob(datauri, type);
if (window.navigator.msSaveOrOpenBlob) // IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else { // Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}});
};
// called when we've selected a different element
var selectedChanged = function(window,elems) {
var mode = svgCanvas.getMode();
if(mode === "select") setSelectMode();
if (mode === "pathedit") return updateContextPanel();
// if elems[1] is present, then we have more than one element
selectedElement = (elems.length == 1 || elems[1] == null ? elems[0] : null);
elems = elems.filter(Boolean)
multiselected = (elems.length >= 2) ? elems : false;
if (svgCanvas.elementsAreSame(multiselected)) selectedElement = multiselected[0]
if (selectedElement != null) {
$('#multiselected_panel').hide()
updateToolbar();
if (multiselected.length) { //multiselected elements are the same
$('#tools_top').addClass('multiselected')
}
}
else if (multiselected.length) {
$('.context_panel').hide()
$('#tools_top').removeClass('multiselected')
$('#multiselected_panel').show()
}
else {
$('.context_panel').hide()
$('#canvas_panel').show()
$('#tools_top').removeClass('multiselected')
}
// We need to update the context panel always when we've selected a different element. Otherwise some
// menu items are disabled even if they shouldn't be (e.g. group multiple elements)
updateContextPanel();
svgCanvas.runExtensions("selectedChanged", {
elems: elems,
selectedElement: selectedElement,
multiselected: multiselected
});
};
// Call when part of element is in process of changing, generally
// on mousemove actions like rotate, move, etc.
var elementTransition = function(window,elems) {
var mode = svgCanvas.getMode();
var elem = elems[0];
if(!elem) return;
multiselected = (elems.length >= 2 && elems[1] != null) ? elems : null;
// Only updating fields for single elements for now
if(!multiselected) {
switch ( mode ) {
case "rotate":
var ang = svgCanvas.getRotationAngle(elem);
$('#angle').val(Math.round(ang));
rotateCursor(ang);
$('#tool_reorient').toggleClass('disabled', ang == 0);
break;
}
}
svgCanvas.runExtensions("elementTransition", {
elems: elems
});
};
// called when any element has changed
var elementChanged = function(window,elems) {
var mode = svgCanvas.getMode();
if(mode === "select") {
setSelectMode();
}
for (var i = 0; i < elems.length; ++i) {
var elem = elems[i];
// if the element changed was the svg, then it could be a resolution change
if (elem && elem.tagName === "svg") {
//populateLayers();
updateCanvas();
}
// Update selectedElement if element is no longer part of the image.
// This occurs for the text elements in Firefox
else if(elem && selectedElement && selectedElement.parentNode == null) {
// || elem && elem.tagName == "path" && !multiselected) { // This was added in r1430, but not sure why
selectedElement = elem;
}
}
// we update the contextual panel with potentially new
// positional/sizing information (we DON'T want to update the
// toolbar here as that creates an infinite loop)
// also this updates the history buttons
// we tell it to skip focusing the text control if the
// text element was previously in focus
updateContextPanel();
// In the event a gradient was flipped:
if(selectedElement && mode === "select") {
Editor.paintBox.fill.update();
Editor.paintBox.stroke.update();
}
svgCanvas.runExtensions("elementChanged", {
elems: elems
});
};
var zoomChanged = function(window, bbox, autoCenter) {
var scrbar = 15,
res = svgCanvas.getResolution(),
w_area = workarea,
canvas_pos = $('#svgcanvas').position();
var z_info = svgCanvas.setBBoxZoom(bbox, w_area.width()-scrbar, w_area.height()-scrbar);
if(!z_info) return;
var zoomlevel = z_info.zoom,
bb = z_info.bbox;
if(zoomlevel < .001) {
changeZoom({value: .1});
return;
}
if (typeof animatedZoom != 'undefined') window.cancelAnimationFrame(animatedZoom)
// zoom duration 500ms
var start = Date.now();
var duration = 500;
var diff = (zoomlevel) - (res.zoom)
var zoom = $('#zoom')[0]
var current_zoom = res.zoom
var animateZoom = function(timestamp) {
var progress = Date.now() - start
var tick = progress / duration
tick = (Math.pow((tick-1), 3) +1);
svgCanvas.setZoom(current_zoom + (diff*tick));
updateCanvas();
if (tick < 1 && tick > -.90) {
window.animatedZoom = requestAnimationFrame(animateZoom)
}
else {
$("#zoom").val(parseInt(zoomlevel*100))
$("option", "#zoom_select").removeAttr("selected")
$("option[value="+ parseInt(zoomlevel*100) +"]", "#zoom_select").attr("selected", "selected")
}
}
animateZoom()
if(svgCanvas.getMode() == 'zoom' && bb.width) {
// Go to select if a zoom box was drawn
setSelectMode();
}
zoomDone();
}
$('#cur_context_panel').delegate('a', 'click', function() {
var link = $(this);
if(link.attr('data-root')) {
@ -301,37 +110,7 @@ window.methodDraw = function() {
svgCanvas.clearSelection();
return false;
});
var contextChanged = function(win, context) {
var link_str = '';
if(context) {
var str = '';
link_str = '<a href="#" data-root="y">' + svgCanvas.getCurrentDrawing().getCurrentLayerName() + '</a>';
$(context).parentsUntil('#svgcontent > g').addBack().each(function() {
if(this.id) {
str += ' > ' + this.id;
if(this !== context) {
link_str += ' > <a href="#">' + this.id + '</a>';
} else {
link_str += ' > ' + this.id;
}
}
});
cur_context = str;
} else {
cur_context = null;
}
$('#cur_context_panel').toggle(!!context).html(link_str);
}
var extAdded = function(window, ext) {
if(ext.callback) ext.callback();
}
// Makes sure the current selected paint is available to work with
var prepPaints = function() {
Editor.paintBox.fill.prep();
@ -398,56 +177,6 @@ window.methodDraw = function() {
if (!document.getElementById('canvas_background')) createBackground();
var fill = document.getElementById('canvas_background').getAttribute("fill");
// updates the toolbar (colors, opacity, etc) based on the selected element
// This function also updates the opacity and id elements that are in the context panel
var updateToolbar = function() {
if (selectedElement != null) {
switch ( selectedElement.tagName ) {
case 'use':
$(".context_panel").hide();
$("#use_panel").show();
break;
case 'image':
$(".context_panel").hide();
$("#image_panel").show();
break;
case 'foreignObject':
$(".context_panel").hide();
break;
case 'g':
case 'a':
// Look for common styles
var gWidth = null;
var childs = selectedElement.getElementsByTagName('*');
for(var i = 0, len = childs.length; i < len; i++) {
var swidth = childs[i].getAttribute("stroke-width");
if(i === 0) {
gWidth = swidth;
} else if(gWidth !== swidth) {
gWidth = null;
}
}
$('#stroke_width').val(gWidth === null ? "0" : gWidth);
updateContextPanel();
break;
default:
//removed because multiselect shouldnt set color
//Editor.paintBox.fill.update(false);
//Editor.paintBox.stroke.update(false);
$('#stroke_width').val(selectedElement.getAttribute("stroke-width") || 0);
var dash = selectedElement.getAttribute("stroke-dasharray") || "none"
$('option', '#stroke_style').removeAttr('selected');
$('#stroke_style option[value="'+ dash +'"]').attr("selected", "selected");
$('#stroke_style').trigger('change');
$.fn.dragInput.updateCursor($('#stroke_width')[0])
$.fn.dragInput.updateCursor($('#blur')[0])
}
}
// All elements including image and group have opacity
if(selectedElement != null) {
@ -470,249 +199,6 @@ window.methodDraw = function() {
}
// updates the context panel tools based on the selected element
var updateContextPanel = function(e) {
var elem = selectedElement;
// If element has just been deleted, consider it null
if(elem != null && !elem.parentNode) elem = null;
if (multiselected && multiselected[0] != null && !multiselected[0].parentNode) multiselected = false;
var currentLayerName = svgCanvas.getCurrentDrawing().getCurrentLayerName();
var currentMode = svgCanvas.getMode();
var unit = curConfig.baseUnit !== 'px' ? curConfig.baseUnit : null;
var is_node = currentMode == 'pathedit'; //elem ? (elem.id && elem.id.indexOf('pathpointgrip') == 0) : false;
if (is_node) {
$('.context_panel').hide();
$('#path_node_panel').show();
$('#stroke_panel').hide();
var point = path.getNodePoint();
$('#tool_add_subpath').removeClass('push_button_pressed').addClass('tool_button');
$('#tool_node_delete').toggleClass('disabled', !path.canDeleteNodes);
if(point) {
var seg_type = $('#seg_type');
if(unit) {
point.x = svgedit.units.convertUnit(point.x);
point.y = svgedit.units.convertUnit(point.y);
}
$('#path_node_x').val(Math.round(point.x));
$('#path_node_y').val(Math.round(point.y));
if(point.type) {
seg_type.val(point.type).removeAttr('disabled');
$("#seg_type_label").html(point.type == 4 ? "Straight" : "Curve")
} else {
seg_type.val(4).attr('disabled','disabled');
}
}
$("#tools_top").removeClass("multiselected")
$("#stroke_panel").hide();
$("#canvas_panel").hide();
return;
}
var menu_items = $('#cmenu_canvas li');
$('.context_panel').hide();
$('.menu_item', '#edit_menu').addClass('disabled');
$('.menu_item', '#object_menu').addClass('disabled');
//hack to show the proper multialign box
if (multiselected) {
multiselected = multiselected.filter(Boolean);
elem = (svgCanvas.elementsAreSame(multiselected)) ? multiselected[0] : null
if (elem) $("#tools_top").addClass("multiselected")
}
if (!elem && !multiselected) {
$("#tools_top").removeClass("multiselected")
$("#stroke_panel").hide();
$("#canvas_panel").show();
}
if (elem != null) {
$("#stroke_panel").show();
var elname = elem.nodeName;
var angle = svgCanvas.getRotationAngle(elem);
$('#angle').val(Math.round(angle));
var blurval = svgCanvas.getBlur(elem);
$('#blur').val(blurval);
if(!is_node && currentMode != 'pathedit') {
$('#selected_panel').show();
$('.action_selected').removeClass('disabled');
// Elements in this array already have coord fields
var x, y
if(['g', 'polyline', 'path'].indexOf(elname) >= 0) {
var bb = svgCanvas.getStrokedBBox([elem]);
if(bb) {
x = bb.x;
y = bb.y;
}
}
if(unit) {
x = svgedit.units.convertUnit(x);
y = svgedit.units.convertUnit(y);
}
$("#" + elname +"_x").val(Math.round(x))
$("#" + elname +"_y").val(Math.round(y))
if (elname === "polyline") {
//we're acting as if polylines were paths
$("#path_x").val(Math.round(x))
$("#path_y").val(Math.round(y))
}
// Elements in this array cannot be converted to a path
var no_path = ['image', 'text', 'path', 'g', 'use'].indexOf(elname) == -1;
if (no_path) $('.action_path_convert_selected').removeClass('disabled');
if (elname === "path") $('.action_path_selected').removeClass('disabled');
}
var link_href = null;
if (el_name === 'a') {
link_href = svgCanvas.getHref(elem);
$('#g_panel').show();
}
if(elem.parentNode.tagName === 'a') {
if(!$(elem).siblings().length) {
$('#a_panel').show();
link_href = svgCanvas.getHref(elem.parentNode);
}
}
// Hide/show the make_link buttons
$('#tool_make_link, #tool_make_link').toggle(!link_href);
if(link_href) {
$('#link_url').val(link_href);
}
// update contextual tools here
var panels = {
g: [],
a: [],
rect: ['rx','width','height', 'x', 'y'],
image: ['width','height', 'x', 'y'],
circle: ['cx','cy','r'],
ellipse: ['cx','cy','rx','ry'],
line: ['x1','y1','x2','y2'],
text: ['x', 'y'],
'use': [],
path : []
};
var el_name = elem.tagName;
if($(elem).data('gsvg')) {
$('#g_panel').show();
}
if (el_name == "path" || el_name == "polyline") {
$('#path_panel').show();
}
if(panels[el_name]) {
var cur_panel = panels[el_name];
$('#' + el_name + '_panel').show();
// corner radius has to live in a different panel
// because otherwise it changes the position of the
// of the elements
if(el_name == "rect") $("#cornerRadiusLabel").show()
else $("#cornerRadiusLabel").hide()
$.each(cur_panel, function(i, item) {
var attrVal = elem.getAttribute(item);
if(curConfig.baseUnit !== 'px' && elem[item]) {
var bv = elem[item].baseVal.value;
attrVal = svgedit.units.convertUnit(bv);
}
//update the draginput cursors
var name_item = document.getElementById(el_name + '_' + item);
name_item.value = Math.round(attrVal) || 0;
if (name_item.getAttribute("data-cursor") === "true") {
$.fn.dragInput.updateCursor(name_item );
}
});
if(el_name == 'text') {
var font_family = elem.getAttribute("font-family");
var cleanFontFamily = font_family.split(",")[0].replace(/'/g, "");
var select = document.getElementById("font_family_dropdown");
$('#text_panel').css("display", "inline");
$('#tool_italic').toggleClass('active', svgCanvas.getItalic())
$('#tool_bold').toggleClass('active', svgCanvas.getBold())
$('#tool_italic').toggleClass('disabled', fonts[cleanFontFamily] ? !fonts[cleanFontFamily]["Italic"] : false)
$('#tool_bold').toggleClass('disabled', fonts[cleanFontFamily] ? !fonts[cleanFontFamily]["Bold"] : false);
$('#font_family').val(font_family);
$(select).find(`option[value=${font_family}]`).prop("selected", true);
$('#font_size').val(elem.getAttribute("font-size"));
$('#text').val(elem.textContent);
$('#preview_font').text(cleanFontFamily).css('font-family', font_family);
} // text
else if(el_name == 'image') {
setImageURL(svgCanvas.getHref(elem));
} // image
else if(el_name === 'g' || el_name === 'use') {
$('#container_panel').show();
$('.action_group_selected').removeClass('disabled');
var title = svgCanvas.getTitle();
}
}
menu_items[(el_name === 'g' ? 'en':'dis') + 'ableContextMenuItems']('#ungroup');
menu_items[((el_name === 'g' || !multiselected) ? 'dis':'en') + 'ableContextMenuItems']('#group');
}
if (multiselected) {
$('#multiselected_panel').show();
$('.action_multi_selected').removeClass('disabled');
menu_items
.enableContextMenuItems('#group')
.disableContextMenuItems('#ungroup');
}
if (!elem) {
menu_items.disableContextMenuItems('#delete,#cut,#copy,#ungroup,#move_front,#move_up,#move_down,#move_back');
}
// update history buttons
if (undoMgr.getUndoStackSize() > 0) {
$('#tool_undo').removeClass( 'disabled');
}
else {
$('#tool_undo').addClass( 'disabled');
}
if (undoMgr.getRedoStackSize() > 0) {
$('#tool_redo').removeClass( 'disabled');
}
else {
$('#tool_redo').addClass( 'disabled');
}
svgCanvas.addedNew = false;
if ( (elem && !is_node) || multiselected) {
// update the selected elements' layer
$('#selLayerNames').removeAttr('disabled').val(currentLayerName);
// Enable regular menu options
canv_menu.enableContextMenuItems('#delete,#cut,#copy,#move_front,#move_up,#move_down,#move_back');
}
};
// bind the selected event to our function that handles updates to the UI
svgCanvas.bind("selected", selectedChanged);
svgCanvas.bind("transition", elementTransition);
svgCanvas.bind("changed", elementChanged);
svgCanvas.bind("exported", exportHandler);
svgCanvas.bind("zoomed", zoomChanged);
svgCanvas.bind("contextset", contextChanged);
svgCanvas.bind("extension_added", extAdded);
svgCanvas.textActions.setInputElem($("#text")[0]);
var changeFontSize = function(ctl) {
svgCanvas.setFontSize(ctl.value);
@ -904,29 +390,6 @@ window.methodDraw = function() {
$(opt).addClass('current').siblings().removeClass('current');
}
//menu handling
var menus = $('.menu');
var blinker = function(e) {
e.target.style.background = "#fff";
setTimeout(function(){e.target.style.background = "#ddd";}, 50);
setTimeout(function(){e.target.style.background = "#fff";}, 150);
setTimeout(function(){e.target.style.background = "#ddd";}, 200);
setTimeout(function(){e.target.style.background = "";}, 200);
setTimeout(function(){$('#menu_bar').removeClass('active')}, 220);
return false;
}
var closer = function(e){
if (e.target.nodeName && e.target.nodeName.toLowerCase() === "input") return false;
if (!$(e.target).hasClass("menu_title") && !$(e.target).parent().hasClass("menu_title")) {
if(!$(e.target).hasClass("disabled") && $(e.target).hasClass("menu_item")) blinker(e)
else $('#menu_bar').removeClass('active')
}
}
$('.menu_item').on('mousedown touchstart', function(e){blinker(e)});
$("svg, body").on('mousedown touchstart', function(e){closer(e)});
var accumulatedDelta = 0
$('#workarea').on('mousewheel', function(e, delta, deltaX, deltaY){
if (e.altKey || e.ctrlKey) {

View File

@ -1,169 +1,174 @@
// todo remove from global scope;
var r_intervals = [];
for(var i = .1; i < 1E5; i *= 10) {
r_intervals.push(1 * i);
r_intervals.push(2 * i);
r_intervals.push(5 * i);
}
MD.Rulers = function(){
function updateRulers(svgCanvas, scanvas, zoom) {
var r_intervals = [];
for(var i = .1; i < 1E5; i *= 10) {
r_intervals.push(1 * i);
r_intervals.push(2 * i);
r_intervals.push(5 * i);
}
var workarea = document.getElementById("workarea");
var title_show = document.getElementById("title_show");
var offset_x = 66;
var offset_y = 48;
if(!zoom) zoom = svgCanvas.getZoom();
if(!scanvas) scanvas = $("#svgcanvas");
var limit = 30000;
var c_elem = svgCanvas.getContentElem();
var units = svgedit.units.getTypeMap();
var unit = 1;
function update(zoom) {
for(var d = 0; d < 2; d++) {
var is_x = (d === 0);
var dim = is_x ? 'x' : 'y';
var lentype = is_x ?'width':'height';
var notlentype = is_x ?'height':'width';
var content_d = c_elem.getAttribute(dim)-0;
var workarea = document.getElementById("workarea");
var title_show = document.getElementById("title_show");
var offset_x = 66;
var offset_y = 48;
if(!zoom) zoom = svgCanvas.getZoom();
const scanvas = $("#svgcanvas");
var $hcanv_orig = $('#ruler_' + dim + ' canvas:first');
var limit = 30000;
// Bit of a hack to fully clear the canvas in Safari & IE9
$hcanv = $hcanv_orig.clone();
$hcanv_orig.replaceWith($hcanv);
var c_elem = svgCanvas.getContentElem();
var hcanv = $hcanv[0];
// Set the canvas size to the width of the container
var ruler_len = scanvas[lentype]()*2;
var total_len = ruler_len;
hcanv.parentNode.style[lentype] = total_len + 'px';
var canv_count = 1;
var ctx_num = 0;
var ctx_arr;
var ctx = hcanv.getContext("2d");
var scale = window.devicePixelRatio || 1;
hcanv.style[lentype] = total_len + "px";
hcanv.style[notlentype] = 15 + "px";
hcanv[lentype] = Math.floor(total_len * scale);
hcanv[notlentype] = Math.floor(15 * scale);
ctx.scale(scale,scale);
var units = svgedit.units.getTypeMap();
var unit = 1;
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(0,0,hcanv.width/scale,hcanv.height/scale);
// Remove any existing canvasses
$hcanv.siblings().remove();
// Create multiple canvases when necessary (due to browser limits)
if(ruler_len >= limit) {
var num = parseInt(ruler_len / limit) + 1;
ctx_arr = Array(num);
ctx_arr[0] = ctx;
for(var i = 1; i < num; i++) {
hcanv[lentype] = limit;
var copy = hcanv.cloneNode(true);
hcanv.parentNode.appendChild(copy);
ctx_arr[i] = copy.getContext('2d');
}
for(var d = 0; d < 2; d++) {
var is_x = (d === 0);
var dim = is_x ? 'x' : 'y';
var lentype = is_x ?'width':'height';
var notlentype = is_x ?'height':'width';
var content_d = c_elem.getAttribute(dim)-0;
copy[lentype] = ruler_len % limit;
var $hcanv_orig = $('#ruler_' + dim + ' canvas:first');
// set copy width to last
ruler_len = limit;
}
hcanv[lentype] = ruler_len * scale;
var u_multi = unit * zoom;
// Calculate the main number interval
var raw_m = 50 / u_multi;
var multi = 1;
for(var i = 0; i < r_intervals.length; i++) {
var num = r_intervals[i];
multi = num;
if(raw_m <= num) {
break;
}
}
var big_int = multi * u_multi;
ctx.font = "normal 9px 'Verdana', sans-serif";
ctx.fillStyle = "#777";
ctx.scale(scale,scale);
// Bit of a hack to fully clear the canvas in Safari & IE9
$hcanv = $hcanv_orig.clone();
$hcanv_orig.replaceWith($hcanv);
var hcanv = $hcanv[0];
// Set the canvas size to the width of the container
var ruler_len = scanvas[lentype]()*2;
var total_len = ruler_len;
hcanv.parentNode.style[lentype] = total_len + 'px';
var canv_count = 1;
var ctx_num = 0;
var ctx_arr;
var ctx = hcanv.getContext("2d");
var scale = window.devicePixelRatio || 1;
hcanv.style[lentype] = total_len + "px";
hcanv.style[notlentype] = 15 + "px";
hcanv[lentype] = Math.floor(total_len * scale);
hcanv[notlentype] = Math.floor(15 * scale);
ctx.scale(scale,scale);
var ruler_d = ((content_d / u_multi) % multi) * u_multi;
var label_pos = ruler_d - big_int;
for (; ruler_d < total_len; ruler_d += big_int) {
label_pos += big_int;
var real_d = ruler_d - content_d;
var cur_d = Math.round(ruler_d) + .5;
if(is_x) {
ctx.moveTo(cur_d, 15);
ctx.lineTo(cur_d, 0);
} else {
ctx.moveTo(15, cur_d);
ctx.lineTo(0, cur_d);
}
var num = (label_pos - content_d) / u_multi;
var label;
if(multi >= 1) {
label = Math.round(num);
} else {
var decs = (multi+'').split('.')[1].length;
label = num.toFixed(decs)-0;
}
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect(0,0,hcanv.width/scale,hcanv.height/scale);
// Change 1000s to Ks
if(label !== 0 && label !== 1000 && label % 1000 === 0) {
label = (label / 1000) + 'K';
}
// Remove any existing canvasses
$hcanv.siblings().remove();
if(is_x) {
ctx.fillText(label, ruler_d+2, 8);
ctx.fillStyle = "#777";
} else {
var str = (label+'').split('');
for(var i = 0; i < str.length; i++) {
ctx.fillText(str[i], 1, (ruler_d+9) + i*9);
ctx.fillStyle = "#777";
}
}
var part = big_int / 10;
for(var i = 1; i < 10; i++) {
var sub_d = Math.round(ruler_d + part * i) + .5;
if(ctx_arr && sub_d > ruler_len) {
ctx_num++;
ctx.stroke();
if(ctx_num >= ctx_arr.length) {
i = 10;
ruler_d = total_len;
continue;
}
ctx = ctx_arr[ctx_num];
ruler_d -= limit;
sub_d = Math.round(ruler_d + part * i) + .5;
// Create multiple canvases when necessary (due to browser limits)
if(ruler_len >= limit) {
var num = parseInt(ruler_len / limit) + 1;
ctx_arr = Array(num);
ctx_arr[0] = ctx;
for(var i = 1; i < num; i++) {
hcanv[lentype] = limit;
var copy = hcanv.cloneNode(true);
hcanv.parentNode.appendChild(copy);
ctx_arr[i] = copy.getContext('2d');
}
var line_num = (i % 2)?12:10;
if(is_x) {
ctx.moveTo(sub_d, 15);
ctx.lineTo(sub_d, line_num);
} else {
ctx.moveTo(15, sub_d);
ctx.lineTo(line_num ,sub_d);
copy[lentype] = ruler_len % limit;
// set copy width to last
ruler_len = limit;
}
hcanv[lentype] = ruler_len * scale;
var u_multi = unit * zoom;
// Calculate the main number interval
var raw_m = 50 / u_multi;
var multi = 1;
for(var i = 0; i < r_intervals.length; i++) {
var num = r_intervals[i];
multi = num;
if(raw_m <= num) {
break;
}
}
var big_int = multi * u_multi;
ctx.font = "normal 9px 'Verdana', sans-serif";
ctx.fillStyle = "#777";
ctx.scale(scale,scale);
var ruler_d = ((content_d / u_multi) % multi) * u_multi;
var label_pos = ruler_d - big_int;
for (; ruler_d < total_len; ruler_d += big_int) {
label_pos += big_int;
var real_d = ruler_d - content_d;
var cur_d = Math.round(ruler_d) + .5;
if(is_x) {
ctx.moveTo(cur_d, 15);
ctx.lineTo(cur_d, 0);
} else {
ctx.moveTo(15, cur_d);
ctx.lineTo(0, cur_d);
}
var num = (label_pos - content_d) / u_multi;
var label;
if(multi >= 1) {
label = Math.round(num);
} else {
var decs = (multi+'').split('.')[1].length;
label = num.toFixed(decs)-0;
}
// Change 1000s to Ks
if(label !== 0 && label !== 1000 && label % 1000 === 0) {
label = (label / 1000) + 'K';
}
if(is_x) {
ctx.fillText(label, ruler_d+2, 8);
ctx.fillStyle = "#777";
} else {
var str = (label+'').split('');
for(var i = 0; i < str.length; i++) {
ctx.fillText(str[i], 1, (ruler_d+9) + i*9);
ctx.fillStyle = "#777";
}
}
var part = big_int / 10;
for(var i = 1; i < 10; i++) {
var sub_d = Math.round(ruler_d + part * i) + .5;
if(ctx_arr && sub_d > ruler_len) {
ctx_num++;
ctx.stroke();
if(ctx_num >= ctx_arr.length) {
i = 10;
ruler_d = total_len;
continue;
}
ctx = ctx_arr[ctx_num];
ruler_d -= limit;
sub_d = Math.round(ruler_d + part * i) + .5;
}
var line_num = (i % 2)?12:10;
if(is_x) {
ctx.moveTo(sub_d, 15);
ctx.lineTo(sub_d, line_num);
} else {
ctx.moveTo(15, sub_d);
ctx.lineTo(line_num ,sub_d);
}
}
}
ctx.strokeStyle = "#666";
ctx.stroke();
}
ctx.strokeStyle = "#666";
ctx.stroke();
}
}
this.update = update();
}

View File

@ -0,0 +1 @@

9
src/js/start.js Normal file
View File

@ -0,0 +1,9 @@
// globals
const svgCanvas = new $.SvgCanvas(document.getElementById("svgcanvas"));
const editor = new MD.Editor();
editor.menu = new MD.Menu();
editor.toolbar = new MD.Toolbar();
editor.rulers = new MD.Rulers();
const state = new State();
state.set("canvasId", t("Untitled"));
state.set("canvasMode", state.get("canvasMode"));

73
src/js/state.js Normal file
View File

@ -0,0 +1,73 @@
function State(){
const _self = this;
const tenThousandThings = dao.map(thing => thing.name);
const saveableKeys = dao.filter(thing => thing.save).map(thing => thing.name);
this.data = _loadData();
this.set = (key, val) => {
key = key.split("-")[0] || key;
if (tenThousandThings.indexOf(key) === -1) return console.warn( key + " not implemented");
const archetype = dao.find(thing => thing.name === key);
val = _self.data[_getKey(key)] = archetype.clean(val);
if (~saveableKeys.indexOf(key)) _save(_getKey(key), val);
_self[key](val);
}
this.get = (key) => {
return _self.data[_getKey(key)];
}
this.refresh = () => { dao.forEach(thing => this[thing.name]( this.get(thing.name) ) ); }
// canvas data
this.canvasId = (id) => {/* noop */}
this.canvasMode = (mode) => { editor.toolbar.setMode(mode) }
this.clean = (warn = true) => {
if (warn) {
const confirmed = confirm("Deletes all configuration and text, are you sure?");
if (!confirmed) return;
}
Object.keys(localStorage).forEach(key => localStorage.removeItem(key));
write.reset();
}
// INNER UTILS
function _save(key, val) {
// basic checks
//console.log(key, val)
if (val === undefined || val === null) throw "wont save nuthin, " + key + " " + val;
localStorage.setItem("write" + "-" + key, val.toString());
}
function _getKey(name) {
const key = name.indexOf("page") !== -1
|| name.indexOf("assignment") !== -1
? name + ID
: name + "-0"; // system
return key;
}
function _loadData() {
const data = dao.map(thing => {
return {
[_getKey(thing.name)]: _getValue(_getKey(thing.name), utils.findGetParameter(thing.name) || thing.default)
}
});
return Object.assign({}, ...data);
};
function _getValue(key, def) {
const item = localStorage.getItem("write-" + key) || def;
const archetype = dao.find(thing => thing.name === key.split("-")[0]);
if (archetype) return archetype.clean(item);
else throw "Unkown archetype";
}
}

View File

@ -24,68 +24,6 @@
/*jslint browser: true*/
(function() {
// This fixes $(...).attr() to work as expected with SVG elements.
// Does not currently use *AttributeNS() since we rarely need that.
// See http://api.jquery.com/attr/ for basic documentation of .attr()
// Additional functionality:
// - When getting attributes, a string that's a number is return as type number.
// - If an array is supplied as first parameter, multiple values are returned
// as an object with values for each given attributes
var proxied = jQuery.fn.attr, svgns = "http://www.w3.org/2000/svg";
jQuery.fn.attr = function(key, value) {
var len = this.length;
if(!len) return proxied.apply(this, arguments);
for(var i=0; i<len; i++) {
var elem = this[i];
// set/get SVG attribute
if(elem.namespaceURI === svgns) {
// Setting attribute
if(value !== undefined) {
elem.setAttribute(key, value);
} else if($.isArray(key)) {
// Getting attributes from array
var j = key.length, obj = {};
while(j--) {
var aname = key[j];
var attr = elem.getAttribute(aname);
// This returns a number when appropriate
if(attr || attr === "0") {
attr = isNaN(attr)?attr:attr-0;
}
obj[aname] = attr;
}
return obj;
} else if(typeof key === "object") {
// Setting attributes form object
for(var v in key) {
elem.setAttribute(v, key[v]);
}
// Getting attribute
} else {
var attr = elem.getAttribute(key);
if(attr || attr === "0") {
attr = isNaN(attr)?attr:attr-0;
}
return attr;
}
} else {
return proxied.apply(this, arguments);
}
}
return this;
};
}());
// Class: SvgCanvas
// The main SvgCanvas class that manages all SVG-related functions
//
@ -107,7 +45,10 @@ var svgns = "http://www.w3.org/2000/svg",
var curConfig = {
show_outside_canvas: true,
selectNew: true,
dimensions: [640, 480]
dimensions: [800, 600],
initFill: {color: 'fff', opacity: 1},
initStroke: {width: 1, color: '000', opacity: 1},
imgPath: 'images/',
};
// Update config with new one if given

33
src/js/translate.js Normal file
View File

@ -0,0 +1,33 @@
function t(key){
const lang = typeof state === "undefined" ? "en" : state.get("language");
const dict = {
"Untitled": "Sin Título",
}
const keys = Object.keys(dict);
const values = Object.values(dict);
const originWord = keys.find(word => key === word);
const destinationWord = values.find(word => key === word);
if (!originWord && !destinationWord) {
console.log( "translation missing: " + key);
return key;
}
const translation = lang === "en" ? key : dict[key]// english input;
return translation
}
function translateDom(lang){
const els = dom.qsa("[data-translate]");
els.forEach(el => {
const translation = el.getAttribute("data-translate");
const source = el.innerHTML;
el.innerHTML = translation;
el.setAttribute("data-translate", source);
});
}

12
src/js/utils.js Normal file
View File

@ -0,0 +1,12 @@
const utils = {};
utils.findGetParameter = function(parameterName) {
var result = null,
tmp = [];
var items = location.search.substr(1).split("&");
for (var index = 0; index < items.length; index++) {
tmp = items[index].split("=");
if (tmp[0] === parameterName) result = decodeURIComponent(tmp[1]);
}
return result;
}

46
src/js/zoomChanged.js Normal file
View File

@ -0,0 +1,46 @@
var zoomChanged = function(window, bbox, autoCenter) {
var scrbar = 15,
res = svgCanvas.getResolution(),
w_area = workarea,
canvas_pos = $('#svgcanvas').position();
var z_info = svgCanvas.setBBoxZoom(bbox, w_area.width()-scrbar, w_area.height()-scrbar);
if(!z_info) return;
var zoomlevel = z_info.zoom,
bb = z_info.bbox;
if(zoomlevel < .001) {
changeZoom({value: .1});
return;
}
if (typeof animatedZoom != 'undefined') window.cancelAnimationFrame(animatedZoom)
// zoom duration 500ms
var start = Date.now();
var duration = 500;
var diff = (zoomlevel) - (res.zoom)
var zoom = $('#zoom')[0]
var current_zoom = res.zoom
var animateZoom = function(timestamp) {
var progress = Date.now() - start
var tick = progress / duration
tick = (Math.pow((tick-1), 3) +1);
svgCanvas.setZoom(current_zoom + (diff*tick));
updateCanvas();
if (tick < 1 && tick > -.90) {
window.animatedZoom = requestAnimationFrame(animateZoom)
}
else {
$("#zoom").val(parseInt(zoomlevel*100))
$("option", "#zoom_select").removeAttr("selected")
$("option[value="+ parseInt(zoomlevel*100) +"]", "#zoom_select").attr("selected", "selected")
}
}
animateZoom()
if(svgCanvas.getMode() == 'zoom' && bb.width) {
// Go to select if a zoom box was drawn
setSelectMode();
}
zoomDone();
}