2018-05-16 08:32:44 +00:00
/* eslint-disable no-var, indent, no-redeclare */
/* globals $, svgedit, svgCanvas, jsPDF, canvg */
2011-10-01 04:41:14 +00:00
/ *
2009-11-25 13:51:42 +00:00
* svgcanvas . js
*
2012-09-16 18:53:27 +00:00
* Licensed under the MIT License
2009-11-25 13:51:42 +00:00
*
2010-01-06 04:35:41 +00:00
* Copyright ( c ) 2010 Alexis Deveria
* Copyright ( c ) 2010 Pavol Rusnak
* Copyright ( c ) 2010 Jeff Schiller
2009-11-25 13:51:42 +00:00
*
* /
2010-01-08 20:36:39 +00:00
2010-10-28 06:08:45 +00:00
// Dependencies:
// 1) jQuery
2015-11-05 03:25:30 +00:00
// 2) pathseg.js
// 3) browser.js
// 4) svgtransformlist.js
// 5) math.js
// 6) units.js
// 7) svgutils.js
// 8) sanitize.js
// 9) history.js
// 10) select.js
// 11) draw.js
// 12) path.js
// 13) coords.js
// 14) recalculate.js
2010-10-28 06:08:45 +00:00
2014-02-17 06:48:40 +00:00
( function ( ) {
2013-02-15 15:51:58 +00:00
if ( ! window . console ) {
2009-09-22 21:50:55 +00:00
window . console = { } ;
2018-05-16 08:32:44 +00:00
window . console . log = function ( str ) { } ;
window . console . dir = function ( str ) { } ;
2009-06-23 05:17:53 +00:00
}
2013-02-15 15:51:58 +00:00
if ( window . opera ) {
2018-05-16 08:32:44 +00:00
window . console . log = function ( str ) { opera . postError ( str ) ; } ;
window . console . dir = function ( str ) { } ;
2009-12-31 15:25:57 +00:00
}
2014-01-31 10:40:52 +00:00
} ( ) ) ;
2010-06-18 20:35:47 +00:00
// Class: SvgCanvas
// The main SvgCanvas class that manages all SVG-related functions
//
// Parameters:
// container - The container HTML element that should hold the SVG root element
// config - An object that contains configuration data
2018-05-16 00:53:27 +00:00
$ . SvgCanvas = function ( container , config ) {
2013-02-16 15:02:26 +00:00
// Alias Namespace constants
var NS = svgedit . NS ;
2010-11-07 05:20:03 +00:00
2010-11-06 00:45:21 +00:00
// Default configuration options
var curConfig = {
show _outside _canvas : true ,
2011-01-13 16:17:00 +00:00
selectNew : true ,
2010-11-06 00:45:21 +00:00
dimensions : [ 640 , 480 ]
} ;
// Update config with new one if given
2013-02-15 15:51:58 +00:00
if ( config ) {
2010-11-06 00:45:21 +00:00
$ . extend ( curConfig , config ) ;
}
2010-11-05 17:00:32 +00:00
2010-11-15 09:25:49 +00:00
// Array with width/height of canvas
var dimensions = curConfig . dimensions ;
2010-11-12 19:08:29 +00:00
var canvas = this ;
2010-11-07 05:20:03 +00:00
// "document" element associated with the container (same as window.document using default svg-editor.js)
2011-01-13 06:57:26 +00:00
// NOTE: This is not actually a SVG document, but a HTML document.
2010-11-07 05:20:03 +00:00
var svgdoc = container . ownerDocument ;
2010-11-15 09:25:49 +00:00
// This is a container for the document being edited, not the document itself.
var svgroot = svgdoc . importNode ( svgedit . utilities . text2xml (
2018-05-16 08:32:44 +00:00
'<svg id="svgroot" xmlns="' + NS . SVG + '" xlinkns="' + NS . XLINK + '" ' +
'width="' + dimensions [ 0 ] + '" height="' + dimensions [ 1 ] + '" x="' + dimensions [ 0 ] + '" y="' + dimensions [ 1 ] + '" overflow="visible">' +
'<defs>' +
'<filter id="canvashadow" filterUnits="objectBoundingBox">' +
'<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur"/>' +
'<feOffset in="blur" dx="5" dy="5" result="offsetBlur"/>' +
'<feMerge>' +
'<feMergeNode in="offsetBlur"/>' +
'<feMergeNode in="SourceGraphic"/>' +
'</feMerge>' +
'</filter>' +
'</defs>' +
'</svg>' ) . documentElement ,
true
) ;
2010-11-15 09:25:49 +00:00
container . appendChild ( svgroot ) ;
2010-11-07 05:20:03 +00:00
// The actual element that represents the final output SVG element
2014-04-08 03:21:22 +00:00
var svgcontent = svgdoc . createElementNS ( NS . SVG , 'svg' ) ;
2010-11-07 05:20:03 +00:00
2011-02-01 07:22:18 +00:00
// This function resets the svgcontent element while keeping it in the DOM.
2018-05-16 08:32:44 +00:00
var clearSvgContentElement = canvas . clearSvgContentElement = function ( ) {
2011-02-01 07:22:18 +00:00
while ( svgcontent . firstChild ) { svgcontent . removeChild ( svgcontent . firstChild ) ; }
// TODO: Clear out all other attributes first?
$ ( svgcontent ) . attr ( {
id : 'svgcontent' ,
width : dimensions [ 0 ] ,
height : dimensions [ 1 ] ,
x : dimensions [ 0 ] ,
y : dimensions [ 1 ] ,
overflow : curConfig . show _outside _canvas ? 'visible' : 'hidden' ,
2013-02-16 15:02:26 +00:00
xmlns : NS . SVG ,
2014-04-08 03:21:22 +00:00
'xmlns:se' : NS . SE ,
'xmlns:xlink' : NS . XLINK
2011-02-01 07:22:18 +00:00
} ) . appendTo ( svgroot ) ;
// TODO: make this string optional and set by the client
2018-05-16 08:32:44 +00:00
var comment = svgdoc . createComment ( ' Created with SVG-edit - https://github.com/SVG-Edit/svgedit' ) ;
2011-02-01 07:22:18 +00:00
svgcontent . appendChild ( comment ) ;
} ;
clearSvgContentElement ( ) ;
2010-11-15 09:25:49 +00:00
2011-01-14 18:18:29 +00:00
// Prefix string for element IDs
2014-04-08 03:21:22 +00:00
var idprefix = 'svg_' ;
2011-01-14 18:18:29 +00:00
// Function: setIdPrefix
// Changes the ID prefix to the given value
//
2018-05-16 00:53:27 +00:00
// Parameters:
// p - String with the new prefix
2018-05-16 08:32:44 +00:00
canvas . setIdPrefix = function ( p ) {
2011-01-14 18:18:29 +00:00
idprefix = p ;
} ;
2011-01-16 23:07:35 +00:00
// Current svgedit.draw.Drawing object
// @type {svgedit.draw.Drawing}
2011-02-01 07:22:18 +00:00
canvas . current _drawing _ = new svgedit . draw . Drawing ( svgcontent , idprefix ) ;
2011-01-14 18:18:29 +00:00
2011-01-16 23:07:35 +00:00
// Function: getCurrentDrawing
// Returns the current Drawing.
// @return {svgedit.draw.Drawing}
2018-05-16 08:32:44 +00:00
var getCurrentDrawing = canvas . getCurrentDrawing = function ( ) {
2011-02-01 07:22:18 +00:00
return canvas . current _drawing _ ;
2011-01-14 18:18:29 +00:00
} ;
2010-11-07 05:20:03 +00:00
// Float displaying the current zoom level (1 = 100%, .5 = 50%, etc)
2018-05-16 08:32:44 +00:00
var currentZoom = 1 ;
2010-11-07 05:20:03 +00:00
2010-11-15 09:25:49 +00:00
// pointer to current group (for in-group editing)
2018-05-16 08:32:44 +00:00
var currentGroup = null ;
2010-11-15 09:25:49 +00:00
// Object containing data for the currently selected styles
2018-05-16 08:32:44 +00:00
var allProperties = {
2010-11-15 09:25:49 +00:00
shape : {
2018-05-16 08:32:44 +00:00
fill : ( curConfig . initFill . color === 'none' ? '' : '#' ) + curConfig . initFill . color ,
2010-11-15 09:25:49 +00:00
fill _paint : null ,
fill _opacity : curConfig . initFill . opacity ,
2014-04-08 03:21:22 +00:00
stroke : '#' + curConfig . initStroke . color ,
2010-11-15 09:25:49 +00:00
stroke _paint : null ,
stroke _opacity : curConfig . initStroke . opacity ,
stroke _width : curConfig . initStroke . width ,
stroke _dasharray : 'none' ,
stroke _linejoin : 'miter' ,
stroke _linecap : 'butt' ,
opacity : curConfig . initOpacity
}
} ;
2018-05-16 08:32:44 +00:00
allProperties . text = $ . extend ( true , { } , allProperties . shape ) ;
$ . extend ( allProperties . text , {
2014-04-08 03:21:22 +00:00
fill : '#000000' ,
2016-03-15 07:43:24 +00:00
stroke _width : curConfig . text . stroke _width ,
font _size : curConfig . text . font _size ,
font _family : curConfig . text . font _family
2010-11-15 09:25:49 +00:00
} ) ;
// Current shape style properties
2018-05-16 08:32:44 +00:00
var curShape = allProperties . shape ;
2010-11-15 09:25:49 +00:00
2010-12-02 17:14:24 +00:00
// Array with all the currently selected elements
// default size of 1 until it needs to grow bigger
2015-12-01 03:08:13 +00:00
var selectedElements = [ ] ;
2010-12-02 17:14:24 +00:00
2018-05-16 08:32:44 +00:00
var getJsonFromSvgElement = this . getJsonFromSvgElement = function ( data ) {
2018-01-18 12:37:49 +00:00
// Text node
2018-05-16 08:32:44 +00:00
if ( data . nodeType === 3 ) return data . nodeValue ;
2018-01-18 12:37:49 +00:00
var retval = {
element : data . tagName ,
2018-05-16 08:32:44 +00:00
// namespace: nsMap[data.namespaceURI],
2018-01-18 12:37:49 +00:00
attr : { } ,
2018-05-16 08:32:44 +00:00
children : [ ]
2018-01-18 12:37:49 +00:00
} ;
2018-01-22 09:14:42 +00:00
// Iterate attributes
2018-05-16 08:32:44 +00:00
for ( var i = 0 ; i < data . attributes . length ; i ++ ) {
2018-01-18 12:37:49 +00:00
retval . attr [ data . attributes [ i ] . name ] = data . attributes [ i ] . value ;
} ;
// Iterate children
2018-05-16 08:32:44 +00:00
for ( var i = 0 ; i < data . childNodes . length ; i ++ ) {
2018-01-18 12:37:49 +00:00
retval . children . push ( getJsonFromSvgElement ( data . childNodes [ i ] ) ) ;
}
return retval ;
2018-05-16 08:32:44 +00:00
} ;
2018-01-18 12:37:49 +00:00
2010-11-15 09:25:49 +00:00
// Function: addSvgElementFromJson
// Create a new SVG element based on the given object keys/values and add it to the current layer
2018-05-16 00:53:27 +00:00
// The element will be ran through cleanupElement before being returned
2010-11-15 09:25:49 +00:00
//
// Parameters:
// data - Object with the following keys/values:
// * element - tag name of the SVG element to create
// * attr - Object with attributes key-values to assign to the new element
// * curStyles - Boolean indicating that current style attributes should be applied first
2016-11-14 08:54:53 +00:00
// * children - Optional array with data objects to be added recursively as children
2010-11-15 09:25:49 +00:00
//
// Returns: The new element
2018-05-16 08:32:44 +00:00
var addSvgElementFromJson = this . addSvgElementFromJson = function ( data ) {
if ( typeof data === 'string' ) return svgdoc . createTextNode ( data ) ;
2016-08-08 14:32:59 +00:00
2011-02-10 04:10:03 +00:00
var shape = svgedit . utilities . getElem ( data . attr . id ) ;
2010-11-15 09:25:49 +00:00
// if shape is a path but we need to create a rect/ellipse, then remove the path
2018-05-16 08:32:44 +00:00
var currentLayer = getCurrentDrawing ( ) . getCurrentLayer ( ) ;
if ( shape && data . element !== shape . tagName ) {
currentLayer . removeChild ( shape ) ;
2010-11-15 09:25:49 +00:00
shape = null ;
}
if ( ! shape ) {
2013-02-16 15:02:26 +00:00
shape = svgdoc . createElementNS ( NS . SVG , data . element ) ;
2018-05-16 08:32:44 +00:00
if ( currentLayer ) {
( currentGroup || currentLayer ) . appendChild ( shape ) ;
2010-11-15 09:25:49 +00:00
}
}
2013-02-15 15:51:58 +00:00
if ( data . curStyles ) {
2011-02-10 04:10:03 +00:00
svgedit . utilities . assignAttributes ( shape , {
2018-05-16 08:32:44 +00:00
'fill' : curShape . fill ,
'stroke' : curShape . stroke ,
'stroke-width' : curShape . stroke _width ,
'stroke-dasharray' : curShape . stroke _dasharray ,
'stroke-linejoin' : curShape . stroke _linejoin ,
'stroke-linecap' : curShape . stroke _linecap ,
'stroke-opacity' : curShape . stroke _opacity ,
'fill-opacity' : curShape . fill _opacity ,
'opacity' : curShape . opacity / 2 ,
2014-04-08 03:21:22 +00:00
'style' : 'pointer-events:inherit'
2010-11-15 09:25:49 +00:00
} , 100 ) ;
}
2011-02-10 04:10:03 +00:00
svgedit . utilities . assignAttributes ( shape , data . attr , 100 ) ;
svgedit . utilities . cleanupElement ( shape ) ;
2016-08-05 12:29:58 +00:00
// Children
2016-11-15 08:27:31 +00:00
if ( data . children ) {
2018-05-16 08:32:44 +00:00
data . children . forEach ( function ( child ) {
2016-11-14 09:08:06 +00:00
shape . appendChild ( addSvgElementFromJson ( child ) ) ;
} ) ;
}
2016-08-05 12:29:58 +00:00
2010-11-15 09:25:49 +00:00
return shape ;
} ;
2010-11-05 17:00:32 +00:00
// import svgtransformlist.js
2018-05-16 08:32:44 +00:00
/* var getTransformList = */ canvas . getTransformList = svgedit . transformlist . getTransformList ;
2010-11-05 17:00:32 +00:00
// import from math.js.
var transformPoint = svgedit . math . transformPoint ;
2010-11-12 19:08:29 +00:00
var matrixMultiply = canvas . matrixMultiply = svgedit . math . matrixMultiply ;
var hasMatrixTransform = canvas . hasMatrixTransform = svgedit . math . hasMatrixTransform ;
var transformListToTransform = canvas . transformListToTransform = svgedit . math . transformListToTransform ;
2018-05-16 08:32:44 +00:00
// var snapToAngle = svgedit.math.snapToAngle;
// var getMatrix = svgedit.math.getMatrix;
2010-11-05 17:00:32 +00:00
2010-11-07 16:46:57 +00:00
// initialize from units.js
2010-11-07 05:20:03 +00:00
// send in an object implementing the ElementContainer interface (see units.js)
svgedit . units . init ( {
2018-05-16 08:32:44 +00:00
getBaseUnit : function ( ) { return curConfig . baseUnit ; } ,
2011-02-10 04:10:03 +00:00
getElement : svgedit . utilities . getElem ,
2018-05-16 08:32:44 +00:00
getHeight : function ( ) { return svgcontent . getAttribute ( 'height' ) / currentZoom ; } ,
getWidth : function ( ) { return svgcontent . getAttribute ( 'width' ) / currentZoom ; } ,
getRoundDigits : function ( ) { return saveOptions . round _digits ; }
2010-11-07 05:20:03 +00:00
} ) ;
2010-11-07 16:46:57 +00:00
// import from units.js
2018-05-16 08:32:44 +00:00
/* var convertToNum = */ canvas . convertToNum = svgedit . units . convertToNum ;
2010-11-06 00:45:21 +00:00
2010-11-05 17:00:32 +00:00
// import from svgutils.js
2010-12-02 17:14:24 +00:00
svgedit . utilities . init ( {
2018-05-16 08:32:44 +00:00
getDOMDocument : function ( ) { return svgdoc ; } ,
getDOMContainer : function ( ) { return container ; } ,
getSVGRoot : function ( ) { return svgroot ; } ,
2011-02-04 08:02:46 +00:00
// TODO: replace this mostly with a way to get the current drawing.
2018-05-16 08:32:44 +00:00
getSelectedElements : function ( ) { return selectedElements ; } ,
getSVGContent : function ( ) { return svgcontent ; } ,
getBaseUnit : function ( ) { return curConfig . baseUnit ; } ,
getSnappingStep : function ( ) { return curConfig . snappingStep ; }
2010-12-02 17:14:24 +00:00
} ) ;
2013-02-14 15:19:46 +00:00
var findDefs = canvas . findDefs = svgedit . utilities . findDefs ;
2010-11-12 19:08:29 +00:00
var getUrlFromAttr = canvas . getUrlFromAttr = svgedit . utilities . getUrlFromAttr ;
var getHref = canvas . getHref = svgedit . utilities . getHref ;
var setHref = canvas . setHref = svgedit . utilities . setHref ;
2010-11-05 17:00:32 +00:00
var getPathBBox = svgedit . utilities . getPathBBox ;
2018-05-16 08:32:44 +00:00
/* var getBBox = */ canvas . getBBox = svgedit . utilities . getBBox ;
/* var getRotationAngle = */ canvas . getRotationAngle = svgedit . utilities . getRotationAngle ;
2011-02-04 08:02:46 +00:00
var getElem = canvas . getElem = svgedit . utilities . getElem ;
2018-05-16 08:32:44 +00:00
/* var getRefElem = */ canvas . getRefElem = svgedit . utilities . getRefElem ;
2011-02-04 08:02:46 +00:00
var assignAttributes = canvas . assignAttributes = svgedit . utilities . assignAttributes ;
2011-02-10 04:10:03 +00:00
var cleanupElement = this . cleanupElement = svgedit . utilities . cleanupElement ;
2010-11-05 17:00:32 +00:00
2013-02-17 04:58:04 +00:00
// import from coords.js
svgedit . coords . init ( {
2018-05-16 08:32:44 +00:00
getDrawing : function ( ) { return getCurrentDrawing ( ) ; } ,
getGridSnapping : function ( ) { return curConfig . gridSnapping ; }
2013-02-17 04:58:04 +00:00
} ) ;
var remapElement = this . remapElement = svgedit . coords . remapElement ;
2013-02-20 06:29:25 +00:00
// import from recalculate.js
svgedit . recalculate . init ( {
2018-05-16 08:32:44 +00:00
getSVGRoot : function ( ) { return svgroot ; } ,
getStartTransform : function ( ) { return startTransform ; } ,
setStartTransform : function ( transform ) { startTransform = transform ; }
2013-02-20 06:29:25 +00:00
} ) ;
var recalculateDimensions = this . recalculateDimensions = svgedit . recalculate . recalculateDimensions ;
2010-11-07 20:14:05 +00:00
// import from sanitize.js
2013-02-16 15:02:26 +00:00
var nsMap = svgedit . getReverseNS ( ) ;
2010-11-12 19:08:29 +00:00
var sanitizeSvg = canvas . sanitizeSvg = svgedit . sanitize . sanitizeSvg ;
// import from history.js
var MoveElementCommand = svgedit . history . MoveElementCommand ;
var InsertElementCommand = svgedit . history . InsertElementCommand ;
var RemoveElementCommand = svgedit . history . RemoveElementCommand ;
var ChangeElementCommand = svgedit . history . ChangeElementCommand ;
var BatchCommand = svgedit . history . BatchCommand ;
2014-06-01 21:29:54 +00:00
var call ;
2010-11-12 19:08:29 +00:00
// Implement the svgedit.history.HistoryEventHandler interface.
canvas . undoMgr = new svgedit . history . UndoManager ( {
2018-05-16 08:32:44 +00:00
handleHistoryEvent : function ( eventType , cmd ) {
2010-11-12 19:08:29 +00:00
var EventTypes = svgedit . history . HistoryEventTypes ;
// TODO: handle setBlurOffsets.
2018-05-16 08:32:44 +00:00
if ( eventType === EventTypes . BEFORE _UNAPPLY || eventType === EventTypes . BEFORE _APPLY ) {
2010-11-12 19:08:29 +00:00
canvas . clearSelection ( ) ;
2018-05-16 08:32:44 +00:00
} else if ( eventType === EventTypes . AFTER _APPLY || eventType === EventTypes . AFTER _UNAPPLY ) {
2010-11-12 19:08:29 +00:00
var elems = cmd . elements ( ) ;
canvas . pathActions . clear ( ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , elems ) ;
2010-11-12 19:08:29 +00:00
var cmdType = cmd . type ( ) ;
2018-05-16 08:32:44 +00:00
var isApply = ( eventType === EventTypes . AFTER _APPLY ) ;
if ( cmdType === MoveElementCommand . type ( ) ) {
2010-11-12 19:08:29 +00:00
var parent = isApply ? cmd . newParent : cmd . oldParent ;
2018-05-16 08:32:44 +00:00
if ( parent === svgcontent ) {
2010-11-12 19:08:29 +00:00
canvas . identifyLayers ( ) ;
}
2018-05-16 08:32:44 +00:00
} else if ( cmdType === InsertElementCommand . type ( ) ||
cmdType === RemoveElementCommand . type ( ) ) {
if ( cmd . parent === svgcontent ) {
2010-11-12 19:08:29 +00:00
canvas . identifyLayers ( ) ;
}
2018-05-16 08:32:44 +00:00
if ( cmdType === InsertElementCommand . type ( ) ) {
if ( isApply ) { restoreRefElems ( cmd . elem ) ; }
2010-11-12 19:08:29 +00:00
} else {
2018-05-16 08:32:44 +00:00
if ( ! isApply ) { restoreRefElems ( cmd . elem ) ; }
2010-11-12 19:08:29 +00:00
}
2013-02-15 15:51:58 +00:00
if ( cmd . elem . tagName === 'use' ) {
2011-02-02 18:24:44 +00:00
setUseData ( cmd . elem ) ;
}
2018-05-16 08:32:44 +00:00
} else if ( cmdType === ChangeElementCommand . type ( ) ) {
2010-11-12 19:08:29 +00:00
// if we are changing layer names, re-identify all layers
2018-05-16 08:32:44 +00:00
if ( cmd . elem . tagName === 'title' &&
cmd . elem . parentNode . parentNode === svgcontent
) {
2010-11-12 19:08:29 +00:00
canvas . identifyLayers ( ) ;
}
var values = isApply ? cmd . newValues : cmd . oldValues ;
// If stdDeviation was changed, update the blur.
2013-02-15 15:51:58 +00:00
if ( values . stdDeviation ) {
canvas . setBlurOffsets ( cmd . elem . parentNode , values . stdDeviation ) ;
2010-11-12 19:08:29 +00:00
}
2012-11-28 05:39:48 +00:00
// This is resolved in later versions of webkit, perhaps we should
// have a featured detection for correct 'use' behavior?
// ——————————
2018-05-16 00:53:27 +00:00
// Remove & Re-add hack for Webkit (issue 775)
2018-05-16 08:32:44 +00:00
// if (cmd.elem.tagName === 'use' && svgedit.browser.isWebkit()) {
2012-11-28 05:39:48 +00:00
// var elem = cmd.elem;
2013-02-15 15:51:58 +00:00
// if (!elem.getAttribute('x') && !elem.getAttribute('y')) {
2012-11-28 05:39:48 +00:00
// var parent = elem.parentNode;
// var sib = elem.nextSibling;
// parent.removeChild(elem);
// parent.insertBefore(elem, sib);
// }
2018-05-16 08:32:44 +00:00
// }
2010-11-12 19:08:29 +00:00
}
}
}
} ) ;
2018-05-16 08:32:44 +00:00
var addCommandToHistory = function ( cmd ) {
2010-11-12 19:08:29 +00:00
canvas . undoMgr . addCommandToHistory ( cmd ) ;
} ;
2016-05-04 13:38:29 +00:00
/ * *
* Get a HistoryRecordingService .
* @ param { svgedit . history . HistoryRecordingService = } hrService - if exists , return it instead of creating a new service .
* @ returns { svgedit . history . HistoryRecordingService }
* /
2018-05-16 08:32:44 +00:00
function historyRecordingService ( hrService ) {
return hrService || new svgedit . history . HistoryRecordingService ( canvas . undoMgr ) ;
2016-05-04 13:38:29 +00:00
}
2010-11-15 09:25:49 +00:00
// import from select.js
svgedit . select . init ( curConfig , {
2018-05-16 08:32:44 +00:00
createSVGElement : function ( jsonMap ) { return canvas . addSvgElementFromJson ( jsonMap ) ; } ,
svgRoot : function ( ) { return svgroot ; } ,
svgContent : function ( ) { return svgcontent ; } ,
currentZoom : function ( ) { return currentZoom ; } ,
2010-11-15 09:25:49 +00:00
// TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js.
2018-05-16 08:32:44 +00:00
getStrokedBBox : function ( elems ) { return canvas . getStrokedBBox ( [ elems ] ) ; }
2010-11-15 09:25:49 +00:00
} ) ;
// this object manages selectors for us
var selectorManager = this . selectorManager = svgedit . select . getSelectorManager ( ) ;
2010-11-07 20:14:05 +00:00
2011-02-04 08:02:46 +00:00
// Import from path.js
svgedit . path . init ( {
2018-05-16 08:32:44 +00:00
getCurrentZoom : function ( ) { return currentZoom ; } ,
getSVGRoot : function ( ) { return svgroot ; }
2011-02-04 08:02:46 +00:00
} ) ;
2010-11-15 09:24:02 +00:00
// Interface strings, usually for title elements
var uiStrings = {
2018-05-16 08:32:44 +00:00
exportNoBlur : 'Blurred elements will appear as un-blurred' ,
exportNoforeignObject : 'foreignObject elements will not appear' ,
exportNoDashArray : 'Strokes will appear filled' ,
exportNoText : 'Text may not appear as expected'
2010-11-15 09:24:02 +00:00
} ;
2010-11-06 00:45:21 +00:00
var visElems = 'a,circle,ellipse,foreignObject,g,image,line,path,polygon,polyline,rect,svg,text,tspan,use' ;
2018-05-16 08:32:44 +00:00
var refAttrs = [ 'clip-path' , 'fill' , 'filter' , 'marker-end' , 'marker-mid' , 'marker-start' , 'mask' , 'stroke' ] ;
2010-06-30 18:27:36 +00:00
2010-09-10 18:51:59 +00:00
var elData = $ . data ;
2010-11-12 19:08:29 +00:00
2010-06-21 15:05:41 +00:00
// Animation element to change the opacity of any newly created element
2018-05-16 08:32:44 +00:00
var opacAni = document . createElementNS ( NS . SVG , 'animate' ) ;
$ ( opacAni ) . attr ( {
2010-06-21 15:05:41 +00:00
attributeName : 'opacity' ,
begin : 'indefinite' ,
dur : 1 ,
fill : 'freeze'
} ) . appendTo ( svgroot ) ;
2018-05-16 08:32:44 +00:00
var restoreRefElems = function ( elem ) {
2010-10-08 16:34:24 +00:00
// Look for missing reference elements, restore any found
2014-06-01 21:29:54 +00:00
var o , i , l ,
2018-05-16 08:32:44 +00:00
attrs = $ ( elem ) . attr ( refAttrs ) ;
2014-01-31 00:39:35 +00:00
for ( o in attrs ) {
2010-10-08 16:34:24 +00:00
var val = attrs [ o ] ;
if ( val && val . indexOf ( 'url(' ) === 0 ) {
2013-02-14 15:19:46 +00:00
var id = svgedit . utilities . getUrlFromAttr ( val ) . substr ( 1 ) ;
2010-10-08 16:34:24 +00:00
var ref = getElem ( id ) ;
2013-02-15 15:51:58 +00:00
if ( ! ref ) {
2013-02-14 15:19:46 +00:00
svgedit . utilities . findDefs ( ) . appendChild ( removedElements [ id ] ) ;
2010-10-08 16:34:24 +00:00
delete removedElements [ id ] ;
}
}
}
2018-05-16 00:53:27 +00:00
2011-02-02 19:36:49 +00:00
var childs = elem . getElementsByTagName ( '*' ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( childs . length ) {
2014-01-31 00:39:35 +00:00
for ( i = 0 , l = childs . length ; i < l ; i ++ ) {
2011-02-02 19:36:49 +00:00
restoreRefElems ( childs [ i ] ) ;
}
}
2010-11-12 19:08:29 +00:00
} ;
2010-06-21 18:06:21 +00:00
2018-05-16 08:32:44 +00:00
( function ( ) {
2010-06-22 14:52:51 +00:00
// TODO For Issue 208: this is a start on a thumbnail
2014-04-08 03:21:22 +00:00
// var svgthumb = svgdoc.createElementNS(NS.SVG, 'use');
2010-06-22 14:52:51 +00:00
// svgthumb.setAttribute('width', '100');
// svgthumb.setAttribute('height', '100');
2010-11-07 16:46:57 +00:00
// svgedit.utilities.setHref(svgthumb, '#svgcontent');
2010-06-22 14:52:51 +00:00
// svgroot.appendChild(svgthumb);
2014-01-31 10:40:52 +00:00
} ( ) ) ;
2010-06-22 14:52:51 +00:00
2011-01-16 16:28:26 +00:00
// Object to contain image data for raster images that were found encodable
var encodableImages = { } ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// String with image URL of last loadable image
2018-05-16 08:32:44 +00:00
lastGoodImgUrl = curConfig . imgPath + 'logo.png' ,
2018-05-16 00:53:27 +00:00
2010-09-22 18:42:24 +00:00
// Array with current disabled elements (for in-group editing)
2018-05-16 08:32:44 +00:00
disabledElems = [ ] ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Object with save options
2018-05-16 08:32:44 +00:00
saveOptions = { round _digits : 5 } ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Boolean indicating whether or not a draw action has been started
started = false ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// String with an element's initial transform attribute value
2013-02-20 06:29:25 +00:00
startTransform = null ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// String indicating the current editor mode
2018-05-16 08:32:44 +00:00
currentMode = 'select' ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// String with the current direction in which an element is being resized
2018-05-16 08:32:44 +00:00
currentResizeMode = 'none' ,
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
// Object with IDs for imported files, to see if one was already added
2018-05-16 08:32:44 +00:00
importIds = { } ,
2009-08-17 06:56:55 +00:00
2013-02-15 15:51:58 +00:00
// Current text style properties
2018-05-16 08:32:44 +00:00
curText = allProperties . text ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Current general properties
2018-05-16 08:32:44 +00:00
curProperties = curShape ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Array with selected elements' Bounding box object
2011-02-24 16:13:26 +00:00
// selectedBBoxes = new Array(1),
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// The DOM element that was just selected
justSelected = null ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// DOM element for selection rectangle drawn by the user
rubberBox = null ,
2018-05-16 00:53:27 +00:00
2015-12-01 03:08:13 +00:00
// Array of current BBoxes, used in getIntersectionList().
2010-06-22 14:52:51 +00:00
curBBoxes = [ ] ,
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Object to contain all included extensions
2010-08-17 18:09:50 +00:00
extensions = { } ,
2018-05-16 00:53:27 +00:00
2010-08-17 18:09:50 +00:00
// Canvas point for the most recent right click
2010-10-08 16:34:24 +00:00
lastClickPoint = null ,
2018-05-16 00:53:27 +00:00
2010-10-08 16:34:24 +00:00
// Map of deleted reference elements
2013-02-15 15:51:58 +00:00
removedElements = { } ;
2010-08-17 18:09:50 +00:00
2010-06-22 14:52:51 +00:00
// Should this return an array by default, so extension results aren't overwritten?
2018-05-16 08:32:44 +00:00
var runExtensions = this . runExtensions = function ( action , vars , returnArray ) {
2013-02-15 16:51:48 +00:00
var result = returnArray ? [ ] : false ;
2018-05-16 08:32:44 +00:00
$ . each ( extensions , function ( name , opts ) {
2014-02-18 15:06:27 +00:00
if ( opts && action in opts ) {
2013-02-15 15:51:58 +00:00
if ( returnArray ) {
result . push ( opts [ action ] ( vars ) ) ;
2010-06-22 14:52:51 +00:00
} else {
result = opts [ action ] ( vars ) ;
}
}
2009-08-31 14:54:59 +00:00
} ) ;
2010-06-22 14:52:51 +00:00
return result ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-30 18:27:36 +00:00
// Function: addExtension
// Add an extension to the editor
2018-05-16 00:53:27 +00:00
//
2010-06-30 18:27:36 +00:00
// Parameters:
// name - String with the ID of the extension
2018-05-16 08:32:44 +00:00
// extFunc - Function supplied by the extension with its data
this . addExtension = function ( name , extFunc ) {
2014-01-31 10:40:52 +00:00
var ext ;
2013-02-15 15:51:58 +00:00
if ( ! ( name in extensions ) ) {
2010-06-30 18:27:36 +00:00
// Provide private vars/funcs here. Is there a better way to do this?
2018-05-16 08:32:44 +00:00
if ( $ . isFunction ( extFunc ) ) {
ext = extFunc ( $ . extend ( canvas . getPrivateMethods ( ) , {
2013-02-15 15:51:58 +00:00
svgroot : svgroot ,
svgcontent : svgcontent ,
nonce : getCurrentDrawing ( ) . getNonce ( ) ,
selectorManager : selectorManager
} ) ) ;
2010-07-01 20:14:12 +00:00
} else {
2018-05-16 08:32:44 +00:00
ext = extFunc ;
2010-07-01 20:14:12 +00:00
}
2010-06-30 18:27:36 +00:00
extensions [ name ] = ext ;
2014-04-08 03:21:22 +00:00
call ( 'extension_added' , ext ) ;
2010-06-30 18:27:36 +00:00
} else {
2014-04-08 03:21:22 +00:00
console . log ( 'Cannot add extension "' + name + '", an extension by that name already exists.' ) ;
2010-06-30 18:27:36 +00:00
}
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// This method rounds the incoming value to the nearest value based on the currentZoom
var round = this . round = function ( val ) {
return parseInt ( val * currentZoom , 10 ) / currentZoom ;
2010-06-22 14:52:51 +00:00
} ;
2009-08-31 14:54:59 +00:00
2010-06-22 14:52:51 +00:00
// This method sends back an array or a NodeList full of elements that
2018-05-16 08:32:44 +00:00
// intersect the multi-select rubber-band-box on the currentLayer only.
2018-05-16 00:53:27 +00:00
//
2015-12-01 03:08:13 +00:00
// We brute-force getIntersectionList for browsers that do not support it (Firefox).
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// Reference:
// Firefox does not implement getIntersectionList(), see https://bugzilla.mozilla.org/show_bug.cgi?id=501421
2018-05-16 08:32:44 +00:00
var getIntersectionList = this . getIntersectionList = function ( rect ) {
2010-06-22 14:52:51 +00:00
if ( rubberBox == null ) { return null ; }
2018-05-16 08:32:44 +00:00
var parent = currentGroup || getCurrentDrawing ( ) . getCurrentLayer ( ) ;
2015-12-01 03:08:13 +00:00
var rubberBBox ;
if ( ! rect ) {
rubberBBox = rubberBox . getBBox ( ) ;
2016-02-10 08:38:43 +00:00
var o , bb = svgcontent . createSVGRect ( ) ;
2016-01-16 18:40:29 +00:00
for ( o in rubberBBox ) {
2018-05-16 08:32:44 +00:00
bb [ o ] = rubberBBox [ o ] / currentZoom ;
2018-05-16 00:53:27 +00:00
}
2016-01-16 18:40:29 +00:00
rubberBBox = bb ;
2015-12-01 03:08:13 +00:00
} else {
2016-09-14 17:44:09 +00:00
rubberBBox = svgcontent . createSVGRect ( ) ;
rubberBBox . x = rect . x ;
rubberBBox . y = rect . y ;
rubberBBox . width = rect . width ;
rubberBBox . height = rect . height ;
2010-01-25 21:22:03 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var resultList = null ;
2016-01-16 18:42:15 +00:00
if ( ! svgedit . browser . isIE ) {
2018-05-16 08:32:44 +00:00
if ( typeof svgroot . getIntersectionList === 'function' ) {
2016-01-16 18:42:15 +00:00
// Offset the bbox of the rubber box by the offset of the svgcontent element.
rubberBBox . x += parseInt ( svgcontent . getAttribute ( 'x' ) , 10 ) ;
rubberBBox . y += parseInt ( svgcontent . getAttribute ( 'y' ) , 10 ) ;
2015-12-01 03:08:13 +00:00
2016-01-16 18:42:15 +00:00
resultList = svgroot . getIntersectionList ( rubberBBox , parent ) ;
}
2015-12-01 03:08:13 +00:00
}
2009-09-17 14:47:54 +00:00
2018-05-16 08:32:44 +00:00
if ( resultList == null || typeof resultList . item !== 'function' ) {
2010-06-22 14:52:51 +00:00
resultList = [ ] ;
2015-12-01 03:08:13 +00:00
if ( ! curBBoxes . length ) {
// Cache all bboxes
curBBoxes = getVisibleElementsAndBBoxes ( parent ) ;
2010-08-20 14:52:55 +00:00
}
2010-06-22 14:52:51 +00:00
var i = curBBoxes . length ;
while ( i -- ) {
2018-05-16 08:32:44 +00:00
if ( ! rubberBBox . width ) { continue ; }
2014-01-31 10:40:52 +00:00
if ( svgedit . math . rectsIntersect ( rubberBBox , curBBoxes [ i ] . bbox ) ) {
2010-06-22 14:52:51 +00:00
resultList . push ( curBBoxes [ i ] . elem ) ;
}
2009-07-01 15:34:15 +00:00
}
2010-06-22 14:52:51 +00:00
}
2015-12-01 03:08:13 +00:00
2018-05-16 00:53:27 +00:00
// addToSelection expects an array, but it's ok to pass a NodeList
// because using square-bracket notation is allowed:
2010-06-22 14:52:51 +00:00
// http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html
return resultList ;
} ;
2009-08-26 19:59:52 +00:00
2010-11-05 15:29:30 +00:00
// TODO(codedread): Migrate this into svgutils.js
2010-06-30 18:27:36 +00:00
// Function: getStrokedBBox
// Get the bounding box for one or more stroked and/or transformed elements
2018-05-16 00:53:27 +00:00
//
2010-06-30 18:27:36 +00:00
// Parameters:
// elems - Array with DOM elements to check
2018-05-16 00:53:27 +00:00
//
2010-06-30 18:27:36 +00:00
// Returns:
// A single bounding box object
2018-05-16 08:32:44 +00:00
var getStrokedBBox = this . getStrokedBBox = function ( elems ) {
if ( ! elems ) { elems = getVisibleElements ( ) ; }
return svgedit . utilities . getStrokedBBox ( elems , addSvgElementFromJson , pathActions ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-30 18:27:36 +00:00
// Function: getVisibleElements
// Get all elements that have a BBox (excludes <defs>, <title>, etc).
// Note that 0-opacity, off-screen etc elements are still considered "visible"
// for this function
//
// Parameters:
// parent - The parent DOM element to search within
//
// Returns:
2010-11-14 19:01:00 +00:00
// An array with all "visible" elements.
2018-05-16 08:32:44 +00:00
var getVisibleElements = this . getVisibleElements = function ( parent ) {
2014-01-31 00:39:35 +00:00
if ( ! parent ) {
parent = $ ( svgcontent ) . children ( ) ; // Prevent layers from being included
}
2018-05-16 00:53:27 +00:00
2010-11-14 19:01:00 +00:00
var contentElems = [ ] ;
2018-05-16 08:32:44 +00:00
$ ( parent ) . children ( ) . each ( function ( i , elem ) {
2015-12-01 03:08:13 +00:00
if ( elem . getBBox ) {
contentElems . push ( elem ) ;
}
2010-11-14 19:01:00 +00:00
} ) ;
return contentElems . reverse ( ) ;
} ;
// Function: getVisibleElementsAndBBoxes
// Get all elements that have a BBox (excludes <defs>, <title>, etc).
// Note that 0-opacity, off-screen etc elements are still considered "visible"
// for this function
//
// Parameters:
// parent - The parent DOM element to search within
//
// Returns:
// An array with objects that include:
2010-06-30 18:27:36 +00:00
// * elem - The element
// * bbox - The element's BBox as retrieved from getStrokedBBox
2018-05-16 08:32:44 +00:00
var getVisibleElementsAndBBoxes = this . getVisibleElementsAndBBoxes = function ( parent ) {
2014-01-31 00:39:35 +00:00
if ( ! parent ) {
parent = $ ( svgcontent ) . children ( ) ; // Prevent layers from being included
}
2010-06-30 18:27:36 +00:00
var contentElems = [ ] ;
2018-05-16 08:32:44 +00:00
$ ( parent ) . children ( ) . each ( function ( i , elem ) {
2015-12-01 03:08:13 +00:00
if ( elem . getBBox ) {
2018-05-16 08:32:44 +00:00
contentElems . push ( { elem : elem , bbox : getStrokedBBox ( [ elem ] ) } ) ;
2015-12-01 03:08:13 +00:00
}
2010-06-30 18:27:36 +00:00
} ) ;
return contentElems . reverse ( ) ;
2010-11-14 19:01:00 +00:00
} ;
2010-06-30 18:27:36 +00:00
2010-07-16 15:46:54 +00:00
// Function: groupSvgElem
// Wrap an SVG element into a group element, mark the group as 'gsvg'
//
// Parameters:
// elem - SVG element to wrap
2018-05-16 08:32:44 +00:00
var groupSvgElem = this . groupSvgElem = function ( elem ) {
2014-04-08 03:21:22 +00:00
var g = document . createElementNS ( NS . SVG , 'g' ) ;
2010-07-16 15:46:54 +00:00
elem . parentNode . replaceChild ( g , elem ) ;
$ ( g ) . append ( elem ) . data ( 'gsvg' , elem ) [ 0 ] . id = getNextId ( ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-07-16 15:46:54 +00:00
2010-06-30 18:27:36 +00:00
// Set scope for these functions
2014-06-01 21:29:54 +00:00
var getId , getNextId ;
2014-02-11 13:32:40 +00:00
var textActions , pathActions ;
2010-06-30 18:27:36 +00:00
2018-05-16 08:32:44 +00:00
( function ( c ) {
2010-06-30 18:27:36 +00:00
// Object to contain editor event names and callback functions
var events = { } ;
2018-05-16 08:32:44 +00:00
getId = c . getId = function ( ) { return getCurrentDrawing ( ) . getId ( ) ; } ;
getNextId = c . getNextId = function ( ) { return getCurrentDrawing ( ) . getNextId ( ) ; } ;
2013-02-15 23:05:23 +00:00
2010-09-23 19:53:43 +00:00
// Function: call
2010-06-30 18:27:36 +00:00
// Run the callback function associated with the given event
//
// Parameters:
// event - String with the event name
// arg - Argument to pass through to the callback function
2018-05-16 08:32:44 +00:00
call = c . call = function ( event , arg ) {
2010-06-30 18:27:36 +00:00
if ( events [ event ] ) {
2010-11-12 19:08:29 +00:00
return events [ event ] ( this , arg ) ;
2010-06-30 18:27:36 +00:00
}
} ;
2013-02-15 23:05:23 +00:00
2010-06-30 18:27:36 +00:00
// Function: bind
// Attaches a callback function to an event
//
// Parameters:
// event - String indicating the name of the event
// f - The callback function to bind to the event
2013-02-15 23:05:23 +00:00
//
2010-06-30 18:27:36 +00:00
// Return:
// The previous event
2018-05-16 08:32:44 +00:00
c . bind = function ( event , f ) {
2013-02-15 15:51:58 +00:00
var old = events [ event ] ;
2010-06-30 18:27:36 +00:00
events [ event ] = f ;
return old ;
} ;
} ( canvas ) ) ;
2010-11-07 20:14:05 +00:00
// Function: canvas.prepareSvg
// Runs the SVG Document through the sanitizer and then updates its paths.
2010-06-22 14:52:51 +00:00
//
// Parameters:
2010-11-07 20:14:05 +00:00
// newDoc - The SVG DOM document
2018-05-16 08:32:44 +00:00
this . prepareSvg = function ( newDoc ) {
2010-11-07 20:14:05 +00:00
this . sanitizeSvg ( newDoc . documentElement ) ;
2010-04-28 16:53:36 +00:00
2010-11-07 20:14:05 +00:00
// convert paths into absolute commands
2014-02-11 13:32:40 +00:00
var i , path , len ,
2014-04-08 03:21:22 +00:00
paths = newDoc . getElementsByTagNameNS ( NS . SVG , 'path' ) ;
2014-01-31 00:39:35 +00:00
for ( i = 0 , len = paths . length ; i < len ; ++ i ) {
path = paths [ i ] ;
2010-11-07 20:14:05 +00:00
path . setAttribute ( 'd' , pathActions . convertPath ( path ) ) ;
pathActions . fixEnd ( path ) ;
2010-06-22 14:52:51 +00:00
}
} ;
2010-04-07 18:18:28 +00:00
2010-06-29 20:43:44 +00:00
// Function: ffClone
2018-05-16 00:53:27 +00:00
// Hack for Firefox bugs where text element features aren't updated or get
2011-01-31 20:02:24 +00:00
// messed up. See issue 136 and issue 137.
2018-05-16 00:53:27 +00:00
// This function clones the element and re-selects it
// TODO: Test for this bug on load and add it to "support" object instead of
2010-06-29 20:43:44 +00:00
// browser sniffing
//
2018-05-16 00:53:27 +00:00
// Parameters:
2010-06-29 20:43:44 +00:00
// elem - The (text) DOM element to clone
2018-05-16 08:32:44 +00:00
var ffClone = function ( elem ) {
if ( ! svgedit . browser . isGecko ( ) ) { return elem ; }
2013-02-15 15:51:58 +00:00
var clone = elem . cloneNode ( true ) ;
2010-06-29 20:43:44 +00:00
elem . parentNode . insertBefore ( clone , elem ) ;
elem . parentNode . removeChild ( elem ) ;
selectorManager . releaseSelector ( elem ) ;
selectedElements [ 0 ] = clone ;
selectorManager . requestSelector ( clone ) . showGrips ( true ) ;
return clone ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-29 20:43:44 +00:00
// this.each is deprecated, if any extension used this it can be recreated by doing this:
// $(canvas.getRootElem()).children().each(...)
2018-05-16 08:32:44 +00:00
// this.each = function (cb) {
2014-01-31 00:39:35 +00:00
// $(svgroot).children().each(cb);
2010-06-29 20:43:44 +00:00
// };
// Function: setRotationAngle
// Removes any old rotations if present, prepends a new rotation at the
// transformed center
//
// Parameters:
// val - The new rotation angle in degrees
// preventUndo - Boolean indicating whether the action should be undoable or not
2018-05-16 08:32:44 +00:00
this . setRotationAngle = function ( val , preventUndo ) {
2010-06-29 20:43:44 +00:00
// ensure val is the proper type
val = parseFloat ( val ) ;
var elem = selectedElements [ 0 ] ;
2014-04-08 03:21:22 +00:00
var oldTransform = elem . getAttribute ( 'transform' ) ;
2011-02-24 16:13:26 +00:00
var bbox = svgedit . utilities . getBBox ( elem ) ;
2018-05-16 08:32:44 +00:00
var cx = bbox . x + bbox . width / 2 , cy = bbox . y + bbox . height / 2 ;
2013-02-14 15:19:46 +00:00
var tlist = svgedit . transformlist . getTransformList ( elem ) ;
2018-05-16 00:53:27 +00:00
2010-06-29 20:43:44 +00:00
// only remove the real rotational transform if present (i.e. at index=0)
if ( tlist . numberOfItems > 0 ) {
var xform = tlist . getItem ( 0 ) ;
2018-05-16 08:32:44 +00:00
if ( xform . type === 4 ) {
2010-06-29 20:43:44 +00:00
tlist . removeItem ( 0 ) ;
}
}
2018-05-16 08:32:44 +00:00
// find Rnc and insert it
if ( val !== 0 ) {
2013-02-15 16:51:48 +00:00
var center = svgedit . math . transformPoint ( cx , cy , svgedit . math . transformListToTransform ( tlist ) . matrix ) ;
2018-05-16 08:32:44 +00:00
var Rnc = svgroot . createSVGTransform ( ) ;
Rnc . setRotate ( val , center . x , center . y ) ;
2013-02-15 15:51:58 +00:00
if ( tlist . numberOfItems ) {
2018-05-16 08:32:44 +00:00
tlist . insertItemBefore ( Rnc , 0 ) ;
2010-08-30 17:17:57 +00:00
} else {
2018-05-16 08:32:44 +00:00
tlist . appendItem ( Rnc ) ;
2010-08-30 17:17:57 +00:00
}
2018-05-16 08:32:44 +00:00
} else if ( tlist . numberOfItems === 0 ) {
2014-04-08 03:21:22 +00:00
elem . removeAttribute ( 'transform' ) ;
2010-06-29 20:43:44 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-29 20:43:44 +00:00
if ( ! preventUndo ) {
// we need to undo it, then redo it so it can be undo-able! :)
// TODO: figure out how to make changes to transform list undo-able cross-browser?
2014-04-08 03:21:22 +00:00
var newTransform = elem . getAttribute ( 'transform' ) ;
elem . setAttribute ( 'transform' , oldTransform ) ;
changeSelectedAttribute ( 'transform' , newTransform , selectedElements ) ;
call ( 'changed' , selectedElements ) ;
}
2018-05-16 08:32:44 +00:00
// var pointGripContainer = svgedit.utilities.getElem('pathpointgrip_container');
// if (elem.nodeName === 'path' && pointGripContainer) {
// pathActions.setPointContainerTransform(elem.getAttribute('transform'));
// }
2010-06-29 20:43:44 +00:00
var selector = selectorManager . requestSelector ( selectedElements [ 0 ] ) ;
selector . resize ( ) ;
selector . updateGripCursors ( val ) ;
} ;
2010-06-22 18:17:42 +00:00
// Function: recalculateAllSelectedDimensions
2018-05-16 00:53:27 +00:00
// Runs recalculateDimensions on the selected elements,
2010-06-22 18:17:42 +00:00
// adding the changes to a single batch command
2018-05-16 08:32:44 +00:00
var recalculateAllSelectedDimensions = this . recalculateAllSelectedDimensions = function ( ) {
var text = ( currentResizeMode === 'none' ? 'position' : 'size' ) ;
2013-02-17 08:21:07 +00:00
var batchCmd = new svgedit . history . BatchCommand ( text ) ;
2010-06-22 14:52:51 +00:00
var i = selectedElements . length ;
2013-02-15 15:51:58 +00:00
while ( i -- ) {
2010-06-22 14:52:51 +00:00
var elem = selectedElements [ i ] ;
2018-05-16 08:32:44 +00:00
// if (svgedit.utilities.getRotationAngle(elem) && !svgedit.math.hasMatrixTransform(getTransformList(elem))) { continue; }
2013-02-20 06:29:25 +00:00
var cmd = svgedit . recalculate . recalculateDimensions ( elem ) ;
2010-06-22 14:52:51 +00:00
if ( cmd ) {
batchCmd . addSubCommand ( cmd ) ;
}
}
if ( ! batchCmd . isEmpty ( ) ) {
addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , selectedElements ) ;
2010-06-22 14:52:51 +00:00
}
} ;
2010-06-22 18:17:42 +00:00
// Debug tool to easily see the current matrix in the browser's console
2018-05-16 08:32:44 +00:00
var logMatrix = function ( m ) {
2013-02-15 16:51:48 +00:00
console . log ( [ m . a , m . b , m . c , m . d , m . e , m . f ] ) ;
2010-06-22 14:52:51 +00:00
} ;
2010-06-22 18:17:42 +00:00
// Root Current Transformation Matrix in user units
2018-05-16 08:32:44 +00:00
var rootSctm = null ;
2010-06-22 18:17:42 +00:00
2010-06-22 14:52:51 +00:00
// Group: Selection
2009-10-13 02:48:27 +00:00
2010-06-22 14:52:51 +00:00
// Function: clearSelection
2014-01-31 10:40:52 +00:00
// Clears the selection. The 'selected' handler is then called.
2018-05-16 00:53:27 +00:00
// Parameters:
2010-06-22 18:17:42 +00:00
// noCall - Optional boolean that when true does not call the "selected" handler
2018-05-16 08:32:44 +00:00
var clearSelection = this . clearSelection = function ( noCall ) {
selectedElements . map ( function ( elem ) {
if ( elem == null ) return ;
2018-01-18 12:37:49 +00:00
selectorManager . releaseSelector ( elem ) ;
} ) ;
selectedElements = [ ] ;
2018-05-16 08:32:44 +00:00
if ( ! noCall ) { call ( 'selected' , selectedElements ) ; }
2010-06-22 14:52:51 +00:00
} ;
// TODO: do we need to worry about selectedBBoxes here?
// Function: addToSelection
2014-01-31 10:40:52 +00:00
// Adds a list of elements to the selection. The 'selected' handler is then called.
2010-06-22 14:52:51 +00:00
//
// Parameters:
// elemsToAdd - an array of DOM elements to add to the selection
// showGrips - a boolean flag indicating whether the resize grips should be shown
2018-05-16 08:32:44 +00:00
var addToSelection = this . addToSelection = function ( elemsToAdd , showGrips ) {
if ( elemsToAdd . length === 0 ) { return ; }
2010-06-22 14:52:51 +00:00
// find the first null in our selectedElements array
var j = 0 ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
while ( j < selectedElements . length ) {
2018-05-16 00:53:27 +00:00
if ( selectedElements [ j ] == null ) {
2010-06-22 14:52:51 +00:00
break ;
}
++ j ;
}
2009-08-17 06:56:55 +00:00
2010-06-22 14:52:51 +00:00
// now add each element consecutively
var i = elemsToAdd . length ;
while ( i -- ) {
var elem = elemsToAdd [ i ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { continue ; }
2016-04-24 20:56:32 +00:00
var bbox = svgedit . utilities . getBBox ( elem ) ;
2018-05-16 08:32:44 +00:00
if ( ! bbox ) { continue ; }
2010-11-30 20:51:07 +00:00
2013-02-15 15:51:58 +00:00
if ( elem . tagName === 'a' && elem . childNodes . length === 1 ) {
2018-05-16 00:53:27 +00:00
// Make "a" element's child be the selected element
2010-11-30 20:51:07 +00:00
elem = elem . firstChild ;
}
2010-06-22 14:52:51 +00:00
// if it's not already there, add it
2018-05-16 08:32:44 +00:00
if ( selectedElements . indexOf ( elem ) === - 1 ) {
2010-06-22 14:52:51 +00:00
selectedElements [ j ] = elem ;
2010-11-30 20:51:07 +00:00
2010-06-22 14:52:51 +00:00
// only the first selectedBBoxes element is ever used in the codebase these days
2018-05-16 08:32:44 +00:00
// if (j === 0) selectedBBoxes[0] = svgedit.utilities.getBBox(elem);
2010-06-22 14:52:51 +00:00
j ++ ;
2016-04-24 20:56:32 +00:00
var sel = selectorManager . requestSelector ( elem , bbox ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
if ( selectedElements . length > 1 ) {
sel . showGrips ( false ) ;
2009-07-01 15:34:15 +00:00
}
}
2010-06-22 14:52:51 +00:00
}
2014-04-08 03:21:22 +00:00
call ( 'selected' , selectedElements ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( showGrips || selectedElements . length === 1 ) {
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( selectedElements [ 0 ] ) . showGrips ( true ) ;
2018-05-16 08:32:44 +00:00
} else {
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( selectedElements [ 0 ] ) . showGrips ( false ) ;
}
// make sure the elements are in the correct order
// See: http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition
2018-05-16 08:32:44 +00:00
selectedElements . sort ( function ( a , b ) {
2013-02-15 15:51:58 +00:00
if ( a && b && a . compareDocumentPosition ) {
2018-05-16 00:53:27 +00:00
return 3 - ( b . compareDocumentPosition ( a ) & 6 ) ;
2013-02-15 15:51:58 +00:00
}
if ( a == null ) {
2010-06-22 14:52:51 +00:00
return 1 ;
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Make sure first elements are not null
2014-01-31 10:40:52 +00:00
while ( selectedElements [ 0 ] == null ) {
selectedElements . shift ( 0 ) ;
}
2010-06-22 14:52:51 +00:00
} ;
2009-12-18 05:00:45 +00:00
2010-09-17 20:33:33 +00:00
// Function: selectOnly()
// Selects only the given elements, shortcut for clearSelection(); addToSelection()
//
// Parameters:
// elems - an array of DOM elements to be selected
2018-05-16 08:32:44 +00:00
var selectOnly = this . selectOnly = function ( elems , showGrips ) {
2010-09-17 20:33:33 +00:00
clearSelection ( true ) ;
addToSelection ( elems , showGrips ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-09-17 20:33:33 +00:00
2010-06-22 14:52:51 +00:00
// TODO: could use slice here to make this faster?
// TODO: should the 'selected' handler
// Function: removeFromSelection
// Removes elements from the selection.
//
// Parameters:
// elemsToRemove - an array of elements to remove from selection
2018-05-16 08:32:44 +00:00
/* var removeFromSelection = */ this . removeFromSelection = function ( elemsToRemove ) {
2010-06-22 14:52:51 +00:00
if ( selectedElements [ 0 ] == null ) { return ; }
2018-05-16 08:32:44 +00:00
if ( elemsToRemove . length === 0 ) { return ; }
2010-06-22 14:52:51 +00:00
// find every element and remove it from our array copy
2014-01-31 10:40:52 +00:00
var i ,
2010-06-22 14:52:51 +00:00
j = 0 ,
2014-06-01 21:29:54 +00:00
newSelectedItems = [ ] ,
2010-06-22 14:52:51 +00:00
len = selectedElements . length ;
2014-06-01 21:29:54 +00:00
newSelectedItems . length = len ;
2014-01-31 10:40:52 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2010-06-22 14:52:51 +00:00
var elem = selectedElements [ i ] ;
if ( elem ) {
// keep the item
2018-05-16 08:32:44 +00:00
if ( elemsToRemove . indexOf ( elem ) === - 1 ) {
2010-06-22 14:52:51 +00:00
newSelectedItems [ j ] = elem ;
2009-12-29 14:26:04 +00:00
j ++ ;
2013-02-15 16:51:48 +00:00
} else { // remove the item and its selector
2010-06-22 14:52:51 +00:00
selectorManager . releaseSelector ( elem ) ;
2009-07-01 15:34:15 +00:00
}
}
2010-06-22 14:52:51 +00:00
}
// the copy becomes the master now
selectedElements = newSelectedItems ;
} ;
2010-06-28 13:10:22 +00:00
// Function: selectAllInCurrentLayer
// Clears the selection, then adds all elements in the current layer to the selection.
2018-05-16 08:32:44 +00:00
this . selectAllInCurrentLayer = function ( ) {
var currentLayer = getCurrentDrawing ( ) . getCurrentLayer ( ) ;
if ( currentLayer ) {
currentMode = 'select' ;
selectOnly ( $ ( currentGroup || currentLayer ) . children ( ) ) ;
2010-06-28 13:10:22 +00:00
}
} ;
2010-06-22 18:17:42 +00:00
// Function: getMouseTarget
// Gets the desired element from a mouse event
2018-05-16 00:53:27 +00:00
//
2010-06-22 18:17:42 +00:00
// Parameters:
// evt - Event object from the mouse event
2018-05-16 00:53:27 +00:00
//
2010-06-22 18:17:42 +00:00
// Returns:
// DOM element we want
2018-05-16 08:32:44 +00:00
var getMouseTarget = this . getMouseTarget = function ( evt ) {
2010-06-22 14:52:51 +00:00
if ( evt == null ) {
return null ;
}
2018-05-16 08:32:44 +00:00
var mouseTarget = evt . target ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// if it was a <use>, Opera and WebKit return the SVGElementInstance
2018-05-16 08:32:44 +00:00
if ( mouseTarget . correspondingUseElement ) { mouseTarget = mouseTarget . correspondingUseElement ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// for foreign content, go up until we find the foreignObject
2018-05-16 00:53:27 +00:00
// WebKit browsers set the mouse target to the svgcanvas div
2018-05-16 08:32:44 +00:00
if ( [ NS . MATH , NS . HTML ] . indexOf ( mouseTarget . namespaceURI ) >= 0 &&
mouseTarget . id !== 'svgcanvas'
) {
while ( mouseTarget . nodeName !== 'foreignObject' ) {
mouseTarget = mouseTarget . parentNode ;
if ( ! mouseTarget ) { return svgroot ; }
2009-12-13 03:31:30 +00:00
}
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// Get the desired mouseTarget with jQuery selector-fu
2010-07-16 15:46:54 +00:00
// If it's root-like, select the root
2018-05-16 08:32:44 +00:00
var currentLayer = getCurrentDrawing ( ) . getCurrentLayer ( ) ;
if ( [ svgroot , container , svgcontent , currentLayer ] . indexOf ( mouseTarget ) >= 0 ) {
2010-07-16 15:46:54 +00:00
return svgroot ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var $target = $ ( mouseTarget ) ;
2010-07-16 15:46:54 +00:00
// If it's a selection grip, return the grip parent
2013-02-15 15:51:58 +00:00
if ( $target . closest ( '#selectorParentGroup' ) . length ) {
2018-05-16 08:32:44 +00:00
// While we could instead have just returned mouseTarget,
2010-07-16 15:46:54 +00:00
// this makes it easier to indentify as being a selector grip
return selectorManager . selectorParentGroup ;
}
2010-09-23 19:53:43 +00:00
2018-05-16 08:32:44 +00:00
while ( mouseTarget . parentNode !== ( currentGroup || currentLayer ) ) {
mouseTarget = mouseTarget . parentNode ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
//
// // go up until we hit a child of a layer
// while (mouseTarget.parentNode.parentNode.tagName == 'g') {
// mouseTarget = mouseTarget.parentNode;
// }
2010-06-22 14:52:51 +00:00
// Webkit bubbles the mouse event all the way up to the div, so we
2018-05-16 08:32:44 +00:00
// set the mouseTarget to the svgroot like the other browsers
// if (mouseTarget.nodeName.toLowerCase() == 'div') {
// mouseTarget = svgroot;
// }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
return mouseTarget ;
2010-06-22 14:52:51 +00:00
} ;
2018-05-16 08:32:44 +00:00
var drawnPath = null ;
2010-06-22 14:52:51 +00:00
// Mouse events
2018-05-16 08:32:44 +00:00
( function ( ) {
var dAttr = null ,
startX = null ,
startY = null ,
rStartX = null ,
rStartY = null ,
initBbox = { } ,
2010-06-22 14:52:51 +00:00
freehand = {
minx : null ,
miny : null ,
maxx : null ,
maxy : null
2013-02-13 16:10:15 +00:00
} ,
2013-02-15 15:51:58 +00:00
sumDistance = 0 ,
2018-05-16 08:32:44 +00:00
controllPoint2 = { x : 0 , y : 0 } ,
controllPoint1 = { x : 0 , y : 0 } ,
start = { x : 0 , y : 0 } ,
end = { x : 0 , y : 0 } ,
2013-02-13 16:10:15 +00:00
parameter ,
nextParameter ,
2018-05-16 08:32:44 +00:00
bSpline = { x : 0 , y : 0 } ,
nextPos = { x : 0 , y : 0 } ,
2013-02-15 15:51:58 +00:00
THRESHOLD _DIST = 0.8 ,
STEP _COUNT = 10 ;
2013-02-13 16:10:15 +00:00
2018-05-16 08:32:44 +00:00
var getBsplinePoint = function ( t ) {
var spline = { x : 0 , y : 0 } ,
2013-02-15 15:51:58 +00:00
p0 = controllPoint2 ,
p1 = controllPoint1 ,
p2 = start ,
p3 = end ,
S = 1.0 / 6.0 ,
t2 = t * t ,
t3 = t2 * t ;
2013-02-13 16:10:15 +00:00
2014-01-31 10:40:52 +00:00
var m = [
[ - 1 , 3 , - 3 , 1 ] ,
[ 3 , - 6 , 3 , 0 ] ,
[ - 3 , 0 , 3 , 0 ] ,
[ 1 , 4 , 1 , 0 ]
] ;
2013-02-13 16:10:15 +00:00
spline . x = S * (
2018-05-16 08:32:44 +00:00
( p0 . x * m [ 0 ] [ 0 ] + p1 . x * m [ 0 ] [ 1 ] + p2 . x * m [ 0 ] [ 2 ] + p3 . x * m [ 0 ] [ 3 ] ) * t3 +
( p0 . x * m [ 1 ] [ 0 ] + p1 . x * m [ 1 ] [ 1 ] + p2 . x * m [ 1 ] [ 2 ] + p3 . x * m [ 1 ] [ 3 ] ) * t2 +
( p0 . x * m [ 2 ] [ 0 ] + p1 . x * m [ 2 ] [ 1 ] + p2 . x * m [ 2 ] [ 2 ] + p3 . x * m [ 2 ] [ 3 ] ) * t +
( p0 . x * m [ 3 ] [ 0 ] + p1 . x * m [ 3 ] [ 1 ] + p2 . x * m [ 3 ] [ 2 ] + p3 . x * m [ 3 ] [ 3 ] )
2014-01-31 10:40:52 +00:00
) ;
2013-02-13 16:10:15 +00:00
spline . y = S * (
2018-05-16 08:32:44 +00:00
( p0 . y * m [ 0 ] [ 0 ] + p1 . y * m [ 0 ] [ 1 ] + p2 . y * m [ 0 ] [ 2 ] + p3 . y * m [ 0 ] [ 3 ] ) * t3 +
( p0 . y * m [ 1 ] [ 0 ] + p1 . y * m [ 1 ] [ 1 ] + p2 . y * m [ 1 ] [ 2 ] + p3 . y * m [ 1 ] [ 3 ] ) * t2 +
( p0 . y * m [ 2 ] [ 0 ] + p1 . y * m [ 2 ] [ 1 ] + p2 . y * m [ 2 ] [ 2 ] + p3 . y * m [ 2 ] [ 3 ] ) * t +
( p0 . y * m [ 3 ] [ 0 ] + p1 . y * m [ 3 ] [ 1 ] + p2 . y * m [ 3 ] [ 2 ] + p3 . y * m [ 3 ] [ 3 ] )
2014-01-31 10:40:52 +00:00
) ;
2013-02-13 16:10:15 +00:00
return {
2018-05-16 08:32:44 +00:00
x : spline . x ,
y : spline . y
2010-06-22 14:52:51 +00:00
} ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
// - when we are in a create mode, the element is added to the canvas
2014-01-31 10:40:52 +00:00
// but the action is not recorded until mousing up
2010-06-22 14:52:51 +00:00
// - when we are in select mode, select the element, remember the position
2014-01-31 10:40:52 +00:00
// and do nothing else
2018-05-16 08:32:44 +00:00
var mouseDown = function ( evt ) {
if ( canvas . spaceKey || evt . button === 1 ) { return ; }
2014-01-31 10:40:52 +00:00
2018-05-16 08:32:44 +00:00
var rightClick = evt . button === 2 ;
2018-05-16 00:53:27 +00:00
2014-01-31 10:40:52 +00:00
if ( evt . altKey ) { // duplicate when dragging
2013-02-15 16:51:48 +00:00
svgCanvas . cloneSelectedElements ( 0 , 0 ) ;
2011-01-30 05:55:33 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
rootSctm = $ ( '#svgcontent g' ) [ 0 ] . getScreenCTM ( ) . inverse ( ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var pt = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ,
mouseX = pt . x * currentZoom ,
mouseY = pt . y * currentZoom ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
evt . preventDefault ( ) ;
2010-08-17 18:09:50 +00:00
2018-05-16 08:32:44 +00:00
if ( rightClick ) {
currentMode = 'select' ;
2010-08-17 18:09:50 +00:00
lastClickPoint = pt ;
}
2018-05-16 00:53:27 +00:00
2010-06-28 20:09:34 +00:00
// This would seem to be unnecessary...
2018-05-16 08:32:44 +00:00
// if (['select', 'resize'].indexOf(currentMode) === -1) {
// setGradient();
// }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var x = mouseX / currentZoom ,
y = mouseY / currentZoom ,
mouseTarget = getMouseTarget ( evt ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( mouseTarget . tagName === 'a' && mouseTarget . childNodes . length === 1 ) {
mouseTarget = mouseTarget . firstChild ;
2010-11-30 20:51:07 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// realX/y ignores grid-snap value
var realX = x ;
rStartX = startX = x ;
var realY = y ;
rStartY = startY = y ;
2010-09-09 12:46:34 +00:00
2018-05-16 08:32:44 +00:00
if ( curConfig . gridSnapping ) {
2013-02-17 04:58:04 +00:00
x = svgedit . utilities . snapToGrid ( x ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
2018-05-16 08:32:44 +00:00
startX = svgedit . utilities . snapToGrid ( startX ) ;
startY = svgedit . utilities . snapToGrid ( startY ) ;
2010-09-09 12:46:34 +00:00
}
2018-05-16 00:53:27 +00:00
// if it is a selector grip, then it must be a single element selected,
2018-05-16 08:32:44 +00:00
// set the mouseTarget to that and update the mode to rotate/resize
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( mouseTarget === selectorManager . selectorParentGroup && selectedElements [ 0 ] != null ) {
2010-09-10 18:51:59 +00:00
var grip = evt . target ;
2014-04-08 03:21:22 +00:00
var griptype = elData ( grip , 'type' ) ;
2010-06-22 14:52:51 +00:00
// rotating
2018-05-16 08:32:44 +00:00
if ( griptype === 'rotate' ) {
currentMode = 'rotate' ;
2010-06-22 14:52:51 +00:00
// resizing
2018-05-16 08:32:44 +00:00
} else if ( griptype === 'resize' ) {
currentMode = 'resize' ;
currentResizeMode = elData ( grip , 'dir' ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
mouseTarget = selectedElements [ 0 ] ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
startTransform = mouseTarget . getAttribute ( 'transform' ) ;
var i , strokeW ,
tlist = svgedit . transformlist . getTransformList ( mouseTarget ) ;
switch ( currentMode ) {
2018-05-16 00:53:27 +00:00
case 'select' :
started = true ;
2018-05-16 08:32:44 +00:00
currentResizeMode = 'none' ;
if ( rightClick ) { started = false ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( mouseTarget !== svgroot ) {
2018-05-16 00:53:27 +00:00
// if this element is not yet selected, clear selection and select it
2018-05-16 08:32:44 +00:00
if ( selectedElements . indexOf ( mouseTarget ) === - 1 ) {
2018-05-16 00:53:27 +00:00
// only clear selection if shift is not pressed (otherwise, add
// element to selection)
if ( ! evt . shiftKey ) {
// No need to do the call here as it will be done on addToSelection
clearSelection ( true ) ;
2009-10-17 20:29:56 +00:00
}
2018-05-16 08:32:44 +00:00
addToSelection ( [ mouseTarget ] ) ;
justSelected = mouseTarget ;
2018-05-16 00:53:27 +00:00
pathActions . clear ( ) ;
}
// else if it's a path, go into pathedit mode in mouseup
2018-05-16 08:32:44 +00:00
if ( ! rightClick ) {
2018-05-16 00:53:27 +00:00
// insert a dummy transform so if the element(s) are moved it will have
// a transform to use for its translate
for ( i = 0 ; i < selectedElements . length ; ++ i ) {
2018-05-16 08:32:44 +00:00
if ( selectedElements [ i ] == null ) { continue ; }
2018-05-16 00:53:27 +00:00
var slist = svgedit . transformlist . getTransformList ( selectedElements [ i ] ) ;
if ( slist . numberOfItems ) {
slist . insertItemBefore ( svgroot . createSVGTransform ( ) , 0 ) ;
} else {
slist . appendItem ( svgroot . createSVGTransform ( ) ) ;
2010-08-23 13:58:51 +00:00
}
2009-08-24 17:40:41 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
} else if ( ! rightClick ) {
2018-05-16 00:53:27 +00:00
clearSelection ( ) ;
2018-05-16 08:32:44 +00:00
currentMode = 'multiselect' ;
2010-06-22 14:52:51 +00:00
if ( rubberBox == null ) {
rubberBox = selectorManager . getRubberBandBox ( ) ;
}
2018-05-16 08:32:44 +00:00
rStartX *= currentZoom ;
rStartY *= currentZoom ;
// console.log('p',[evt.pageX, evt.pageY]);
// console.log('c',[evt.clientX, evt.clientY]);
// console.log('o',[evt.offsetX, evt.offsetY]);
// console.log('s',[startX, startY]);
2018-05-16 00:53:27 +00:00
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : rStartX ,
'y' : rStartY ,
2018-05-16 00:53:27 +00:00
'width' : 0 ,
'height' : 0 ,
'display' : 'inline'
2010-06-22 14:52:51 +00:00
} , 100 ) ;
2018-05-16 00:53:27 +00:00
}
break ;
case 'zoom' :
started = true ;
if ( rubberBox == null ) {
rubberBox = selectorManager . getRubberBandBox ( ) ;
}
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : realX * currentZoom ,
'y' : realX * currentZoom ,
2018-05-16 00:53:27 +00:00
'width' : 0 ,
'height' : 0 ,
'display' : 'inline'
} , 100 ) ;
break ;
case 'resize' :
started = true ;
2018-05-16 08:32:44 +00:00
startX = x ;
startY = y ;
2018-05-16 00:53:27 +00:00
// Getting the BBox from the selection box, since we know we
// want to orient around it
2018-05-16 08:32:44 +00:00
initBbox = svgedit . utilities . getBBox ( $ ( '#selectedBox0' ) [ 0 ] ) ;
2018-05-16 00:53:27 +00:00
var bb = { } ;
2018-05-16 08:32:44 +00:00
$ . each ( initBbox , function ( key , val ) {
bb [ key ] = val / currentZoom ;
2018-05-16 00:53:27 +00:00
} ) ;
2018-05-16 08:32:44 +00:00
initBbox = bb ;
2018-05-16 00:53:27 +00:00
// append three dummy transforms to the tlist so that
// we can translate,scale,translate in mousemove
2018-05-16 08:32:44 +00:00
var pos = svgedit . utilities . getRotationAngle ( mouseTarget ) ? 1 : 0 ;
2018-05-16 00:53:27 +00:00
if ( svgedit . math . hasMatrixTransform ( tlist ) ) {
tlist . insertItemBefore ( svgroot . createSVGTransform ( ) , pos ) ;
tlist . insertItemBefore ( svgroot . createSVGTransform ( ) , pos ) ;
tlist . insertItemBefore ( svgroot . createSVGTransform ( ) , pos ) ;
} else {
tlist . appendItem ( svgroot . createSVGTransform ( ) ) ;
tlist . appendItem ( svgroot . createSVGTransform ( ) ) ;
tlist . appendItem ( svgroot . createSVGTransform ( ) ) ;
if ( svgedit . browser . supportsNonScalingStroke ( ) ) {
// Handle crash for newer Chrome and Safari 6 (Mobile and Desktop):
// https://code.google.com/p/svg-edit/issues/detail?id=904
// Chromium issue: https://code.google.com/p/chromium/issues/detail?id=114625
// TODO: Remove this workaround once vendor fixes the issue
var isWebkit = svgedit . browser . isWebkit ( ) ;
if ( isWebkit ) {
2018-05-16 08:32:44 +00:00
var delayedStroke = function ( ele ) {
2018-05-16 00:53:27 +00:00
var _stroke = ele . getAttributeNS ( null , 'stroke' ) ;
ele . removeAttributeNS ( null , 'stroke' ) ;
// Re-apply stroke after delay. Anything higher than 1 seems to cause flicker
2018-05-16 08:32:44 +00:00
if ( _stroke !== null ) setTimeout ( function ( ) { ele . setAttributeNS ( null , 'stroke' , _stroke ) ; } , 0 ) ;
2018-05-16 00:53:27 +00:00
} ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
mouseTarget . style . vectorEffect = 'non-scaling-stroke' ;
if ( isWebkit ) { delayedStroke ( mouseTarget ) ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var all = mouseTarget . getElementsByTagName ( '*' ) ,
2018-05-16 00:53:27 +00:00
len = all . length ;
for ( i = 0 ; i < len ; i ++ ) {
all [ i ] . style . vectorEffect = 'non-scaling-stroke' ;
2018-05-16 08:32:44 +00:00
if ( isWebkit ) { delayedStroke ( all [ i ] ) ; }
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
}
}
break ;
case 'fhellipse' :
case 'fhrect' :
case 'fhpath' :
2018-05-16 08:32:44 +00:00
start . x = realX ;
start . y = realY ;
2018-05-16 00:53:27 +00:00
started = true ;
2018-05-16 08:32:44 +00:00
dAttr = realX + ',' + realY + ' ' ;
strokeW = parseFloat ( curShape . stroke _width ) === 0 ? 1 : curShape . stroke _width ;
2018-05-16 00:53:27 +00:00
addSvgElementFromJson ( {
element : 'polyline' ,
curStyles : true ,
attr : {
2018-05-16 08:32:44 +00:00
points : dAttr ,
2018-05-16 00:53:27 +00:00
id : getNextId ( ) ,
fill : 'none' ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2 ,
2018-05-16 00:53:27 +00:00
'stroke-linecap' : 'round' ,
style : 'pointer-events:none'
}
} ) ;
2018-05-16 08:32:44 +00:00
freehand . minx = realX ;
freehand . maxx = realX ;
freehand . miny = realY ;
freehand . maxy = realY ;
2018-05-16 00:53:27 +00:00
break ;
case 'image' :
started = true ;
var newImage = addSvgElementFromJson ( {
element : 'image' ,
attr : {
x : x ,
y : y ,
width : 0 ,
height : 0 ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2 ,
2018-05-16 00:53:27 +00:00
style : 'pointer-events:inherit'
}
} ) ;
2018-05-16 08:32:44 +00:00
setHref ( newImage , lastGoodImgUrl ) ;
2018-05-16 00:53:27 +00:00
svgedit . utilities . preventClickDefault ( newImage ) ;
break ;
case 'square' :
// FIXME: once we create the rect, we lose information that this was a square
// (for resizing purposes this could be important)
2018-05-16 08:32:44 +00:00
// Fallthrough
2018-05-16 00:53:27 +00:00
case 'rect' :
started = true ;
2018-05-16 08:32:44 +00:00
startX = x ;
startY = y ;
2018-05-16 00:53:27 +00:00
addSvgElementFromJson ( {
element : 'rect' ,
curStyles : true ,
attr : {
x : x ,
y : y ,
width : 0 ,
height : 0 ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2
2018-05-16 00:53:27 +00:00
}
} ) ;
break ;
case 'line' :
started = true ;
2018-05-16 08:32:44 +00:00
strokeW = Number ( curShape . stroke _width ) === 0 ? 1 : curShape . stroke _width ;
2018-05-16 00:53:27 +00:00
addSvgElementFromJson ( {
element : 'line' ,
curStyles : true ,
attr : {
x1 : x ,
y1 : y ,
x2 : x ,
y2 : y ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
stroke : curShape . stroke ,
'stroke-width' : strokeW ,
'stroke-dasharray' : curShape . stroke _dasharray ,
'stroke-linejoin' : curShape . stroke _linejoin ,
'stroke-linecap' : curShape . stroke _linecap ,
'stroke-opacity' : curShape . stroke _opacity ,
2018-05-16 00:53:27 +00:00
fill : 'none' ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2 ,
2018-05-16 00:53:27 +00:00
style : 'pointer-events:none'
}
} ) ;
break ;
case 'circle' :
started = true ;
addSvgElementFromJson ( {
element : 'circle' ,
curStyles : true ,
attr : {
cx : x ,
cy : y ,
r : 0 ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2
2018-05-16 00:53:27 +00:00
}
} ) ;
break ;
case 'ellipse' :
started = true ;
addSvgElementFromJson ( {
element : 'ellipse' ,
curStyles : true ,
attr : {
cx : x ,
cy : y ,
rx : 0 ,
ry : 0 ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2
2018-05-16 00:53:27 +00:00
}
} ) ;
break ;
case 'text' :
started = true ;
2018-05-16 08:32:44 +00:00
/* var newText = */ addSvgElementFromJson ( {
2018-05-16 00:53:27 +00:00
element : 'text' ,
curStyles : true ,
attr : {
x : x ,
y : y ,
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
fill : curText . fill ,
'stroke-width' : curText . stroke _width ,
'font-size' : curText . font _size ,
'font-family' : curText . font _family ,
2018-05-16 00:53:27 +00:00
'text-anchor' : 'middle' ,
'xml:space' : 'preserve' ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity
2018-05-16 00:53:27 +00:00
}
} ) ;
2018-05-16 08:32:44 +00:00
// newText.textContent = 'text';
2018-05-16 00:53:27 +00:00
break ;
case 'path' :
// Fall through
case 'pathedit' :
2018-05-16 08:32:44 +00:00
startX *= currentZoom ;
startY *= currentZoom ;
pathActions . mouseDown ( evt , mouseTarget , startX , startY ) ;
2018-05-16 00:53:27 +00:00
started = true ;
break ;
case 'textedit' :
2018-05-16 08:32:44 +00:00
startX *= currentZoom ;
startY *= currentZoom ;
textActions . mouseDown ( evt , mouseTarget , startX , startY ) ;
2018-05-16 00:53:27 +00:00
started = true ;
break ;
case 'rotate' :
started = true ;
// we are starting an undoable change (a drag-rotation)
canvas . undoMgr . beginUndoableChange ( 'transform' , selectedElements ) ;
break ;
default :
// This could occur in an extension
break ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var extResult = runExtensions ( 'mouseDown' , {
2010-06-22 14:52:51 +00:00
event : evt ,
2018-05-16 08:32:44 +00:00
start _x : startX ,
start _y : startY ,
2010-06-22 14:52:51 +00:00
selectedElements : selectedElements
} , true ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( extResult , function ( i , r ) {
2013-02-15 15:51:58 +00:00
if ( r && r . started ) {
2010-06-22 14:52:51 +00:00
started = true ;
2009-12-30 18:06:29 +00:00
}
2010-06-22 14:52:51 +00:00
} ) ;
} ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// in this function we do not record any state changes yet (but we do update
// any elements that are still being created, moved or resized on the canvas)
2018-05-16 08:32:44 +00:00
var mouseMove = function ( evt ) {
if ( ! started ) { return ; }
if ( evt . button === 1 || canvas . spaceKey ) { return ; }
2011-02-23 18:30:09 +00:00
2014-02-11 13:46:39 +00:00
var i , xya , c , cx , cy , dx , dy , len , angle , box ,
2014-02-10 13:27:34 +00:00
selected = selectedElements [ 0 ] ,
2018-05-16 08:32:44 +00:00
pt = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ,
mouseX = pt . x * currentZoom ,
mouseY = pt . y * currentZoom ,
2013-02-14 15:19:46 +00:00
shape = svgedit . utilities . getElem ( getId ( ) ) ;
2010-08-23 20:16:27 +00:00
2018-05-16 08:32:44 +00:00
var realX = mouseX / currentZoom ;
var x = realX ;
var realY = mouseY / currentZoom ;
var y = realY ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( curConfig . gridSnapping ) {
2013-02-17 04:58:04 +00:00
x = svgedit . utilities . snapToGrid ( x ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
2010-09-09 12:46:34 +00:00
}
2010-06-22 14:52:51 +00:00
evt . preventDefault ( ) ;
2014-02-11 14:02:48 +00:00
var tlist ;
2018-05-16 08:32:44 +00:00
switch ( currentMode ) {
2018-05-16 00:53:27 +00:00
case 'select' :
// we temporarily use a translate on the element(s) being dragged
// this transform is removed upon mousing up and the element is
// relocated to the new location
if ( selectedElements [ 0 ] !== null ) {
2018-05-16 08:32:44 +00:00
dx = x - startX ;
dy = y - startY ;
2010-09-09 12:46:34 +00:00
2018-05-16 08:32:44 +00:00
if ( curConfig . gridSnapping ) {
2018-05-16 00:53:27 +00:00
dx = svgedit . utilities . snapToGrid ( dx ) ;
dy = svgedit . utilities . snapToGrid ( dy ) ;
}
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
if ( evt . shiftKey ) {
2018-05-16 08:32:44 +00:00
xya = svgedit . math . snapToAngle ( startX , startY , x , y ) ;
2018-05-16 00:53:27 +00:00
x = xya . x ;
y = xya . y ;
}
2018-05-16 08:32:44 +00:00
if ( dx !== 0 || dy !== 0 ) {
2018-05-16 00:53:27 +00:00
len = selectedElements . length ;
for ( i = 0 ; i < len ; ++ i ) {
selected = selectedElements [ i ] ;
2018-05-16 08:32:44 +00:00
if ( selected == null ) { break ; }
// if (i === 0) {
// var box = svgedit.utilities.getBBox(selected);
// selectedBBoxes[i].x = box.x + dx;
// selectedBBoxes[i].y = box.y + dy;
// }
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
// update the dummy transform in our transform list
// to be a translate
var xform = svgroot . createSVGTransform ( ) ;
tlist = svgedit . transformlist . getTransformList ( selected ) ;
// Note that if Webkit and there's no ID for this
// element, the dummy transform may have gotten lost.
// This results in unexpected behaviour
xform . setTranslate ( dx , dy ) ;
if ( tlist . numberOfItems ) {
tlist . replaceItem ( xform , 0 ) ;
} else {
tlist . appendItem ( xform ) ;
2009-12-30 18:06:29 +00:00
}
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
// update our internal bbox that we're tracking while dragging
selectorManager . requestSelector ( selected ) . resize ( ) ;
2015-12-01 03:08:13 +00:00
}
2010-09-09 12:46:34 +00:00
2018-05-16 00:53:27 +00:00
call ( 'transition' , selectedElements ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
}
break ;
case 'multiselect' :
2018-05-16 08:32:44 +00:00
realX *= currentZoom ;
realY *= currentZoom ;
2018-05-16 00:53:27 +00:00
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : Math . min ( rStartX , realX ) ,
'y' : Math . min ( rStartY , realY ) ,
'width' : Math . abs ( realX - rStartX ) ,
'height' : Math . abs ( realY - rStartY )
2018-05-16 00:53:27 +00:00
} , 100 ) ;
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
// for each selected:
// - if newList contains selected, do nothing
// - if newList doesn't contain selected, remove it from selected
// - for any newList that was not in selectedElements, add it to selected
var elemsToRemove = selectedElements . slice ( ) , elemsToAdd = [ ] ,
newList = getIntersectionList ( ) ;
// For every element in the intersection, add if not present in selectedElements.
len = newList . length ;
for ( i = 0 ; i < len ; ++ i ) {
var intElem = newList [ i ] ;
// Found an element that was not selected before, so we should add it.
2018-05-16 08:32:44 +00:00
if ( selectedElements . indexOf ( intElem ) === - 1 ) {
2018-05-16 00:53:27 +00:00
elemsToAdd . push ( intElem ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
// Found an element that was already selected, so we shouldn't remove it.
var foundInd = elemsToRemove . indexOf ( intElem ) ;
2018-05-16 08:32:44 +00:00
if ( foundInd !== - 1 ) {
elemsToRemove . splice ( foundInd , 1 ) ;
2010-09-09 12:46:34 +00:00
}
2018-05-16 00:53:27 +00:00
}
2010-09-09 12:46:34 +00:00
2018-05-16 00:53:27 +00:00
if ( elemsToRemove . length > 0 ) {
canvas . removeFromSelection ( elemsToRemove ) ;
}
if ( elemsToAdd . length > 0 ) {
canvas . addToSelection ( elemsToAdd ) ;
}
break ;
case 'resize' :
// we track the resize bounding box and translate/scale the selected element
// while the mouse is down, when mouse goes up, we use this to recalculate
// the shape's coordinates
tlist = svgedit . transformlist . getTransformList ( selected ) ;
var hasMatrix = svgedit . math . hasMatrixTransform ( tlist ) ;
2018-05-16 08:32:44 +00:00
box = hasMatrix ? initBbox : svgedit . utilities . getBBox ( selected ) ;
2018-05-16 00:53:27 +00:00
var left = box . x , top = box . y , width = box . width ,
height = box . height ;
2018-05-16 08:32:44 +00:00
dx = ( x - startX ) ;
dy = ( y - startY ) ;
2018-05-16 00:53:27 +00:00
if ( curConfig . gridSnapping ) {
dx = svgedit . utilities . snapToGrid ( dx ) ;
dy = svgedit . utilities . snapToGrid ( dy ) ;
height = svgedit . utilities . snapToGrid ( height ) ;
width = svgedit . utilities . snapToGrid ( width ) ;
}
// if rotated, adjust the dx,dy values
angle = svgedit . utilities . getRotationAngle ( selected ) ;
if ( angle ) {
2018-05-16 08:32:44 +00:00
var r = Math . sqrt ( dx * dx + dy * dy ) ,
2018-05-16 00:53:27 +00:00
theta = Math . atan2 ( dy , dx ) - angle * Math . PI / 180.0 ;
dx = r * Math . cos ( theta ) ;
dy = r * Math . sin ( theta ) ;
}
// if not stretching in y direction, set dy to 0
// if not stretching in x direction, set dx to 0
2018-05-16 08:32:44 +00:00
if ( currentResizeMode . indexOf ( 'n' ) === - 1 && currentResizeMode . indexOf ( 's' ) === - 1 ) {
2018-05-16 00:53:27 +00:00
dy = 0 ;
}
2018-05-16 08:32:44 +00:00
if ( currentResizeMode . indexOf ( 'e' ) === - 1 && currentResizeMode . indexOf ( 'w' ) === - 1 ) {
2018-05-16 00:53:27 +00:00
dx = 0 ;
}
2018-05-16 08:32:44 +00:00
var // ts = null,
2018-05-16 00:53:27 +00:00
tx = 0 , ty = 0 ,
2018-05-16 08:32:44 +00:00
sy = height ? ( height + dy ) / height : 1 ,
sx = width ? ( width + dx ) / width : 1 ;
2018-05-16 00:53:27 +00:00
// if we are dragging on the north side, then adjust the scale factor and ty
2018-05-16 08:32:44 +00:00
if ( currentResizeMode . indexOf ( 'n' ) >= 0 ) {
sy = height ? ( height - dy ) / height : 1 ;
2018-05-16 00:53:27 +00:00
ty = height ;
}
// if we dragging on the east side, then adjust the scale factor and tx
2018-05-16 08:32:44 +00:00
if ( currentResizeMode . indexOf ( 'w' ) >= 0 ) {
sx = width ? ( width - dx ) / width : 1 ;
2018-05-16 00:53:27 +00:00
tx = width ;
}
// update the transform list with translate,scale,translate
var translateOrigin = svgroot . createSVGTransform ( ) ,
scale = svgroot . createSVGTransform ( ) ,
translateBack = svgroot . createSVGTransform ( ) ;
if ( curConfig . gridSnapping ) {
left = svgedit . utilities . snapToGrid ( left ) ;
tx = svgedit . utilities . snapToGrid ( tx ) ;
top = svgedit . utilities . snapToGrid ( top ) ;
ty = svgedit . utilities . snapToGrid ( ty ) ;
}
2018-05-16 08:32:44 +00:00
translateOrigin . setTranslate ( - ( left + tx ) , - ( top + ty ) ) ;
2018-05-16 00:53:27 +00:00
if ( evt . shiftKey ) {
2018-05-16 08:32:44 +00:00
if ( sx === 1 ) {
sx = sy ;
} else { sy = sx ; }
2018-05-16 00:53:27 +00:00
}
scale . setScale ( sx , sy ) ;
2018-05-16 08:32:44 +00:00
translateBack . setTranslate ( left + tx , top + ty ) ;
2018-05-16 00:53:27 +00:00
if ( hasMatrix ) {
var diff = angle ? 1 : 0 ;
2018-05-16 08:32:44 +00:00
tlist . replaceItem ( translateOrigin , 2 + diff ) ;
tlist . replaceItem ( scale , 1 + diff ) ;
2018-05-16 00:53:27 +00:00
tlist . replaceItem ( translateBack , Number ( diff ) ) ;
} else {
var N = tlist . numberOfItems ;
2018-05-16 08:32:44 +00:00
tlist . replaceItem ( translateBack , N - 3 ) ;
tlist . replaceItem ( scale , N - 2 ) ;
tlist . replaceItem ( translateOrigin , N - 1 ) ;
2018-05-16 00:53:27 +00:00
}
selectorManager . requestSelector ( selected ) . resize ( ) ;
call ( 'transition' , selectedElements ) ;
break ;
case 'zoom' :
2018-05-16 08:32:44 +00:00
realX *= currentZoom ;
realY *= currentZoom ;
2018-05-16 00:53:27 +00:00
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : Math . min ( rStartX * currentZoom , realX ) ,
'y' : Math . min ( rStartY * currentZoom , realY ) ,
'width' : Math . abs ( realX - rStartX * currentZoom ) ,
'height' : Math . abs ( realY - rStartY * currentZoom )
2018-05-16 00:53:27 +00:00
} , 100 ) ;
break ;
case 'text' :
2018-05-16 08:32:44 +00:00
svgedit . utilities . assignAttributes ( shape , {
2018-05-16 00:53:27 +00:00
'x' : x ,
'y' : y
} , 1000 ) ;
break ;
case 'line' :
if ( curConfig . gridSnapping ) {
x = svgedit . utilities . snapToGrid ( x ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
}
var x2 = x ;
var y2 = y ;
if ( evt . shiftKey ) {
2018-05-16 08:32:44 +00:00
xya = svgedit . math . snapToAngle ( startX , startY , x2 , y2 ) ;
2018-05-16 00:53:27 +00:00
x2 = xya . x ;
y2 = xya . y ;
}
shape . setAttributeNS ( null , 'x2' , x2 ) ;
shape . setAttributeNS ( null , 'y2' , y2 ) ;
break ;
case 'foreignObject' :
// fall through
case 'square' :
// fall through
case 'rect' :
// fall through
case 'image' :
2018-05-16 08:32:44 +00:00
var square = ( currentMode === 'square' ) || evt . shiftKey ,
w = Math . abs ( x - startX ) ,
h = Math . abs ( y - startY ) ,
newX , newY ;
2018-05-16 00:53:27 +00:00
if ( square ) {
w = h = Math . max ( w , h ) ;
2018-05-16 08:32:44 +00:00
newX = startX < x ? startX : startX - w ;
newY = startY < y ? startY : startY - h ;
2018-05-16 00:53:27 +00:00
} else {
2018-05-16 08:32:44 +00:00
newX = Math . min ( startX , x ) ;
newY = Math . min ( startY , y ) ;
2018-05-16 00:53:27 +00:00
}
if ( curConfig . gridSnapping ) {
w = svgedit . utilities . snapToGrid ( w ) ;
h = svgedit . utilities . snapToGrid ( h ) ;
2018-05-16 08:32:44 +00:00
newX = svgedit . utilities . snapToGrid ( newX ) ;
newY = svgedit . utilities . snapToGrid ( newY ) ;
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
svgedit . utilities . assignAttributes ( shape , {
2018-05-16 00:53:27 +00:00
'width' : w ,
'height' : h ,
2018-05-16 08:32:44 +00:00
'x' : newX ,
'y' : newY
} , 1000 ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'circle' :
c = $ ( shape ) . attr ( [ 'cx' , 'cy' ] ) ;
cx = c . cx ;
cy = c . cy ;
2018-05-16 08:32:44 +00:00
var rad = Math . sqrt ( ( x - cx ) * ( x - cx ) + ( y - cy ) * ( y - cy ) ) ;
2018-05-16 00:53:27 +00:00
if ( curConfig . gridSnapping ) {
rad = svgedit . utilities . snapToGrid ( rad ) ;
}
shape . setAttributeNS ( null , 'r' , rad ) ;
break ;
case 'ellipse' :
c = $ ( shape ) . attr ( [ 'cx' , 'cy' ] ) ;
cx = c . cx ;
cy = c . cy ;
if ( curConfig . gridSnapping ) {
x = svgedit . utilities . snapToGrid ( x ) ;
cx = svgedit . utilities . snapToGrid ( cx ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
cy = svgedit . utilities . snapToGrid ( cy ) ;
}
2018-05-16 08:32:44 +00:00
shape . setAttributeNS ( null , 'rx' , Math . abs ( x - cx ) ) ;
var ry = Math . abs ( evt . shiftKey ? ( x - cx ) : ( y - cy ) ) ;
shape . setAttributeNS ( null , 'ry' , ry ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'fhellipse' :
case 'fhrect' :
2018-05-16 08:32:44 +00:00
freehand . minx = Math . min ( realX , freehand . minx ) ;
freehand . maxx = Math . max ( realX , freehand . maxx ) ;
freehand . miny = Math . min ( realY , freehand . miny ) ;
freehand . maxy = Math . max ( realY , freehand . maxy ) ;
// Fallthrough
2018-05-16 00:53:27 +00:00
case 'fhpath' :
2018-05-16 08:32:44 +00:00
// dAttr += + realX + ',' + realY + ' ';
// shape.setAttributeNS(null, 'points', dAttr);
end . x = realX ; end . y = realY ;
2018-05-16 00:53:27 +00:00
if ( controllPoint2 . x && controllPoint2 . y ) {
for ( i = 0 ; i < STEP _COUNT - 1 ; i ++ ) {
parameter = i / STEP _COUNT ;
nextParameter = ( i + 1 ) / STEP _COUNT ;
bSpline = getBsplinePoint ( nextParameter ) ;
nextPos = bSpline ;
bSpline = getBsplinePoint ( parameter ) ;
sumDistance += Math . sqrt ( ( nextPos . x - bSpline . x ) * ( nextPos . x - bSpline . x ) + ( nextPos . y - bSpline . y ) * ( nextPos . y - bSpline . y ) ) ;
if ( sumDistance > THRESHOLD _DIST ) {
2018-05-16 08:32:44 +00:00
dAttr += + bSpline . x + ',' + bSpline . y + ' ' ;
shape . setAttributeNS ( null , 'points' , dAttr ) ;
2018-05-16 00:53:27 +00:00
sumDistance -= THRESHOLD _DIST ;
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
controllPoint2 = { x : controllPoint1 . x , y : controllPoint1 . y } ;
controllPoint1 = { x : start . x , y : start . y } ;
start = { x : end . x , y : end . y } ;
2018-05-16 00:53:27 +00:00
break ;
// update path stretch line coordinates
case 'path' :
// fall through
case 'pathedit' :
2018-05-16 08:32:44 +00:00
x *= currentZoom ;
y *= currentZoom ;
2018-05-16 00:53:27 +00:00
if ( curConfig . gridSnapping ) {
x = svgedit . utilities . snapToGrid ( x ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
2018-05-16 08:32:44 +00:00
startX = svgedit . utilities . snapToGrid ( startX ) ;
startY = svgedit . utilities . snapToGrid ( startY ) ;
2018-05-16 00:53:27 +00:00
}
if ( evt . shiftKey ) {
var path = svgedit . path . path ;
var x1 , y1 ;
if ( path ) {
2018-05-16 08:32:44 +00:00
x1 = path . dragging ? path . dragging [ 0 ] : startX ;
y1 = path . dragging ? path . dragging [ 1 ] : startY ;
2010-06-22 14:52:51 +00:00
} else {
2018-05-16 08:32:44 +00:00
x1 = startX ;
y1 = startY ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
xya = svgedit . math . snapToAngle ( x1 , y1 , x , y ) ;
x = xya . x ;
y = xya . y ;
}
2010-09-16 12:56:09 +00:00
2018-05-16 00:53:27 +00:00
if ( rubberBox && rubberBox . getAttribute ( 'display' ) !== 'none' ) {
2018-05-16 08:32:44 +00:00
realX *= currentZoom ;
realY *= currentZoom ;
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : Math . min ( rStartX * currentZoom , realX ) ,
'y' : Math . min ( rStartY * currentZoom , realY ) ,
'width' : Math . abs ( realX - rStartX * currentZoom ) ,
'height' : Math . abs ( realY - rStartY * currentZoom )
} , 100 ) ;
2018-05-16 00:53:27 +00:00
}
pathActions . mouseMove ( x , y ) ;
2010-09-09 12:46:34 +00:00
2018-05-16 00:53:27 +00:00
break ;
case 'textedit' :
2018-05-16 08:32:44 +00:00
x *= currentZoom ;
y *= currentZoom ;
// if (rubberBox && rubberBox.getAttribute('display') !== 'none') {
// svgedit.utilities.assignAttributes(rubberBox, {
// 'x': Math.min(startX, x),
// 'y': Math.min(startY, y),
// 'width': Math.abs(x - startX),
// 'height': Math.abs(y - startY)
// }, 100);
// }
textActions . mouseMove ( mouseX , mouseY ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'rotate' :
box = svgedit . utilities . getBBox ( selected ) ;
2018-05-16 08:32:44 +00:00
cx = box . x + box . width / 2 ;
cy = box . y + box . height / 2 ;
2018-05-16 00:53:27 +00:00
var m = svgedit . math . getMatrix ( selected ) ,
center = svgedit . math . transformPoint ( cx , cy , m ) ;
cx = center . x ;
cy = center . y ;
2018-05-16 08:32:44 +00:00
angle = ( ( Math . atan2 ( cy - y , cx - x ) * ( 180 / Math . PI ) ) - 90 ) % 360 ;
2018-05-16 00:53:27 +00:00
if ( curConfig . gridSnapping ) {
angle = svgedit . utilities . snapToGrid ( angle ) ;
}
if ( evt . shiftKey ) { // restrict rotations to nice angles (WRS)
var snap = 45 ;
2018-05-16 08:32:44 +00:00
angle = Math . round ( angle / snap ) * snap ;
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
canvas . setRotationAngle ( angle < - 180 ? ( 360 + angle ) : angle , true ) ;
2018-05-16 00:53:27 +00:00
call ( 'transition' , selectedElements ) ;
break ;
default :
break ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
runExtensions ( 'mouseMove' , {
2010-06-22 14:52:51 +00:00
event : evt ,
2018-05-16 08:32:44 +00:00
mouse _x : mouseX ,
mouse _y : mouseY ,
2010-06-22 14:52:51 +00:00
selected : selected
} ) ;
} ; // mouseMove()
2018-05-16 00:53:27 +00:00
2010-06-22 18:17:42 +00:00
// - in create mode, the element's opacity is set properly, we create an InsertElementCommand
2014-01-31 10:40:52 +00:00
// and store it on the Undo stack
2010-06-22 18:17:42 +00:00
// - in move/resize mode, the element's attributes which were affected by the move/resize are
2014-01-31 10:40:52 +00:00
// identified, a ChangeElementCommand is created and stored on the stack for those attrs
// this is done in when we recalculate the selected dimensions()
2018-05-16 08:32:44 +00:00
var mouseUp = function ( evt ) {
if ( evt . button === 2 ) { return ; }
2010-06-22 14:52:51 +00:00
var tempJustSelected = justSelected ;
justSelected = null ;
2018-05-16 08:32:44 +00:00
if ( ! started ) { return ; }
var pt = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ,
mouseX = pt . x * currentZoom ,
mouseY = pt . y * currentZoom ,
x = mouseX / currentZoom ,
y = mouseY / currentZoom ,
2013-02-14 15:19:46 +00:00
element = svgedit . utilities . getElem ( getId ( ) ) ,
2010-06-22 14:52:51 +00:00
keep = false ;
2010-09-09 12:46:34 +00:00
2018-05-16 08:32:44 +00:00
var realX = x ;
var realY = y ;
2010-11-07 20:14:05 +00:00
2010-10-19 17:20:28 +00:00
// TODO: Make true when in multi-unit mode
var useUnit = false ; // (curConfig.baseUnit !== 'px');
2010-06-22 14:52:51 +00:00
started = false ;
2014-02-11 13:32:40 +00:00
var attrs , t ;
2018-05-16 08:32:44 +00:00
switch ( currentMode ) {
2018-05-16 00:53:27 +00:00
// intentionally fall-through to select here
case 'resize' :
case 'multiselect' :
if ( rubberBox != null ) {
rubberBox . setAttribute ( 'display' , 'none' ) ;
curBBoxes = [ ] ;
}
2018-05-16 08:32:44 +00:00
currentMode = 'select' ;
// Fallthrough
2018-05-16 00:53:27 +00:00
case 'select' :
if ( selectedElements [ 0 ] != null ) {
// if we only have one selected element
if ( selectedElements [ 1 ] == null ) {
// set our current stroke/fill properties to the element's
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
switch ( selected . tagName ) {
2018-05-16 00:53:27 +00:00
case 'g' :
case 'use' :
case 'image' :
case 'foreignObject' :
break ;
default :
2018-05-16 08:32:44 +00:00
curProperties . fill = selected . getAttribute ( 'fill' ) ;
curProperties . fill _opacity = selected . getAttribute ( 'fill-opacity' ) ;
curProperties . stroke = selected . getAttribute ( 'stroke' ) ;
curProperties . stroke _opacity = selected . getAttribute ( 'stroke-opacity' ) ;
curProperties . stroke _width = selected . getAttribute ( 'stroke-width' ) ;
curProperties . stroke _dasharray = selected . getAttribute ( 'stroke-dasharray' ) ;
curProperties . stroke _linejoin = selected . getAttribute ( 'stroke-linejoin' ) ;
curProperties . stroke _linecap = selected . getAttribute ( 'stroke-linecap' ) ;
2018-05-16 00:53:27 +00:00
}
2011-03-09 20:00:49 +00:00
2018-05-16 08:32:44 +00:00
if ( selected . tagName === 'text' ) {
curText . font _size = selected . getAttribute ( 'font-size' ) ;
curText . font _family = selected . getAttribute ( 'font-family' ) ;
2010-02-03 14:50:48 +00:00
}
2018-05-16 00:53:27 +00:00
selectorManager . requestSelector ( selected ) . showGrips ( true ) ;
// This shouldn't be necessary as it was done on mouseDown...
// call('selected', [selected]);
}
// always recalculate dimensions to strip off stray identity transforms
recalculateAllSelectedDimensions ( ) ;
// if it was being dragged/resized
2018-05-16 08:32:44 +00:00
if ( realX !== rStartX || realY !== rStartY ) {
2018-05-16 00:53:27 +00:00
var i , len = selectedElements . length ;
for ( i = 0 ; i < len ; ++ i ) {
2018-05-16 08:32:44 +00:00
if ( selectedElements [ i ] == null ) { break ; }
2018-05-16 00:53:27 +00:00
if ( ! selectedElements [ i ] . firstChild ) {
// Not needed for groups (incorrectly resizes elems), possibly not needed at all?
selectorManager . requestSelector ( selectedElements [ i ] ) . resize ( ) ;
2010-06-22 14:52:51 +00:00
}
2009-12-30 18:06:29 +00:00
}
2018-05-16 00:53:27 +00:00
// no change in position/size, so maybe we should move to pathedit
2018-05-16 08:32:44 +00:00
} else {
2018-05-16 00:53:27 +00:00
t = evt . target ;
if ( selectedElements [ 0 ] . nodeName === 'path' && selectedElements [ 1 ] == null ) {
pathActions . select ( selectedElements [ 0 ] ) ;
2018-05-16 08:32:44 +00:00
// if it was a path
2018-05-16 00:53:27 +00:00
// else, if it was selected and this is a shift-click, remove it from selection
2018-05-16 08:32:44 +00:00
} else if ( evt . shiftKey ) {
if ( tempJustSelected !== t ) {
2018-05-16 00:53:27 +00:00
canvas . removeFromSelection ( [ t ] ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
}
} // no change in mouse position
// Remove non-scaling stroke
if ( svgedit . browser . supportsNonScalingStroke ( ) ) {
var elem = selectedElements [ 0 ] ;
if ( elem ) {
elem . removeAttribute ( 'style' ) ;
2018-05-16 08:32:44 +00:00
svgedit . utilities . walkTree ( elem , function ( elem ) {
2010-10-11 17:16:10 +00:00
elem . removeAttribute ( 'style' ) ;
2018-05-16 00:53:27 +00:00
} ) ;
2010-10-11 17:16:10 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
}
return ;
case 'zoom' :
if ( rubberBox != null ) {
rubberBox . setAttribute ( 'display' , 'none' ) ;
}
var factor = evt . shiftKey ? 0.5 : 2 ;
call ( 'zoomed' , {
2018-05-16 08:32:44 +00:00
'x' : Math . min ( rStartX , realX ) ,
'y' : Math . min ( rStartY , realY ) ,
'width' : Math . abs ( realX - rStartX ) ,
'height' : Math . abs ( realY - rStartY ) ,
2018-05-16 00:53:27 +00:00
'factor' : factor
} ) ;
return ;
case 'fhpath' :
// Check that the path contains at least 2 points; a degenerate one-point path
// causes problems.
// Webkit ignores how we set the points attribute with commas and uses space
// to separate all coordinates, see https://bugs.webkit.org/show_bug.cgi?id=29870
sumDistance = 0 ;
2018-05-16 08:32:44 +00:00
controllPoint2 = { x : 0 , y : 0 } ;
controllPoint1 = { x : 0 , y : 0 } ;
start = { x : 0 , y : 0 } ;
end = { x : 0 , y : 0 } ;
2018-05-16 00:53:27 +00:00
var coords = element . getAttribute ( 'points' ) ;
var commaIndex = coords . indexOf ( ',' ) ;
if ( commaIndex >= 0 ) {
2018-05-16 08:32:44 +00:00
keep = coords . indexOf ( ',' , commaIndex + 1 ) >= 0 ;
2018-05-16 00:53:27 +00:00
} else {
2018-05-16 08:32:44 +00:00
keep = coords . indexOf ( ' ' , coords . indexOf ( ' ' ) + 1 ) >= 0 ;
2018-05-16 00:53:27 +00:00
}
if ( keep ) {
element = pathActions . smoothPolylineIntoPath ( element ) ;
}
break ;
case 'line' :
attrs = $ ( element ) . attr ( [ 'x1' , 'x2' , 'y1' , 'y2' ] ) ;
2018-05-16 08:32:44 +00:00
keep = ( attrs . x1 !== attrs . x2 || attrs . y1 !== attrs . y2 ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'foreignObject' :
case 'square' :
case 'rect' :
case 'image' :
attrs = $ ( element ) . attr ( [ 'width' , 'height' ] ) ;
// Image should be kept regardless of size (use inherit dimensions later)
2018-05-16 08:32:44 +00:00
keep = ( attrs . width !== '0' || attrs . height !== '0' ) || currentMode === 'image' ;
2018-05-16 00:53:27 +00:00
break ;
case 'circle' :
2018-05-16 08:32:44 +00:00
keep = ( element . getAttribute ( 'r' ) !== '0' ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'ellipse' :
attrs = $ ( element ) . attr ( [ 'rx' , 'ry' ] ) ;
keep = ( attrs . rx != null || attrs . ry != null ) ;
break ;
case 'fhellipse' :
if ( ( freehand . maxx - freehand . minx ) > 0 &&
( freehand . maxy - freehand . miny ) > 0 ) {
element = addSvgElementFromJson ( {
element : 'ellipse' ,
curStyles : true ,
attr : {
cx : ( freehand . minx + freehand . maxx ) / 2 ,
cy : ( freehand . miny + freehand . maxy ) / 2 ,
rx : ( freehand . maxx - freehand . minx ) / 2 ,
ry : ( freehand . maxy - freehand . miny ) / 2 ,
id : getId ( )
}
2010-06-22 14:52:51 +00:00
} ) ;
2018-05-16 08:32:44 +00:00
call ( 'changed' , [ element ] ) ;
2010-06-22 14:52:51 +00:00
keep = true ;
2018-05-16 00:53:27 +00:00
}
break ;
case 'fhrect' :
if ( ( freehand . maxx - freehand . minx ) > 0 &&
( freehand . maxy - freehand . miny ) > 0 ) {
element = addSvgElementFromJson ( {
element : 'rect' ,
curStyles : true ,
attr : {
x : freehand . minx ,
y : freehand . miny ,
width : ( freehand . maxx - freehand . minx ) ,
height : ( freehand . maxy - freehand . miny ) ,
id : getId ( )
}
} ) ;
2018-05-16 08:32:44 +00:00
call ( 'changed' , [ element ] ) ;
2010-06-22 14:52:51 +00:00
keep = true ;
2018-05-16 00:53:27 +00:00
}
break ;
case 'text' :
keep = true ;
selectOnly ( [ element ] ) ;
textActions . start ( element ) ;
break ;
case 'path' :
// set element to null here so that it is not removed nor finalized
element = null ;
// continue to be set to true so that mouseMove happens
started = true ;
2018-05-16 08:32:44 +00:00
var res = pathActions . mouseUp ( evt , element , mouseX , mouseY ) ;
2018-05-16 00:53:27 +00:00
element = res . element ;
keep = res . keep ;
break ;
case 'pathedit' :
keep = true ;
element = null ;
pathActions . mouseUp ( evt ) ;
break ;
case 'textedit' :
keep = false ;
element = null ;
2018-05-16 08:32:44 +00:00
textActions . mouseUp ( evt , mouseX , mouseY ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'rotate' :
keep = true ;
element = null ;
2018-05-16 08:32:44 +00:00
currentMode = 'select' ;
2018-05-16 00:53:27 +00:00
var batchCmd = canvas . undoMgr . finishUndoableChange ( ) ;
if ( ! batchCmd . isEmpty ( ) ) {
addCommandToHistory ( batchCmd ) ;
}
// perform recalculation to weed out any stray identity transforms that might get stuck
recalculateAllSelectedDimensions ( ) ;
call ( 'changed' , selectedElements ) ;
break ;
default :
// This could occur in an extension
break ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var extResult = runExtensions ( 'mouseUp' , {
2010-06-22 14:52:51 +00:00
event : evt ,
2018-05-16 08:32:44 +00:00
mouse _x : mouseX ,
mouse _y : mouseY
2010-06-22 14:52:51 +00:00
} , true ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( extResult , function ( i , r ) {
2013-02-15 15:51:58 +00:00
if ( r ) {
2010-06-22 14:52:51 +00:00
keep = r . keep || keep ;
element = r . element ;
started = r . started || started ;
2010-01-18 19:14:08 +00:00
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
if ( ! keep && element != null ) {
2011-01-25 16:15:29 +00:00
getCurrentDrawing ( ) . releaseId ( getId ( ) ) ;
2010-06-22 14:52:51 +00:00
element . parentNode . removeChild ( element ) ;
element = null ;
2018-05-16 00:53:27 +00:00
2014-02-11 13:32:40 +00:00
t = evt . target ;
2018-05-16 00:53:27 +00:00
// if this element is in a group, go up until we reach the top-level group
2010-06-22 14:52:51 +00:00
// just below the layer groups
// TODO: once we implement links, we also would have to check for <a> elements
2018-05-16 08:32:44 +00:00
while ( t . parentNode . parentNode . tagName === 'g' ) {
2010-06-22 14:52:51 +00:00
t = t . parentNode ;
}
2018-05-16 00:53:27 +00:00
// if we are not in the middle of creating a path, and we've clicked on some shape,
2010-06-22 14:52:51 +00:00
// then go to Select mode.
// WebKit returns <div> when the canvas is clicked, Firefox/Opera return <svg>
2018-05-16 08:32:44 +00:00
if ( ( currentMode !== 'path' || ! drawnPath ) &&
t . parentNode . id !== 'selectorParentGroup' &&
t . id !== 'svgcanvas' && t . id !== 'svgroot'
) {
2010-06-22 14:52:51 +00:00
// switch into "select" mode if we've clicked on an element
2014-04-08 03:21:22 +00:00
canvas . setMode ( 'select' ) ;
2010-09-17 20:33:33 +00:00
selectOnly ( [ t ] , true ) ;
2010-06-22 14:52:51 +00:00
}
} else if ( element != null ) {
canvas . addedNew = true ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( useUnit ) { svgedit . units . convertAttrs ( element ) ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var aniDur = 0.2 , cAni ;
if ( opacAni . beginElement && parseFloat ( element . getAttribute ( 'opacity' ) ) !== curShape . opacity ) {
cAni = $ ( opacAni ) . clone ( ) . attr ( {
to : curShape . opacity ,
dur : aniDur
2010-06-22 14:52:51 +00:00
} ) . appendTo ( element ) ;
2010-07-29 15:09:49 +00:00
try {
// Fails in FF4 on foreignObject
2018-05-16 08:32:44 +00:00
cAni [ 0 ] . beginElement ( ) ;
} catch ( e ) { }
2010-06-22 14:52:51 +00:00
} else {
2018-05-16 08:32:44 +00:00
aniDur = 0 ;
2010-04-12 15:59:46 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Ideally this would be done on the endEvent of the animation,
// but that doesn't seem to be supported in Webkit
2018-05-16 08:32:44 +00:00
setTimeout ( function ( ) {
if ( cAni ) { cAni . remove ( ) ; }
element . setAttribute ( 'opacity' , curShape . opacity ) ;
2014-04-08 03:21:22 +00:00
element . setAttribute ( 'style' , 'pointer-events:inherit' ) ;
2010-06-22 14:52:51 +00:00
cleanupElement ( element ) ;
2018-05-16 08:32:44 +00:00
if ( currentMode === 'path' ) {
2010-06-22 14:52:51 +00:00
pathActions . toEditMode ( element ) ;
2013-02-15 15:51:58 +00:00
} else if ( curConfig . selectNew ) {
selectOnly ( [ element ] , true ) ;
2010-06-22 14:52:51 +00:00
}
// we create the insert command that is stored on the stack
// undo means to call cmd.unapply(), redo means to call cmd.apply()
2013-02-17 08:21:07 +00:00
addCommandToHistory ( new svgedit . history . InsertElementCommand ( element ) ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
call ( 'changed' , [ element ] ) ;
} , aniDur * 1000 ) ;
2010-04-12 15:59:46 +00:00
}
2018-05-16 00:53:27 +00:00
2013-02-20 06:29:25 +00:00
startTransform = null ;
2010-06-22 14:52:51 +00:00
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var dblClick = function ( evt ) {
var evtTarget = evt . target ;
var parent = evtTarget . parentNode ;
2018-05-16 00:53:27 +00:00
2010-09-22 18:42:24 +00:00
// Do nothing if already in current group
2018-05-16 08:32:44 +00:00
if ( parent === currentGroup ) { return ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var mouseTarget = getMouseTarget ( evt ) ;
var tagName = mouseTarget . tagName ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( tagName === 'text' && currentMode !== 'textedit' ) {
var pt = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ;
textActions . select ( mouseTarget , pt . x , pt . y ) ;
2010-09-28 16:57:26 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( ( tagName === 'g' || tagName === 'a' ) &&
svgedit . utilities . getRotationAngle ( mouseTarget )
) {
2018-05-16 00:53:27 +00:00
// TODO: Allow method of in-group editing without having to do
2010-10-07 20:20:43 +00:00
// this (similar to editing rotated paths)
2018-05-16 00:53:27 +00:00
2010-10-07 20:20:43 +00:00
// Ungroup and regroup
2018-05-16 08:32:44 +00:00
pushGroupProperties ( mouseTarget ) ;
mouseTarget = selectedElements [ 0 ] ;
2010-10-07 20:20:43 +00:00
clearSelection ( true ) ;
2010-09-22 18:42:24 +00:00
}
2010-09-25 18:24:21 +00:00
// Reset context
2018-05-16 08:32:44 +00:00
if ( currentGroup ) {
2010-09-25 18:24:21 +00:00
leaveContext ( ) ;
}
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ( parent . tagName !== 'g' && parent . tagName !== 'a' ) ||
2011-01-25 16:15:29 +00:00
parent === getCurrentDrawing ( ) . getCurrentLayer ( ) ||
2018-05-16 08:32:44 +00:00
mouseTarget === selectorManager . selectorParentGroup
) {
2010-09-22 18:42:24 +00:00
// Escape from in-group edit
return ;
}
2018-05-16 08:32:44 +00:00
setContext ( mouseTarget ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
// prevent links from being followed in the canvas
2018-05-16 08:32:44 +00:00
var handleLinkInCanvas = function ( e ) {
2010-06-22 14:52:51 +00:00
e . preventDefault ( ) ;
return false ;
} ;
2018-05-16 00:53:27 +00:00
2011-01-17 22:11:35 +00:00
// Added mouseup to the container here.
// TODO(codedread): Figure out why after the Closure compiler, the window mouseup is ignored.
$ ( container ) . mousedown ( mouseDown ) . mousemove ( mouseMove ) . click ( handleLinkInCanvas ) . dblclick ( dblClick ) . mouseup ( mouseUp ) ;
2018-05-16 08:32:44 +00:00
// $(window).mouseup(mouseUp);
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// TODO(rafaelcastrocouto): User preference for shift key and zoom factor
$ ( container ) . bind ( 'mousewheel DOMMouseScroll' , function ( e ) {
// if (!e.shiftKey) { return; }
2010-06-22 14:52:51 +00:00
e . preventDefault ( ) ;
2013-01-16 11:42:18 +00:00
var evt = e . originalEvent ;
2010-06-22 14:52:51 +00:00
2018-05-16 08:32:44 +00:00
rootSctm = $ ( '#svgcontent g' ) [ 0 ] . getScreenCTM ( ) . inverse ( ) ;
var pt = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ;
2013-01-16 11:42:18 +00:00
2010-06-22 14:52:51 +00:00
var bbox = {
'x' : pt . x ,
'y' : pt . y ,
'width' : 0 ,
'height' : 0
} ;
2013-01-16 11:42:18 +00:00
var delta = ( evt . wheelDelta ) ? evt . wheelDelta : ( evt . detail ) ? - evt . detail : 0 ;
2018-05-16 08:32:44 +00:00
if ( ! delta ) { return ; }
2013-01-16 11:42:18 +00:00
2018-05-16 08:32:44 +00:00
bbox . factor = Math . max ( 3 / 4 , Math . min ( 4 / 3 , ( delta ) ) ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
call ( 'zoomed' , bbox ) ;
2010-06-22 14:52:51 +00:00
} ) ;
} ( ) ) ;
2010-04-30 15:06:47 +00:00
2010-06-29 20:43:44 +00:00
// Group: Text edit functions
2010-06-22 18:17:42 +00:00
// Functions relating to editing text elements
2018-05-16 08:32:44 +00:00
textActions = canvas . textActions = ( function ( ) {
2010-09-28 16:57:26 +00:00
var curtext ;
2010-06-22 14:52:51 +00:00
var textinput ;
var cursor ;
var selblock ;
var blinker ;
var chardata = [ ] ;
2018-05-16 08:32:44 +00:00
var textbb ; // , transbb;
2010-06-22 14:52:51 +00:00
var matrix ;
2018-05-16 08:32:44 +00:00
var lastX , lastY ;
var allowDbl ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function setCursor ( index ) {
2014-04-08 03:21:22 +00:00
var empty = ( textinput . value === '' ) ;
2010-07-20 13:35:11 +00:00
$ ( textinput ) . focus ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! arguments . length ) {
if ( empty ) {
2010-06-22 14:52:51 +00:00
index = 0 ;
} else {
2018-05-16 08:32:44 +00:00
if ( textinput . selectionEnd !== textinput . selectionStart ) { return ; }
2010-06-22 14:52:51 +00:00
index = textinput . selectionEnd ;
2010-04-12 15:59:46 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var charbb ;
charbb = chardata [ index ] ;
2013-02-15 15:51:58 +00:00
if ( ! empty ) {
2010-06-22 14:52:51 +00:00
textinput . setSelectionRange ( index , index ) ;
}
2014-04-08 03:21:22 +00:00
cursor = svgedit . utilities . getElem ( 'text_cursor' ) ;
2010-06-22 14:52:51 +00:00
if ( ! cursor ) {
2014-04-08 03:21:22 +00:00
cursor = document . createElementNS ( NS . SVG , 'line' ) ;
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( cursor , {
2014-04-08 03:21:22 +00:00
id : 'text_cursor' ,
stroke : '#333' ,
2010-06-22 14:52:51 +00:00
'stroke-width' : 1
2010-04-12 15:59:46 +00:00
} ) ;
2014-04-08 03:21:22 +00:00
cursor = svgedit . utilities . getElem ( 'selectorParentGroup' ) . appendChild ( cursor ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! blinker ) {
2018-05-16 08:32:44 +00:00
blinker = setInterval ( function ( ) {
2010-06-22 14:52:51 +00:00
var show = ( cursor . getAttribute ( 'display' ) === 'none' ) ;
2018-05-16 08:32:44 +00:00
cursor . setAttribute ( 'display' , show ? 'inline' : 'none' ) ;
2010-06-22 14:52:51 +00:00
} , 600 ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var startPt = ptToScreen ( charbb . x , textbb . y ) ;
var endPt = ptToScreen ( charbb . x , ( textbb . y + textbb . height ) ) ;
2018-05-16 00:53:27 +00:00
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( cursor , {
2018-05-16 08:32:44 +00:00
x1 : startPt . x ,
y1 : startPt . y ,
x2 : endPt . x ,
y2 : endPt . y ,
2010-06-22 14:52:51 +00:00
visibility : 'visible' ,
display : 'inline'
} ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( selblock ) { selblock . setAttribute ( 'd' , '' ) ; }
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function setSelection ( start , end , skipInput ) {
2013-02-15 15:51:58 +00:00
if ( start === end ) {
2010-06-22 14:52:51 +00:00
setCursor ( end ) ;
return ;
}
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! skipInput ) {
2010-06-22 14:52:51 +00:00
textinput . setSelectionRange ( start , end ) ;
2010-04-12 15:59:46 +00:00
}
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
selblock = svgedit . utilities . getElem ( 'text_selectblock' ) ;
2010-06-22 14:52:51 +00:00
if ( ! selblock ) {
2014-04-08 03:21:22 +00:00
selblock = document . createElementNS ( NS . SVG , 'path' ) ;
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( selblock , {
2014-04-08 03:21:22 +00:00
id : 'text_selectblock' ,
fill : 'green' ,
opacity : 0.5 ,
style : 'pointer-events:none'
2010-06-22 14:52:51 +00:00
} ) ;
2014-04-08 03:21:22 +00:00
svgedit . utilities . getElem ( 'selectorParentGroup' ) . appendChild ( selblock ) ;
2010-04-12 15:59:46 +00:00
}
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
var startbb = chardata [ start ] ;
2010-06-22 14:52:51 +00:00
var endbb = chardata [ end ] ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
cursor . setAttribute ( 'visibility' , 'hidden' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var tl = ptToScreen ( startbb . x , textbb . y ) ,
tr = ptToScreen ( startbb . x + ( endbb . x - startbb . x ) , textbb . y ) ,
bl = ptToScreen ( startbb . x , textbb . y + textbb . height ) ,
br = ptToScreen ( startbb . x + ( endbb . x - startbb . x ) , textbb . y + textbb . height ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var dstr = 'M' + tl . x + ',' + tl . y +
' L' + tr . x + ',' + tr . y +
' ' + br . x + ',' + br . y +
' ' + bl . x + ',' + bl . y + 'z' ;
2018-05-16 00:53:27 +00:00
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( selblock , {
2010-06-22 14:52:51 +00:00
d : dstr ,
'display' : 'inline'
} ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function getIndexFromPoint ( mouseX , mouseY ) {
2010-06-22 14:52:51 +00:00
// Position cursor here
var pt = svgroot . createSVGPoint ( ) ;
2018-05-16 08:32:44 +00:00
pt . x = mouseX ;
pt . y = mouseY ;
2010-06-22 14:52:51 +00:00
// No content, so return 0
2018-05-16 08:32:44 +00:00
if ( chardata . length === 1 ) { return 0 ; }
2010-06-22 14:52:51 +00:00
// Determine if cursor should be on left or right of character
var charpos = curtext . getCharNumAtPosition ( pt ) ;
2013-02-15 15:51:58 +00:00
if ( charpos < 0 ) {
2010-06-22 14:52:51 +00:00
// Out of text range, look at mouse coords
charpos = chardata . length - 2 ;
2018-05-16 08:32:44 +00:00
if ( mouseX <= chardata [ 0 ] . x ) {
2010-06-22 14:52:51 +00:00
charpos = 0 ;
}
2013-02-15 15:51:58 +00:00
} else if ( charpos >= chardata . length - 2 ) {
2010-06-22 14:52:51 +00:00
charpos = chardata . length - 2 ;
}
var charbb = chardata [ charpos ] ;
2018-05-16 08:32:44 +00:00
var mid = charbb . x + ( charbb . width / 2 ) ;
if ( mouseX > mid ) {
2010-06-22 14:52:51 +00:00
charpos ++ ;
}
return charpos ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function setCursorFromPoint ( mouseX , mouseY ) {
setCursor ( getIndexFromPoint ( mouseX , mouseY ) ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function setEndSelectionFromPoint ( x , y , apply ) {
2010-06-22 14:52:51 +00:00
var i1 = textinput . selectionStart ;
var i2 = getIndexFromPoint ( x , y ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var start = Math . min ( i1 , i2 ) ;
var end = Math . max ( i1 , i2 ) ;
setSelection ( start , end , ! apply ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function screenToPt ( xIn , yIn ) {
2010-06-22 14:52:51 +00:00
var out = {
2018-05-16 08:32:44 +00:00
x : xIn ,
y : yIn
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
out . x /= currentZoom ;
out . y /= currentZoom ;
2010-04-30 15:06:47 +00:00
2013-02-15 15:51:58 +00:00
if ( matrix ) {
2013-02-14 15:19:46 +00:00
var pt = svgedit . math . transformPoint ( out . x , out . y , matrix . inverse ( ) ) ;
2010-06-22 14:52:51 +00:00
out . x = pt . x ;
out . y = pt . y ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
return out ;
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
function ptToScreen ( xIn , yIn ) {
2010-06-22 14:52:51 +00:00
var out = {
2018-05-16 08:32:44 +00:00
x : xIn ,
y : yIn
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( matrix ) {
2013-02-14 15:19:46 +00:00
var pt = svgedit . math . transformPoint ( out . x , out . y , matrix ) ;
2010-06-22 14:52:51 +00:00
out . x = pt . x ;
out . y = pt . y ;
2010-04-13 15:28:52 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
out . x *= currentZoom ;
out . y *= currentZoom ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
return out ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
/ *
// Not currently in use
function hideCursor ( ) {
2013-02-15 15:51:58 +00:00
if ( cursor ) {
2010-06-22 14:52:51 +00:00
cursor . setAttribute ( 'visibility' , 'hidden' ) ;
2010-04-13 15:28:52 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
* /
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function selectAll ( evt ) {
2010-06-22 14:52:51 +00:00
setSelection ( 0 , curtext . textContent . length ) ;
$ ( this ) . unbind ( evt ) ;
}
2010-04-13 15:28:52 +00:00
2018-05-16 08:32:44 +00:00
function selectWord ( evt ) {
if ( ! allowDbl || ! curtext ) { return ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var ept = svgedit . math . transformPoint ( evt . pageX , evt . pageY , rootSctm ) ,
mouseX = ept . x * currentZoom ,
mouseY = ept . y * currentZoom ;
var pt = screenToPt ( mouseX , mouseY ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var index = getIndexFromPoint ( pt . x , pt . y ) ;
var str = curtext . textContent ;
var first = str . substr ( 0 , index ) . replace ( /[a-z0-9]+$/i , '' ) . length ;
var m = str . substr ( index ) . match ( /^[a-z0-9]+/i ) ;
2018-05-16 08:32:44 +00:00
var last = ( m ? m [ 0 ] . length : 0 ) + index ;
2010-06-22 14:52:51 +00:00
setSelection ( first , last ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Set tripleclick
$ ( evt . target ) . click ( selectAll ) ;
2018-05-16 08:32:44 +00:00
setTimeout ( function ( ) {
2010-06-22 14:52:51 +00:00
$ ( evt . target ) . unbind ( 'click' , selectAll ) ;
} , 300 ) ;
}
return {
2018-05-16 08:32:44 +00:00
select : function ( target , x , y ) {
2010-09-28 16:57:26 +00:00
curtext = target ;
textActions . toEditMode ( x , y ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
start : function ( elem ) {
2010-06-22 14:52:51 +00:00
curtext = elem ;
textActions . toEditMode ( ) ;
} ,
2018-05-16 08:32:44 +00:00
mouseDown : function ( evt , mouseTarget , startX , startY ) {
var pt = screenToPt ( startX , startY ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
textinput . focus ( ) ;
setCursorFromPoint ( pt . x , pt . y ) ;
2018-05-16 08:32:44 +00:00
lastX = startX ;
lastY = startY ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// TODO: Find way to block native selection
} ,
2018-05-16 08:32:44 +00:00
mouseMove : function ( mouseX , mouseY ) {
var pt = screenToPt ( mouseX , mouseY ) ;
2010-06-22 14:52:51 +00:00
setEndSelectionFromPoint ( pt . x , pt . y ) ;
2018-05-16 00:53:27 +00:00
} ,
2018-05-16 08:32:44 +00:00
mouseUp : function ( evt , mouseX , mouseY ) {
var pt = screenToPt ( mouseX , mouseY ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
setEndSelectionFromPoint ( pt . x , pt . y , true ) ;
2018-05-16 00:53:27 +00:00
// TODO: Find a way to make this work: Use transformed BBox instead of evt.target
2018-05-16 08:32:44 +00:00
// if (lastX === mouseX && lastY === mouseY
// && !svgedit.math.rectsIntersect(transbb, {x: pt.x, y: pt.y, width: 0, height: 0})) {
// textActions.toSelectMode(true);
// }
2011-02-23 15:56:14 +00:00
2013-02-15 15:51:58 +00:00
if (
2018-05-16 08:32:44 +00:00
evt . target !== curtext &&
mouseX < lastX + 2 &&
mouseX > lastX - 2 &&
mouseY < lastY + 2 &&
mouseY > lastY - 2
) {
2010-06-22 14:52:51 +00:00
textActions . toSelectMode ( true ) ;
}
} ,
setCursor : setCursor ,
2018-05-16 08:32:44 +00:00
toEditMode : function ( x , y ) {
allowDbl = false ;
currentMode = 'textedit' ;
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( curtext ) . showGrips ( false ) ;
2010-07-20 13:35:11 +00:00
// Make selector group accept clicks
2018-05-16 08:32:44 +00:00
/* var selector = */ selectorManager . requestSelector ( curtext ) ; // Do we need this? Has side effect of setting lock, so keeping for now, but next line wasn't being used
// var sel = selector.selectorRect;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
textActions . init ( ) ;
2010-09-28 16:57:26 +00:00
2010-06-22 14:52:51 +00:00
$ ( curtext ) . css ( 'cursor' , 'text' ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// if (svgedit.browser.supportsEditableText()) {
// curtext.setAttribute('editable', 'simple');
// return;
// }
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! arguments . length ) {
2010-06-22 14:52:51 +00:00
setCursor ( ) ;
} else {
var pt = screenToPt ( x , y ) ;
setCursorFromPoint ( pt . x , pt . y ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
setTimeout ( function ( ) {
allowDbl = true ;
2010-06-22 14:52:51 +00:00
} , 300 ) ;
} ,
2018-05-16 08:32:44 +00:00
toSelectMode : function ( selectElem ) {
currentMode = 'select' ;
2010-06-22 14:52:51 +00:00
clearInterval ( blinker ) ;
blinker = null ;
2018-05-16 08:32:44 +00:00
if ( selblock ) { $ ( selblock ) . attr ( 'display' , 'none' ) ; }
if ( cursor ) { $ ( cursor ) . attr ( 'visibility' , 'hidden' ) ; }
2010-06-22 14:52:51 +00:00
$ ( curtext ) . css ( 'cursor' , 'move' ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( selectElem ) {
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2010-04-12 19:11:35 +00:00
$ ( curtext ) . css ( 'cursor' , 'move' ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
call ( 'selected' , [ curtext ] ) ;
2010-06-30 18:27:36 +00:00
addToSelection ( [ curtext ] , true ) ;
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
if ( curtext && ! curtext . textContent . length ) {
2010-06-22 14:52:51 +00:00
// No content, so delete
canvas . deleteSelectedElements ( ) ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
$ ( textinput ) . blur ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
curtext = false ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// if (svgedit.browser.supportsEditableText()) {
// curtext.removeAttribute('editable');
// }
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
setInputElem : function ( elem ) {
2010-06-22 14:52:51 +00:00
textinput = elem ;
2018-05-16 08:32:44 +00:00
// $(textinput).blur(hideCursor);
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
clear : function ( ) {
if ( currentMode === 'textedit' ) {
2010-06-22 14:52:51 +00:00
textActions . toSelectMode ( ) ;
}
} ,
2018-05-16 08:32:44 +00:00
init : function ( inputElem ) {
if ( ! curtext ) { return ; }
2014-02-11 13:32:40 +00:00
var i , end ;
2018-05-16 08:32:44 +00:00
// if (svgedit.browser.supportsEditableText()) {
// curtext.select();
// return;
// }
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! curtext . parentNode ) {
2010-06-22 14:52:51 +00:00
// Result of the ffClone, need to get correct element
curtext = selectedElements [ 0 ] ;
selectorManager . requestSelector ( curtext ) . showGrips ( false ) ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var str = curtext . textContent ;
var len = str . length ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var xform = curtext . getAttribute ( 'transform' ) ;
2010-04-13 15:52:00 +00:00
2011-02-24 16:13:26 +00:00
textbb = svgedit . utilities . getBBox ( curtext ) ;
2018-05-16 00:53:27 +00:00
2013-02-14 15:19:46 +00:00
matrix = xform ? svgedit . math . getMatrix ( curtext ) : null ;
2010-04-13 15:52:00 +00:00
2014-06-01 21:29:54 +00:00
chardata = [ ] ;
chardata . length = len ;
2010-06-22 14:52:51 +00:00
textinput . focus ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
$ ( curtext ) . unbind ( 'dblclick' , selectWord ) . dblclick ( selectWord ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! len ) {
2018-05-16 08:32:44 +00:00
end = { x : textbb . x + ( textbb . width / 2 ) , width : 0 } ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
for ( i = 0 ; i < len ; i ++ ) {
2010-06-22 14:52:51 +00:00
var start = curtext . getStartPositionOfChar ( i ) ;
2014-02-11 13:32:40 +00:00
end = curtext . getEndPositionOfChar ( i ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! svgedit . browser . supportsGoodTextCharPos ( ) ) {
2018-05-16 08:32:44 +00:00
var offset = canvas . contentW * currentZoom ;
2011-02-23 15:56:14 +00:00
start . x -= offset ;
end . x -= offset ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
start . x /= currentZoom ;
end . x /= currentZoom ;
2011-02-23 15:56:14 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Get a "bbox" equivalent for each character. Uses the
// bbox data of the actual text for y, height purposes
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// TODO: Decide if y, width and height are actually necessary
chardata [ i ] = {
x : start . x ,
y : textbb . y , // start.y?
width : end . x - start . x ,
height : textbb . height
} ;
2010-04-12 15:59:46 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Add a last bbox for cursor at end of text
chardata . push ( {
x : end . x ,
width : 0
} ) ;
setSelection ( textinput . selectionStart , textinput . selectionEnd , true ) ;
2010-04-12 15:59:46 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2014-02-11 13:32:40 +00:00
} ( ) ) ;
2010-06-22 14:52:51 +00:00
2011-02-04 08:02:46 +00:00
// TODO: Migrate all of this code into path.js
2010-06-29 20:43:44 +00:00
// Group: Path edit functions
2010-06-22 18:17:42 +00:00
// Functions relating to editing path elements
2018-05-16 08:32:44 +00:00
pathActions = canvas . pathActions = ( function ( ) {
2010-06-22 14:52:51 +00:00
var subpath = false ;
2018-05-16 08:32:44 +00:00
var currentPath ;
2010-10-27 16:34:15 +00:00
var newPoint , firstCtrl ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function resetD ( p ) {
2014-04-08 03:21:22 +00:00
p . setAttribute ( 'd' , pathActions . convertPath ( p ) ) ;
2010-06-22 14:52:51 +00:00
}
2011-02-04 16:06:25 +00:00
// TODO: Move into path.js
2018-05-16 08:32:44 +00:00
svgedit . path . Path . prototype . endChanges = function ( text ) {
if ( svgedit . browser . isWebkit ( ) ) { resetD ( this . elem ) ; }
2013-02-17 08:21:07 +00:00
var cmd = new svgedit . history . ChangeElementCommand ( this . elem , { d : this . last _d } , text ) ;
2013-02-15 15:51:58 +00:00
addCommandToHistory ( cmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ this . elem ] ) ;
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 08:32:44 +00:00
svgedit . path . Path . prototype . addPtsToSelection = function ( indexes ) {
2014-02-11 13:32:40 +00:00
var i , seg ;
2018-05-16 08:32:44 +00:00
if ( ! $ . isArray ( indexes ) ) { indexes = [ indexes ] ; }
for ( i = 0 ; i < indexes . length ; i ++ ) {
2013-02-15 15:51:58 +00:00
var index = indexes [ i ] ;
2014-02-11 13:32:40 +00:00
seg = this . segs [ index ] ;
2013-02-15 15:51:58 +00:00
if ( seg . ptgrip ) {
2018-05-16 08:32:44 +00:00
if ( this . selected _pts . indexOf ( index ) === - 1 && index >= 0 ) {
2013-02-15 15:51:58 +00:00
this . selected _pts . push ( index ) ;
2010-02-26 18:18:21 +00:00
}
2013-02-15 15:51:58 +00:00
}
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
this . selected _pts . sort ( ) ;
2014-02-11 13:32:40 +00:00
i = this . selected _pts . length ;
2014-06-01 21:29:54 +00:00
var grips = [ ] ;
grips . length = i ;
2013-02-15 15:51:58 +00:00
// Loop through points to be selected and highlight each
while ( i -- ) {
var pt = this . selected _pts [ i ] ;
2014-02-11 13:32:40 +00:00
seg = this . segs [ pt ] ;
2013-02-15 15:51:58 +00:00
seg . select ( true ) ;
grips [ i ] = seg . ptgrip ;
}
// TODO: Correct this:
pathActions . canDeleteNodes = true ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
pathActions . closed _subpath = this . subpathIsClosed ( this . selected _pts [ 0 ] ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
call ( 'selected' , grips ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2018-05-16 08:32:44 +00:00
currentPath = null ;
drawnPath = null ;
var hasMoved = false ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// This function converts a polyline (created by the fh_path tool) into
// a path element and coverts every three line segments into a single bezier
// curve in an attempt to smooth out the free-hand
2018-05-16 08:32:44 +00:00
var smoothPolylineIntoPath = function ( element ) {
2014-02-11 13:32:40 +00:00
var i , points = element . points ;
2010-06-22 14:52:51 +00:00
var N = points . numberOfItems ;
if ( N >= 4 ) {
// loop through every 3 points and convert to a cubic bezier curve segment
2018-05-16 00:53:27 +00:00
//
// NOTE: this is cheating, it means that every 3 points has the potential to
2014-01-31 10:40:52 +00:00
// be a corner instead of treating each point in an equal manner. In general,
2010-06-22 14:52:51 +00:00
// this technique does not look that good.
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// I am open to better ideas!
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// Reading:
// - http://www.efg2.com/Lab/Graphics/Jean-YvesQueinecBezierCurves.htm
// - http://www.codeproject.com/KB/graphics/BezierSpline.aspx?msg=2956963
// - http://www.ian-ko.com/ET_GeoWizards/UserGuide/smooth.htm
// - http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-der.html
var curpos = points . getItem ( 0 ) , prevCtlPt = null ;
var d = [ ] ;
2014-04-08 03:21:22 +00:00
d . push ( [ 'M' , curpos . x , ',' , curpos . y , ' C' ] . join ( '' ) ) ;
2018-05-16 08:32:44 +00:00
for ( i = 1 ; i <= ( N - 4 ) ; i += 3 ) {
2010-06-22 14:52:51 +00:00
var ct1 = points . getItem ( i ) ;
2018-05-16 08:32:44 +00:00
var ct2 = points . getItem ( i + 1 ) ;
var end = points . getItem ( i + 2 ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// if the previous segment had a control point, we want to smooth out
// the control points on both sides
if ( prevCtlPt ) {
2018-05-16 08:32:44 +00:00
var newpts = svgedit . path . smoothControlPoints ( prevCtlPt , ct1 , curpos ) ;
if ( newpts && newpts . length === 2 ) {
var prevArr = d [ d . length - 1 ] . split ( ',' ) ;
2010-06-22 14:52:51 +00:00
prevArr [ 2 ] = newpts [ 0 ] . x ;
prevArr [ 3 ] = newpts [ 0 ] . y ;
2018-05-16 08:32:44 +00:00
d [ d . length - 1 ] = prevArr . join ( ',' ) ;
2010-06-22 14:52:51 +00:00
ct1 = newpts [ 1 ] ;
2010-04-01 20:01:20 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2013-02-15 16:51:48 +00:00
d . push ( [ ct1 . x , ct1 . y , ct2 . x , ct2 . y , end . x , end . y ] . join ( ',' ) ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
curpos = end ;
prevCtlPt = ct2 ;
2009-12-30 18:06:29 +00:00
}
2010-06-22 14:52:51 +00:00
// handle remaining line segments
2014-04-08 03:21:22 +00:00
d . push ( 'L' ) ;
2014-02-11 13:32:40 +00:00
while ( i < N ) {
2010-06-22 14:52:51 +00:00
var pt = points . getItem ( i ) ;
2014-04-08 03:21:22 +00:00
d . push ( [ pt . x , pt . y ] . join ( ',' ) ) ;
2014-02-11 13:32:40 +00:00
i ++ ;
2010-06-22 14:52:51 +00:00
}
2014-04-08 03:21:22 +00:00
d = d . join ( ' ' ) ;
2010-06-22 14:52:51 +00:00
// create new path element
element = addSvgElementFromJson ( {
2014-04-08 03:21:22 +00:00
element : 'path' ,
curStyles : true ,
attr : {
id : getId ( ) ,
d : d ,
fill : 'none'
2010-01-01 15:49:50 +00:00
}
2010-06-22 14:52:51 +00:00
} ) ;
2011-01-26 17:35:28 +00:00
// No need to call "changed", as this is already done under mouseUp
2010-06-22 14:52:51 +00:00
}
return element ;
} ;
2010-02-26 18:18:21 +00:00
2010-06-22 14:52:51 +00:00
return {
2018-05-16 08:32:44 +00:00
mouseDown : function ( evt , mouseTarget , startX , startY ) {
2014-02-11 13:46:39 +00:00
var id ;
2018-05-16 08:32:44 +00:00
if ( currentMode === 'path' ) {
var mouseX = startX ; // Was this meant to work with the other `mouseX`? (was defined globally so adding `var` to at least avoid a global)
var mouseY = startY ; // Was this meant to work with the other `mouseY`? (was defined globally so adding `var` to at least avoid a global)
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var x = mouseX / currentZoom ,
y = mouseY / currentZoom ,
2014-04-08 03:21:22 +00:00
stretchy = svgedit . utilities . getElem ( 'path_stretch_line' ) ;
2018-05-16 00:53:27 +00:00
newPoint = [ x , y ] ;
2018-05-16 08:32:44 +00:00
if ( curConfig . gridSnapping ) {
2013-02-17 04:58:04 +00:00
x = svgedit . utilities . snapToGrid ( x ) ;
y = svgedit . utilities . snapToGrid ( y ) ;
2018-05-16 08:32:44 +00:00
mouseX = svgedit . utilities . snapToGrid ( mouseX ) ;
mouseY = svgedit . utilities . snapToGrid ( mouseY ) ;
2010-09-10 13:23:31 +00:00
}
2010-09-09 12:46:34 +00:00
2010-06-22 14:52:51 +00:00
if ( ! stretchy ) {
2014-04-08 03:21:22 +00:00
stretchy = document . createElementNS ( NS . SVG , 'path' ) ;
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( stretchy , {
2014-04-08 03:21:22 +00:00
id : 'path_stretch_line' ,
stroke : '#22C' ,
'stroke-width' : '0.5' ,
fill : 'none'
2010-06-22 14:52:51 +00:00
} ) ;
2014-04-08 03:21:22 +00:00
stretchy = svgedit . utilities . getElem ( 'selectorParentGroup' ) . appendChild ( stretchy ) ;
2009-12-30 18:06:29 +00:00
}
2014-04-08 03:21:22 +00:00
stretchy . setAttribute ( 'display' , 'inline' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var keep = null ;
2014-02-11 13:46:39 +00:00
var index ;
2010-06-22 14:52:51 +00:00
// if pts array is empty, create path element with M at current point
2018-05-16 08:32:44 +00:00
if ( ! drawnPath ) {
var dAttr = 'M' + x + ',' + y + ' ' ; // Was this meant to work with the other `dAttr`? (was defined globally so adding `var` to at least avoid a global)
drawnPath = addSvgElementFromJson ( {
2014-04-08 03:21:22 +00:00
element : 'path' ,
curStyles : true ,
attr : {
2018-05-16 08:32:44 +00:00
d : dAttr ,
2014-04-08 03:21:22 +00:00
id : getNextId ( ) ,
2018-05-16 08:32:44 +00:00
opacity : curShape . opacity / 2
2010-06-22 14:52:51 +00:00
}
} ) ;
// set stretchy line to first point
2018-05-16 08:32:44 +00:00
stretchy . setAttribute ( 'd' , [ 'M' , mouseX , mouseY , mouseX , mouseY ] . join ( ' ' ) ) ;
2014-02-11 13:46:39 +00:00
index = subpath ? svgedit . path . path . segs . length : 0 ;
2018-05-16 08:32:44 +00:00
svgedit . path . addPointGrip ( index , mouseX , mouseY ) ;
2013-02-15 15:51:58 +00:00
} else {
2010-06-22 14:52:51 +00:00
// determine if we clicked on an existing point
2018-05-16 08:32:44 +00:00
var seglist = drawnPath . pathSegList ;
2010-10-26 13:56:17 +00:00
var i = seglist . numberOfItems ;
2018-05-16 08:32:44 +00:00
var FUZZ = 6 / currentZoom ;
2010-06-22 14:52:51 +00:00
var clickOnPoint = false ;
2013-02-15 15:51:58 +00:00
while ( i ) {
2018-05-16 08:32:44 +00:00
i -- ;
2010-10-26 13:56:17 +00:00
var item = seglist . getItem ( i ) ;
var px = item . x , py = item . y ;
2010-06-22 14:52:51 +00:00
// found a matching point
2018-05-16 08:32:44 +00:00
if ( x >= ( px - FUZZ ) && x <= ( px + FUZZ ) &&
y >= ( py - FUZZ ) && y <= ( py + FUZZ )
) {
2010-06-22 14:52:51 +00:00
clickOnPoint = true ;
break ;
}
2009-12-30 18:06:29 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// get path element that we are in the process of creating
2014-02-11 13:46:39 +00:00
id = getId ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Remove previous path object if previously created
2011-02-23 16:24:07 +00:00
svgedit . path . removePath _ ( id ) ;
2018-05-16 00:53:27 +00:00
2013-02-14 15:19:46 +00:00
var newpath = svgedit . utilities . getElem ( id ) ;
2014-02-11 13:46:39 +00:00
var newseg ;
2018-05-16 08:32:44 +00:00
var sSeg ;
2010-10-26 13:56:17 +00:00
var len = seglist . numberOfItems ;
2010-06-22 14:52:51 +00:00
// if we clicked on an existing point, then we are done this path, commit it
2013-02-15 16:51:48 +00:00
// (i, i+1) are the x,y that were clicked on
2010-06-22 14:52:51 +00:00
if ( clickOnPoint ) {
// if clicked on any other point but the first OR
// the first point was clicked on and there are less than 3 points
// then leave the path open
// otherwise, close the path
2010-10-27 16:34:15 +00:00
if ( i <= 1 && len >= 2 ) {
2010-06-22 14:52:51 +00:00
// Create end segment
2018-05-16 08:32:44 +00:00
var absX = seglist . getItem ( 0 ) . x ;
var absY = seglist . getItem ( 0 ) . y ;
2010-10-27 16:34:15 +00:00
2018-05-16 08:32:44 +00:00
sSeg = stretchy . pathSegList . getItem ( 1 ) ;
if ( sSeg . pathSegType === 4 ) {
newseg = drawnPath . createSVGPathSegLinetoAbs ( absX , absY ) ;
2010-10-27 16:34:15 +00:00
} else {
2018-05-16 08:32:44 +00:00
newseg = drawnPath . createSVGPathSegCurvetoCubicAbs (
absX ,
absY ,
sSeg . x1 / currentZoom ,
sSeg . y1 / currentZoom ,
absX ,
absY
2010-10-27 16:34:15 +00:00
) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var endseg = drawnPath . createSVGPathSegClosePath ( ) ;
2010-10-27 16:34:15 +00:00
seglist . appendItem ( newseg ) ;
seglist . appendItem ( endseg ) ;
2013-02-15 15:51:58 +00:00
} else if ( len < 3 ) {
2010-06-22 14:52:51 +00:00
keep = false ;
return keep ;
}
$ ( stretchy ) . remove ( ) ;
2018-05-16 00:53:27 +00:00
2014-02-11 13:46:39 +00:00
// This will signal to commit the path
2018-05-16 08:32:44 +00:00
// var element = newpath; // Other event handlers define own `element`, so this was probably not meant to interact with them or one which shares state (as there were none); I therefore adding a missing `var` to avoid a global
drawnPath = null ;
2010-06-22 14:52:51 +00:00
started = false ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( subpath ) {
if ( svgedit . path . path . matrix ) {
2013-02-17 04:58:04 +00:00
svgedit . coords . remapElement ( newpath , { } , svgedit . path . path . matrix . inverse ( ) ) ;
2009-12-30 18:06:29 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var newD = newpath . getAttribute ( 'd' ) ;
var origD = $ ( svgedit . path . path . elem ) . attr ( 'd' ) ;
$ ( svgedit . path . path . elem ) . attr ( 'd' , origD + newD ) ;
2010-06-22 14:52:51 +00:00
$ ( newpath ) . remove ( ) ;
2013-02-15 15:51:58 +00:00
if ( svgedit . path . path . matrix ) {
2011-03-07 18:26:12 +00:00
svgedit . path . recalcRotatedPath ( ) ;
2010-03-03 19:26:09 +00:00
}
2011-02-04 08:02:46 +00:00
svgedit . path . path . init ( ) ;
pathActions . toEditMode ( svgedit . path . path . elem ) ;
svgedit . path . path . selectPt ( ) ;
2010-06-22 14:52:51 +00:00
return false ;
}
2010-10-26 13:56:17 +00:00
// else, create a new point, update path element
2018-05-16 08:32:44 +00:00
} else {
2010-06-22 14:52:51 +00:00
// Checks if current target or parents are #svgcontent
2013-02-15 15:51:58 +00:00
if ( ! $ . contains ( container , getMouseTarget ( evt ) ) ) {
2010-06-22 14:52:51 +00:00
// Clicked outside canvas, so don't make point
2014-04-08 03:21:22 +00:00
console . log ( 'Clicked outside canvas' ) ;
2010-06-22 14:52:51 +00:00
return false ;
2009-12-30 18:06:29 +00:00
}
2010-02-12 14:23:55 +00:00
2018-05-16 08:32:44 +00:00
var num = drawnPath . pathSegList . numberOfItems ;
var last = drawnPath . pathSegList . getItem ( num - 1 ) ;
2010-10-26 13:56:17 +00:00
var lastx = last . x , lasty = last . y ;
2010-05-26 15:12:57 +00:00
2013-02-15 16:51:48 +00:00
if ( evt . shiftKey ) {
var xya = svgedit . math . snapToAngle ( lastx , lasty , x , y ) ;
x = xya . x ;
y = xya . y ;
}
2018-05-16 00:53:27 +00:00
2010-10-27 16:34:15 +00:00
// Use the segment defined by stretchy
2018-05-16 08:32:44 +00:00
sSeg = stretchy . pathSegList . getItem ( 1 ) ;
if ( sSeg . pathSegType === 4 ) {
newseg = drawnPath . createSVGPathSegLinetoAbs ( round ( x ) , round ( y ) ) ;
2010-10-27 16:34:15 +00:00
} else {
2018-05-16 08:32:44 +00:00
newseg = drawnPath . createSVGPathSegCurvetoCubicAbs (
2010-10-27 16:34:15 +00:00
round ( x ) ,
round ( y ) ,
2018-05-16 08:32:44 +00:00
sSeg . x1 / currentZoom ,
sSeg . y1 / currentZoom ,
sSeg . x2 / currentZoom ,
sSeg . y2 / currentZoom
2010-10-27 16:34:15 +00:00
) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
drawnPath . pathSegList . appendItem ( newseg ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
x *= currentZoom ;
y *= currentZoom ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// set stretchy line to latest point
2010-10-27 16:34:15 +00:00
stretchy . setAttribute ( 'd' , [ 'M' , x , y , x , y ] . join ( ' ' ) ) ;
2014-02-11 13:46:39 +00:00
index = num ;
2018-05-16 08:32:44 +00:00
if ( subpath ) { index += svgedit . path . path . segs . length ; }
2011-02-04 08:02:46 +00:00
svgedit . path . addPointGrip ( index , x , y ) ;
2009-12-30 18:06:29 +00:00
}
2014-01-31 00:39:35 +00:00
// keep = true;
2010-10-27 16:34:15 +00:00
}
2018-05-16 00:53:27 +00:00
2010-10-27 16:34:15 +00:00
return ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// TODO: Make sure currentPath isn't null at this point
if ( ! svgedit . path . path ) { return ; }
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
svgedit . path . path . storeD ( ) ;
2018-05-16 00:53:27 +00:00
2014-02-11 13:46:39 +00:00
id = evt . target . id ;
2018-05-16 08:32:44 +00:00
var curPt ;
if ( id . substr ( 0 , 14 ) === 'pathpointgrip_' ) {
2010-10-27 16:34:15 +00:00
// Select this point
2018-05-16 08:32:44 +00:00
curPt = svgedit . path . path . cur _pt = parseInt ( id . substr ( 14 ) ) ;
svgedit . path . path . dragging = [ startX , startY ] ;
var seg = svgedit . path . path . segs [ curPt ] ;
2018-05-16 00:53:27 +00:00
// only clear selection if shift is not pressed (otherwise, add
2010-10-27 16:34:15 +00:00
// node to selection)
if ( ! evt . shiftKey ) {
2013-02-15 15:51:58 +00:00
if ( svgedit . path . path . selected _pts . length <= 1 || ! seg . selected ) {
2011-02-04 08:02:46 +00:00
svgedit . path . path . clearSelection ( ) ;
2010-10-27 16:34:15 +00:00
}
2018-05-16 08:32:44 +00:00
svgedit . path . path . addPtsToSelection ( curPt ) ;
2013-02-15 15:51:58 +00:00
} else if ( seg . selected ) {
2018-05-16 08:32:44 +00:00
svgedit . path . path . removePtFromSelection ( curPt ) ;
2010-10-27 16:34:15 +00:00
} else {
2018-05-16 08:32:44 +00:00
svgedit . path . path . addPtsToSelection ( curPt ) ;
2009-12-30 18:06:29 +00:00
}
2018-05-16 08:32:44 +00:00
} else if ( id . indexOf ( 'ctrlpointgrip_' ) === 0 ) {
svgedit . path . path . dragging = [ startX , startY ] ;
2018-05-16 00:53:27 +00:00
2010-10-27 16:34:15 +00:00
var parts = id . split ( '_' ) [ 1 ] . split ( 'c' ) ;
2018-05-16 08:32:44 +00:00
curPt = Number ( parts [ 0 ] ) ;
var ctrlNum = Number ( parts [ 1 ] ) ;
svgedit . path . path . selectPt ( curPt , ctrlNum ) ;
2010-10-27 16:34:15 +00:00
}
// Start selection box
2013-02-15 15:51:58 +00:00
if ( ! svgedit . path . path . dragging ) {
2010-10-27 16:34:15 +00:00
if ( rubberBox == null ) {
rubberBox = selectorManager . getRubberBandBox ( ) ;
}
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( rubberBox , {
2018-05-16 08:32:44 +00:00
'x' : startX * currentZoom ,
'y' : startY * currentZoom ,
'width' : 0 ,
'height' : 0 ,
'display' : 'inline'
2010-10-27 16:34:15 +00:00
} , 100 ) ;
}
} ,
2018-05-16 08:32:44 +00:00
mouseMove : function ( mouseX , mouseY ) {
2010-10-27 16:34:15 +00:00
hasMoved = true ;
2018-05-16 08:32:44 +00:00
if ( currentMode === 'path' ) {
if ( ! drawnPath ) { return ; }
var seglist = drawnPath . pathSegList ;
2010-10-27 16:34:15 +00:00
var index = seglist . numberOfItems - 1 ;
2013-02-15 15:51:58 +00:00
if ( newPoint ) {
2010-10-27 16:34:15 +00:00
// First point
2018-05-16 08:32:44 +00:00
// if (!index) { return; }
2010-10-27 16:34:15 +00:00
// Set control points
2011-02-04 08:02:46 +00:00
var pointGrip1 = svgedit . path . addCtrlGrip ( '1c1' ) ;
var pointGrip2 = svgedit . path . addCtrlGrip ( '0c2' ) ;
2018-05-16 00:53:27 +00:00
2010-10-27 16:34:15 +00:00
// dragging pointGrip1
2018-05-16 08:32:44 +00:00
pointGrip1 . setAttribute ( 'cx' , mouseX ) ;
pointGrip1 . setAttribute ( 'cy' , mouseY ) ;
2010-10-27 16:34:15 +00:00
pointGrip1 . setAttribute ( 'display' , 'inline' ) ;
2018-05-16 08:32:44 +00:00
var ptX = newPoint [ 0 ] ;
var ptY = newPoint [ 1 ] ;
2018-05-16 00:53:27 +00:00
2010-10-27 16:34:15 +00:00
// set curve
2018-05-16 08:32:44 +00:00
// var seg = seglist.getItem(index);
var curX = mouseX / currentZoom ;
var curY = mouseY / currentZoom ;
var altX = ( ptX + ( ptX - curX ) ) ;
var altY = ( ptY + ( ptY - curY ) ) ;
pointGrip2 . setAttribute ( 'cx' , altX * currentZoom ) ;
pointGrip2 . setAttribute ( 'cy' , altY * currentZoom ) ;
2010-10-27 16:34:15 +00:00
pointGrip2 . setAttribute ( 'display' , 'inline' ) ;
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
var ctrlLine = svgedit . path . getCtrlLine ( 1 ) ;
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( ctrlLine , {
2018-05-16 08:32:44 +00:00
x1 : mouseX ,
y1 : mouseY ,
x2 : altX * currentZoom ,
y2 : altY * currentZoom ,
2010-10-27 16:34:15 +00:00
display : 'inline'
} ) ;
2013-02-15 15:51:58 +00:00
if ( index === 0 ) {
2018-05-16 08:32:44 +00:00
firstCtrl = [ mouseX , mouseY ] ;
2010-10-27 16:34:15 +00:00
} else {
var last = seglist . getItem ( index - 1 ) ;
2018-05-16 08:32:44 +00:00
var lastX = last . x ;
var lastY = last . y ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( last . pathSegType === 6 ) {
2018-05-16 08:32:44 +00:00
lastX += ( lastX - last . x2 ) ;
lastY += ( lastY - last . y2 ) ;
2013-02-15 15:51:58 +00:00
} else if ( firstCtrl ) {
2018-05-16 08:32:44 +00:00
lastX = firstCtrl [ 0 ] / currentZoom ;
lastY = firstCtrl [ 1 ] / currentZoom ;
2010-10-27 16:34:15 +00:00
}
2018-05-16 08:32:44 +00:00
svgedit . path . replacePathSeg ( 6 , index , [ ptX , ptY , lastX , lastY , altX , altY ] , drawnPath ) ;
2010-10-27 16:34:15 +00:00
}
} else {
2014-04-08 03:21:22 +00:00
var stretchy = svgedit . utilities . getElem ( 'path_stretch_line' ) ;
2010-10-27 16:34:15 +00:00
if ( stretchy ) {
var prev = seglist . getItem ( index ) ;
2013-02-15 15:51:58 +00:00
if ( prev . pathSegType === 6 ) {
2018-05-16 08:32:44 +00:00
var prevX = prev . x + ( prev . x - prev . x2 ) ;
var prevY = prev . y + ( prev . y - prev . y2 ) ;
svgedit . path . replacePathSeg ( 6 , 1 , [ mouseX , mouseY , prevX * currentZoom , prevY * currentZoom , mouseX , mouseY ] , stretchy ) ;
2013-02-15 15:51:58 +00:00
} else if ( firstCtrl ) {
2018-05-16 08:32:44 +00:00
svgedit . path . replacePathSeg ( 6 , 1 , [ mouseX , mouseY , firstCtrl [ 0 ] , firstCtrl [ 1 ] , mouseX , mouseY ] , stretchy ) ;
2010-10-27 16:34:15 +00:00
} else {
2018-05-16 08:32:44 +00:00
svgedit . path . replacePathSeg ( 4 , 1 , [ mouseX , mouseY ] , stretchy ) ;
2010-10-27 16:34:15 +00:00
}
}
}
return ;
}
// if we are dragging a point, let's move it
2011-02-04 08:02:46 +00:00
if ( svgedit . path . path . dragging ) {
var pt = svgedit . path . getPointFromGrip ( {
x : svgedit . path . path . dragging [ 0 ] ,
y : svgedit . path . path . dragging [ 1 ]
} , svgedit . path . path ) ;
var mpt = svgedit . path . getPointFromGrip ( {
2018-05-16 08:32:44 +00:00
x : mouseX ,
y : mouseY
2011-02-04 08:02:46 +00:00
} , svgedit . path . path ) ;
2018-05-16 08:32:44 +00:00
var diffX = mpt . x - pt . x ;
var diffY = mpt . y - pt . y ;
svgedit . path . path . dragging = [ mouseX , mouseY ] ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( svgedit . path . path . dragctrl ) {
2018-05-16 08:32:44 +00:00
svgedit . path . path . moveCtrl ( diffX , diffY ) ;
2010-10-27 16:34:15 +00:00
} else {
2018-05-16 08:32:44 +00:00
svgedit . path . path . movePts ( diffX , diffY ) ;
2010-10-27 16:34:15 +00:00
}
} else {
2011-02-04 08:02:46 +00:00
svgedit . path . path . selected _pts = [ ] ;
2018-05-16 08:32:44 +00:00
svgedit . path . path . eachSeg ( function ( i ) {
2010-10-27 16:34:15 +00:00
var seg = this ;
2018-05-16 08:32:44 +00:00
if ( ! seg . next && ! seg . prev ) { return ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// var item = seg.item;
2010-10-27 16:34:15 +00:00
var rbb = rubberBox . getBBox ( ) ;
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
var pt = svgedit . path . getGripPt ( seg ) ;
2018-05-16 08:32:44 +00:00
var ptBb = {
2010-10-27 16:34:15 +00:00
x : pt . x ,
y : pt . y ,
width : 0 ,
height : 0
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var sel = svgedit . math . rectsIntersect ( rbb , ptBb ) ;
2010-10-27 16:34:15 +00:00
this . select ( sel ) ;
2018-05-16 08:32:44 +00:00
// Note that addPtsToSelection is not being run
if ( sel ) { svgedit . path . path . selected _pts . push ( seg . index ) ; }
2010-10-27 16:34:15 +00:00
} ) ;
}
2018-05-16 00:53:27 +00:00
} ,
2018-05-16 08:32:44 +00:00
mouseUp : function ( evt , element , mouseX , mouseY ) {
2010-10-27 16:34:15 +00:00
// Create mode
2018-05-16 08:32:44 +00:00
if ( currentMode === 'path' ) {
2010-10-27 16:34:15 +00:00
newPoint = null ;
2018-05-16 08:32:44 +00:00
if ( ! drawnPath ) {
2013-02-14 15:19:46 +00:00
element = svgedit . utilities . getElem ( getId ( ) ) ;
2010-10-27 16:34:15 +00:00
started = false ;
firstCtrl = null ;
}
2010-06-22 14:52:51 +00:00
return {
2010-10-27 16:34:15 +00:00
keep : true ,
2010-06-22 14:52:51 +00:00
element : element
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Edit mode
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
if ( svgedit . path . path . dragging ) {
2018-05-16 08:32:44 +00:00
var lastPt = svgedit . path . path . cur _pt ;
2010-01-19 21:14:53 +00:00
2011-02-04 08:02:46 +00:00
svgedit . path . path . dragging = false ;
svgedit . path . path . dragctrl = false ;
svgedit . path . path . update ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( hasMoved ) {
2014-04-08 03:21:22 +00:00
svgedit . path . path . endChanges ( 'Move path point(s)' ) ;
2018-05-16 00:53:27 +00:00
}
2013-02-15 15:51:58 +00:00
if ( ! evt . shiftKey && ! hasMoved ) {
2018-05-16 08:32:44 +00:00
svgedit . path . path . selectPt ( lastPt ) ;
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
} else if ( rubberBox && rubberBox . getAttribute ( 'display' ) !== 'none' ) {
2010-06-22 14:52:51 +00:00
// Done with multi-node-select
2014-04-08 03:21:22 +00:00
rubberBox . setAttribute ( 'display' , 'none' ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( rubberBox . getAttribute ( 'width' ) <= 2 && rubberBox . getAttribute ( 'height' ) <= 2 ) {
2010-06-22 14:52:51 +00:00
pathActions . toSelectMode ( evt . target ) ;
2010-03-03 19:26:09 +00:00
}
2018-05-16 00:53:27 +00:00
// else, move back to select mode
2010-06-22 14:52:51 +00:00
} else {
pathActions . toSelectMode ( evt . target ) ;
}
hasMoved = false ;
} ,
2018-05-16 08:32:44 +00:00
toEditMode : function ( element ) {
2011-02-23 16:24:07 +00:00
svgedit . path . path = svgedit . path . getPath _ ( element ) ;
2018-05-16 08:32:44 +00:00
currentMode = 'pathedit' ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2011-02-04 08:02:46 +00:00
svgedit . path . path . show ( true ) . update ( ) ;
2011-02-24 16:13:26 +00:00
svgedit . path . path . oldbbox = svgedit . utilities . getBBox ( svgedit . path . path . elem ) ;
2010-06-22 14:52:51 +00:00
subpath = false ;
} ,
2018-05-16 08:32:44 +00:00
toSelectMode : function ( elem ) {
var selPath = ( elem === svgedit . path . path . elem ) ;
currentMode = 'select' ;
2011-02-04 08:02:46 +00:00
svgedit . path . path . show ( false ) ;
2018-05-16 08:32:44 +00:00
currentPath = false ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( svgedit . path . path . matrix ) {
2010-06-22 14:52:51 +00:00
// Rotated, so may need to re-calculate the center
2011-03-07 18:26:12 +00:00
svgedit . path . recalcRotatedPath ( ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( selPath ) {
2014-04-08 03:21:22 +00:00
call ( 'selected' , [ elem ] ) ;
2010-06-30 18:27:36 +00:00
addToSelection ( [ elem ] , true ) ;
2010-06-22 14:52:51 +00:00
}
} ,
2018-05-16 08:32:44 +00:00
addSubPath : function ( on ) {
2013-02-15 15:51:58 +00:00
if ( on ) {
2010-06-22 14:52:51 +00:00
// Internally we go into "path" mode, but in the UI it will
// still appear as if in "pathedit" mode.
2018-05-16 08:32:44 +00:00
currentMode = 'path' ;
2010-06-22 14:52:51 +00:00
subpath = true ;
} else {
pathActions . clear ( true ) ;
2011-02-04 08:02:46 +00:00
pathActions . toEditMode ( svgedit . path . path . elem ) ;
2010-06-22 14:52:51 +00:00
}
} ,
2018-05-16 08:32:44 +00:00
select : function ( target ) {
if ( currentPath === target ) {
2010-06-22 14:52:51 +00:00
pathActions . toEditMode ( target ) ;
2018-05-16 08:32:44 +00:00
currentMode = 'pathedit' ;
// going into pathedit mode
} else {
currentPath = target ;
2018-05-16 00:53:27 +00:00
}
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
reorient : function ( ) {
2010-06-22 14:52:51 +00:00
var elem = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { return ; }
2013-02-14 15:19:46 +00:00
var angle = svgedit . utilities . getRotationAngle ( elem ) ;
2018-05-16 08:32:44 +00:00
if ( angle === 0 ) { return ; }
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Reorient path' ) ;
2010-06-22 14:52:51 +00:00
var changes = {
d : elem . getAttribute ( 'd' ) ,
transform : elem . getAttribute ( 'transform' )
} ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , changes ) ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
this . resetOrientation ( elem ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
2010-02-26 18:18:21 +00:00
2010-06-22 14:52:51 +00:00
// Set matrix to null
2018-05-16 00:53:27 +00:00
svgedit . path . getPath _ ( elem ) . show ( false ) . matrix = null ;
2010-02-26 18:18:21 +00:00
2010-06-22 14:52:51 +00:00
this . clear ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-30 18:27:36 +00:00
addToSelection ( [ elem ] , true ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , selectedElements ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
clear : function ( remove ) {
currentPath = null ;
if ( drawnPath ) {
2013-02-14 15:19:46 +00:00
var elem = svgedit . utilities . getElem ( getId ( ) ) ;
2014-04-08 03:21:22 +00:00
$ ( svgedit . utilities . getElem ( 'path_stretch_line' ) ) . remove ( ) ;
2010-06-22 14:52:51 +00:00
$ ( elem ) . remove ( ) ;
2014-04-08 03:21:22 +00:00
$ ( svgedit . utilities . getElem ( 'pathpointgrip_container' ) ) . find ( '*' ) . attr ( 'display' , 'none' ) ;
2018-05-16 08:32:44 +00:00
drawnPath = firstCtrl = null ;
2010-06-22 14:52:51 +00:00
started = false ;
2018-05-16 08:32:44 +00:00
} else if ( currentMode === 'pathedit' ) {
2010-06-22 14:52:51 +00:00
this . toSelectMode ( ) ;
}
2018-05-16 08:32:44 +00:00
if ( svgedit . path . path ) { svgedit . path . path . init ( ) . show ( false ) ; }
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
resetOrientation : function ( path ) {
if ( path == null || path . nodeName !== 'path' ) { return false ; }
2013-02-14 15:19:46 +00:00
var tlist = svgedit . transformlist . getTransformList ( path ) ;
var m = svgedit . math . transformListToTransform ( tlist ) . matrix ;
2010-06-22 14:52:51 +00:00
tlist . clear ( ) ;
2014-04-08 03:21:22 +00:00
path . removeAttribute ( 'transform' ) ;
2010-06-22 14:52:51 +00:00
var segList = path . pathSegList ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Opera/win/non-EN throws an error here.
// TODO: Find out why!
// Presumed fixed in Opera 10.5, so commented out for now
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// try {
2010-06-22 14:52:51 +00:00
var len = segList . numberOfItems ;
2018-05-16 08:32:44 +00:00
// } catch(err) {
// var fixed_d = pathActions.convertPath(path);
// path.setAttribute('d', fixed_d);
// segList = path.pathSegList;
// var len = segList.numberOfItems;
// }
var i ; // , lastX, lastY;
2014-02-11 13:46:39 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2010-06-22 14:52:51 +00:00
var seg = segList . getItem ( i ) ;
var type = seg . pathSegType ;
2018-05-16 08:32:44 +00:00
if ( type === 1 ) { continue ; }
2010-06-22 14:52:51 +00:00
var pts = [ ] ;
2018-05-16 08:32:44 +00:00
$ . each ( [ '' , 1 , 2 ] , function ( j , n ) {
var x = seg [ 'x' + n ] , y = seg [ 'y' + n ] ;
2013-02-15 15:51:58 +00:00
if ( x !== undefined && y !== undefined ) {
2013-02-14 15:19:46 +00:00
var pt = svgedit . math . transformPoint ( x , y , m ) ;
2010-06-22 14:52:51 +00:00
pts . splice ( pts . length , 0 , pt . x , pt . y ) ;
}
} ) ;
2011-02-04 08:02:46 +00:00
svgedit . path . replacePathSeg ( type , i , pts , path ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
reorientGrads ( path , m ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
zoomChange : function ( ) {
if ( currentMode === 'pathedit' ) {
2011-02-04 08:02:46 +00:00
svgedit . path . path . update ( ) ;
2010-06-22 14:52:51 +00:00
}
} ,
2018-05-16 08:32:44 +00:00
getNodePoint : function ( ) {
var selPt = svgedit . path . path . selected _pts . length ? svgedit . path . path . selected _pts [ 0 ] : 1 ;
2010-06-22 14:52:51 +00:00
2018-05-16 08:32:44 +00:00
var seg = svgedit . path . path . segs [ selPt ] ;
2010-06-22 14:52:51 +00:00
return {
x : seg . item . x ,
y : seg . item . y ,
type : seg . type
} ;
2018-05-16 00:53:27 +00:00
} ,
2018-05-16 08:32:44 +00:00
linkControlPoints : function ( linkPoints ) {
2011-02-04 16:06:25 +00:00
svgedit . path . setLinkControlPoints ( linkPoints ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
clonePathNode : function ( ) {
2011-02-04 08:02:46 +00:00
svgedit . path . path . storeD ( ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var selPts = svgedit . path . path . selected _pts ;
// var segs = svgedit.path.path.segs;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var i = selPts . length ;
2010-06-22 14:52:51 +00:00
var nums = [ ] ;
2010-01-21 20:12:49 +00:00
2013-02-15 15:51:58 +00:00
while ( i -- ) {
2018-05-16 08:32:44 +00:00
var pt = selPts [ i ] ;
2011-02-04 08:02:46 +00:00
svgedit . path . path . addSeg ( pt ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
nums . push ( pt + i ) ;
nums . push ( pt + i + 1 ) ;
}
2011-02-04 08:02:46 +00:00
svgedit . path . path . init ( ) . addPtsToSelection ( nums ) ;
2010-02-26 18:18:21 +00:00
2014-04-08 03:21:22 +00:00
svgedit . path . path . endChanges ( 'Clone path node(s)' ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
opencloseSubPath : function ( ) {
var selPts = svgedit . path . path . selected _pts ;
2010-06-22 14:52:51 +00:00
// Only allow one selected node for now
2018-05-16 08:32:44 +00:00
if ( selPts . length !== 1 ) { return ; }
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
var elem = svgedit . path . path . elem ;
2010-06-22 14:52:51 +00:00
var list = elem . pathSegList ;
2010-03-24 20:13:13 +00:00
2018-05-16 08:32:44 +00:00
// var len = list.numberOfItems;
2010-03-24 20:13:13 +00:00
2018-05-16 08:32:44 +00:00
var index = selPts [ 0 ] ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var openPt = null ;
var startItem = null ;
2010-03-24 20:13:13 +00:00
2010-06-22 14:52:51 +00:00
// Check if subpath is already open
2018-05-16 08:32:44 +00:00
svgedit . path . path . eachSeg ( function ( i ) {
2013-02-15 15:51:58 +00:00
if ( this . type === 2 && i <= index ) {
2018-05-16 08:32:44 +00:00
startItem = this . item ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
if ( i <= index ) { return true ; }
2013-02-15 15:51:58 +00:00
if ( this . type === 2 ) {
2010-06-22 14:52:51 +00:00
// Found M first, so open
2018-05-16 08:32:44 +00:00
openPt = i ;
2010-06-22 14:52:51 +00:00
return false ;
2014-02-11 14:02:48 +00:00
}
if ( this . type === 1 ) {
2010-06-22 14:52:51 +00:00
// Found Z first, so closed
2018-05-16 08:32:44 +00:00
openPt = false ;
2010-06-22 14:52:51 +00:00
return false ;
2010-03-24 20:13:13 +00:00
}
2010-06-22 14:52:51 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( openPt == null ) {
2010-06-22 14:52:51 +00:00
// Single path, so close last seg
2018-05-16 08:32:44 +00:00
openPt = svgedit . path . path . segs . length - 1 ;
2010-06-22 14:52:51 +00:00
}
2010-03-24 20:13:13 +00:00
2018-05-16 08:32:44 +00:00
if ( openPt !== false ) {
2010-06-22 14:52:51 +00:00
// Close this path
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Create a line going to the previous "M"
2018-05-16 08:32:44 +00:00
var newseg = elem . createSVGPathSegLinetoAbs ( startItem . x , startItem . y ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var closer = elem . createSVGPathSegClosePath ( ) ;
2018-05-16 08:32:44 +00:00
if ( openPt === svgedit . path . path . segs . length - 1 ) {
2010-06-22 14:52:51 +00:00
list . appendItem ( newseg ) ;
list . appendItem ( closer ) ;
} else {
2018-05-16 08:32:44 +00:00
svgedit . path . insertItemBefore ( elem , closer , openPt ) ;
svgedit . path . insertItemBefore ( elem , newseg , openPt ) ;
2010-03-24 20:13:13 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
svgedit . path . path . init ( ) . selectPt ( openPt + 1 ) ;
2010-06-22 14:52:51 +00:00
return ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// M 1,1 L 2,2 L 3,3 L 1,1 z // open at 2,2
// M 2,2 L 3,3 L 1,1
2018-05-16 00:53:27 +00:00
// M 1,1 L 2,2 L 1,1 z M 4,4 L 5,5 L6,6 L 5,5 z
// M 1,1 L 2,2 L 1,1 z [M 4,4] L 5,5 L(M)6,6 L 5,5 z
2011-02-04 08:02:46 +00:00
var seg = svgedit . path . path . segs [ index ] ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( seg . mate ) {
2010-06-22 14:52:51 +00:00
list . removeItem ( index ) ; // Removes last "L"
list . removeItem ( index ) ; // Removes the "Z"
2011-02-04 08:02:46 +00:00
svgedit . path . path . init ( ) . selectPt ( index - 1 ) ;
2010-06-22 14:52:51 +00:00
return ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var i , lastM , zSeg ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Find this sub-path's closing point and remove
2018-05-16 08:32:44 +00:00
for ( i = 0 ; i < list . numberOfItems ; i ++ ) {
2010-06-22 14:52:51 +00:00
var item = list . getItem ( i ) ;
2010-03-24 20:13:13 +00:00
2013-02-15 15:51:58 +00:00
if ( item . pathSegType === 2 ) {
2010-06-22 14:52:51 +00:00
// Find the preceding M
2018-05-16 08:32:44 +00:00
lastM = i ;
2013-02-15 15:51:58 +00:00
} else if ( i === index ) {
2010-06-22 14:52:51 +00:00
// Remove it
2018-05-16 08:32:44 +00:00
list . removeItem ( lastM ) ;
2014-01-31 00:39:35 +00:00
// index--;
2013-02-15 15:51:58 +00:00
} else if ( item . pathSegType === 1 && index < i ) {
2010-06-22 14:52:51 +00:00
// Remove the closing seg of this subpath
2018-05-16 08:32:44 +00:00
zSeg = i - 1 ;
2010-06-22 14:52:51 +00:00
list . removeItem ( i ) ;
break ;
2010-03-24 20:13:13 +00:00
}
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var num = ( index - lastM ) - 1 ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
while ( num -- ) {
2018-05-16 08:32:44 +00:00
svgedit . path . insertItemBefore ( elem , list . getItem ( lastM ) , zSeg ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var pt = list . getItem ( lastM ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Make this point the new "M"
2018-05-16 08:32:44 +00:00
svgedit . path . replacePathSeg ( 2 , lastM , [ pt . x , pt . y ] ) ;
2018-05-16 00:53:27 +00:00
2014-02-11 14:02:48 +00:00
i = index ; // i is local here, so has no effect; what is the reason for this?
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
svgedit . path . path . init ( ) . selectPt ( 0 ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
deletePathNode : function ( ) {
if ( ! pathActions . canDeleteNodes ) { return ; }
2011-02-04 08:02:46 +00:00
svgedit . path . path . storeD ( ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var selPts = svgedit . path . path . selected _pts ;
var i = selPts . length ;
2010-06-22 14:52:51 +00:00
2013-02-15 15:51:58 +00:00
while ( i -- ) {
2018-05-16 08:32:44 +00:00
var pt = selPts [ i ] ;
2011-02-04 08:02:46 +00:00
svgedit . path . path . deleteSeg ( pt ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Cleanup
2018-05-16 08:32:44 +00:00
var cleanup = function ( ) {
2011-02-04 08:02:46 +00:00
var segList = svgedit . path . path . elem . pathSegList ;
2010-06-22 14:52:51 +00:00
var len = segList . numberOfItems ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var remItems = function ( pos , count ) {
2013-02-15 15:51:58 +00:00
while ( count -- ) {
2010-06-22 14:52:51 +00:00
segList . removeItem ( pos ) ;
}
2013-02-15 15:51:58 +00:00
} ;
2010-01-21 17:38:37 +00:00
2018-05-16 08:32:44 +00:00
if ( len <= 1 ) { return true ; }
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
while ( len -- ) {
2010-06-22 14:52:51 +00:00
var item = segList . getItem ( len ) ;
2013-02-15 15:51:58 +00:00
if ( item . pathSegType === 1 ) {
2018-05-16 08:32:44 +00:00
var prev = segList . getItem ( len - 1 ) ;
var nprev = segList . getItem ( len - 2 ) ;
2013-02-15 15:51:58 +00:00
if ( prev . pathSegType === 2 ) {
2018-05-16 08:32:44 +00:00
remItems ( len - 1 , 2 ) ;
2010-06-22 14:52:51 +00:00
cleanup ( ) ;
break ;
2013-02-15 15:51:58 +00:00
} else if ( nprev . pathSegType === 2 ) {
2018-05-16 08:32:44 +00:00
remItems ( len - 2 , 3 ) ;
2010-06-22 14:52:51 +00:00
cleanup ( ) ;
break ;
2010-03-11 15:27:18 +00:00
}
2013-02-15 15:51:58 +00:00
} else if ( item . pathSegType === 2 ) {
if ( len > 0 ) {
2018-05-16 08:32:44 +00:00
var prevType = segList . getItem ( len - 1 ) . pathSegType ;
2014-01-31 10:40:52 +00:00
// Path has M M
2018-05-16 08:32:44 +00:00
if ( prevType === 2 ) {
remItems ( len - 1 , 1 ) ;
2010-03-11 15:27:18 +00:00
cleanup ( ) ;
break ;
2018-05-16 00:53:27 +00:00
// Entire path ends with Z M
2018-05-16 08:32:44 +00:00
} else if ( prevType === 1 && segList . numberOfItems - 1 === len ) {
2010-06-22 14:52:51 +00:00
remItems ( len , 1 ) ;
2010-03-11 15:27:18 +00:00
cleanup ( ) ;
break ;
}
}
2010-02-26 19:44:41 +00:00
}
2018-05-16 00:53:27 +00:00
}
2010-06-22 14:52:51 +00:00
return false ;
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
cleanup ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Completely delete a path with 1 or 0 segments
2013-02-15 15:51:58 +00:00
if ( svgedit . path . path . elem . pathSegList . numberOfItems <= 1 ) {
2011-02-04 08:02:46 +00:00
pathActions . toSelectMode ( svgedit . path . path . elem ) ;
2010-06-22 14:52:51 +00:00
canvas . deleteSelectedElements ( ) ;
return ;
}
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
svgedit . path . path . init ( ) ;
svgedit . path . path . clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// TODO: Find right way to select point now
2018-05-16 08:32:44 +00:00
// path.selectPt(selPt);
2013-02-15 15:51:58 +00:00
if ( window . opera ) { // Opera repaints incorrectly
2018-05-16 00:53:27 +00:00
var cp = $ ( svgedit . path . path . elem ) ;
2013-02-15 16:51:48 +00:00
cp . attr ( 'd' , cp . attr ( 'd' ) ) ;
2010-06-22 14:52:51 +00:00
}
2014-04-08 03:21:22 +00:00
svgedit . path . path . endChanges ( 'Delete path node(s)' ) ;
2010-06-22 14:52:51 +00:00
} ,
smoothPolylineIntoPath : smoothPolylineIntoPath ,
2018-05-16 08:32:44 +00:00
setSegType : function ( v ) {
2011-02-04 08:02:46 +00:00
svgedit . path . path . setSegType ( v ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
moveNode : function ( attr , newValue ) {
var selPts = svgedit . path . path . selected _pts ;
if ( ! selPts . length ) { return ; }
2018-05-16 00:53:27 +00:00
2011-02-04 08:02:46 +00:00
svgedit . path . path . storeD ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Get first selected point
2018-05-16 08:32:44 +00:00
var seg = svgedit . path . path . segs [ selPts [ 0 ] ] ;
var diff = { x : 0 , y : 0 } ;
2010-06-22 14:52:51 +00:00
diff [ attr ] = newValue - seg . item [ attr ] ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
seg . move ( diff . x , diff . y ) ;
2014-04-08 03:21:22 +00:00
svgedit . path . path . endChanges ( 'Move path point' ) ;
2010-06-22 14:52:51 +00:00
} ,
2018-05-16 08:32:44 +00:00
fixEnd : function ( elem ) {
2010-06-22 14:52:51 +00:00
// Adds an extra segment if the last seg before a Z doesn't end
// at its M point
// M0,0 L0,100 L100,100 z
var segList = elem . pathSegList ;
var len = segList . numberOfItems ;
2018-05-16 08:32:44 +00:00
var i , lastM ;
2014-02-11 14:02:48 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2010-06-22 14:52:51 +00:00
var item = segList . getItem ( i ) ;
2013-02-15 15:51:58 +00:00
if ( item . pathSegType === 2 ) {
2018-05-16 08:32:44 +00:00
lastM = item ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( item . pathSegType === 1 ) {
2018-05-16 08:32:44 +00:00
var prev = segList . getItem ( i - 1 ) ;
if ( prev . x !== lastM . x || prev . y !== lastM . y ) {
2010-06-22 14:52:51 +00:00
// Add an L segment here
2018-05-16 08:32:44 +00:00
var newseg = elem . createSVGPathSegLinetoAbs ( lastM . x , lastM . y ) ;
2011-02-04 08:02:46 +00:00
svgedit . path . insertItemBefore ( elem , newseg , i ) ;
2010-06-22 14:52:51 +00:00
// Can this be done better?
pathActions . fixEnd ( elem ) ;
break ;
2010-02-26 19:44:41 +00:00
}
2010-06-22 14:52:51 +00:00
}
}
2018-05-16 08:32:44 +00:00
if ( svgedit . browser . isWebkit ( ) ) { resetD ( elem ) ; }
2010-06-22 14:52:51 +00:00
} ,
// Convert a path to one with only absolute or relative values
2016-05-04 13:38:29 +00:00
convertPath : svgedit . utilities . convertPath
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 08:32:44 +00:00
} ) ( ) ;
2011-02-04 16:06:25 +00:00
// end pathActions
2010-06-22 14:52:51 +00:00
// Group: Serialization
// Function: removeUnusedDefElems
// Looks at DOM elements inside the <defs> to see if they are referred to,
// removes them from the DOM if they are not.
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// Returns:
// The amount of elements that were removed
2018-05-16 08:32:44 +00:00
var removeUnusedDefElems = this . removeUnusedDefElems = function ( ) {
2014-04-08 03:21:22 +00:00
var defs = svgcontent . getElementsByTagNameNS ( NS . SVG , 'defs' ) ;
2018-05-16 08:32:44 +00:00
if ( ! defs || ! defs . length ) { return 0 ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
// if (!defs.firstChild) { return; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var defelemUses = [ ] ,
2010-06-22 14:52:51 +00:00
numRemoved = 0 ;
var attrs = [ 'fill' , 'stroke' , 'filter' , 'marker-start' , 'marker-mid' , 'marker-end' ] ;
var alen = attrs . length ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var allEls = svgcontent . getElementsByTagNameNS ( NS . SVG , '*' ) ;
var allLen = allEls . length ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
var i , j ;
2018-05-16 08:32:44 +00:00
for ( i = 0 ; i < allLen ; i ++ ) {
var el = allEls [ i ] ;
2014-02-17 06:48:40 +00:00
for ( j = 0 ; j < alen ; j ++ ) {
2013-02-14 15:19:46 +00:00
var ref = svgedit . utilities . getUrlFromAttr ( el . getAttribute ( attrs [ j ] ) ) ;
2013-02-15 15:51:58 +00:00
if ( ref ) {
2018-05-16 08:32:44 +00:00
defelemUses . push ( ref . substr ( 1 ) ) ;
2011-02-02 21:26:41 +00:00
}
2009-12-30 18:06:29 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// gradients can refer to other gradients
2010-08-16 17:53:15 +00:00
var href = getHref ( el ) ;
2010-10-12 15:20:17 +00:00
if ( href && href . indexOf ( '#' ) === 0 ) {
2018-05-16 08:32:44 +00:00
defelemUses . push ( href . substr ( 1 ) ) ;
2009-12-30 18:06:29 +00:00
}
2013-02-15 15:51:58 +00:00
}
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var defelems = $ ( defs ) . find ( 'linearGradient, radialGradient, filter, marker, svg, symbol' ) ;
2014-02-17 06:48:40 +00:00
i = defelems . length ;
2010-06-22 14:52:51 +00:00
while ( i -- ) {
var defelem = defelems [ i ] ;
var id = defelem . id ;
2018-05-16 08:32:44 +00:00
if ( defelemUses . indexOf ( id ) < 0 ) {
2010-10-08 16:34:24 +00:00
// Not found, so remove (but remember)
removedElements [ id ] = defelem ;
2010-06-22 14:52:51 +00:00
defelem . parentNode . removeChild ( defelem ) ;
numRemoved ++ ;
}
}
2011-02-02 21:26:41 +00:00
2010-06-22 14:52:51 +00:00
return numRemoved ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
// Function: svgCanvasToString
2018-05-16 00:53:27 +00:00
// Main function to set up the SVG content for output
2010-06-22 14:52:51 +00:00
//
2018-05-16 00:53:27 +00:00
// Returns:
2010-06-22 14:52:51 +00:00
// String containing the SVG image for output
2018-05-16 08:32:44 +00:00
this . svgCanvasToString = function ( ) {
2010-06-22 14:52:51 +00:00
// keep calling it until there are none to remove
2013-02-15 15:51:58 +00:00
while ( removeUnusedDefElems ( ) > 0 ) { }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
pathActions . clear ( true ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Keep SVG-Edit comment on top
2018-05-16 08:32:44 +00:00
$ . each ( svgcontent . childNodes , function ( i , node ) {
2013-02-15 15:51:58 +00:00
if ( i && node . nodeType === 8 && node . data . indexOf ( 'Created with' ) >= 0 ) {
2010-06-22 14:52:51 +00:00
svgcontent . insertBefore ( node , svgcontent . firstChild ) ;
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-09-22 18:42:24 +00:00
// Move out of in-group editing mode
2018-05-16 08:32:44 +00:00
if ( currentGroup ) {
2010-09-23 19:53:43 +00:00
leaveContext ( ) ;
2018-05-16 08:32:44 +00:00
selectOnly ( [ currentGroup ] ) ;
2010-09-22 18:42:24 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var nakedSvgs = [ ] ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// Unwrap gsvg if it has no special attributes (only id and style)
2018-05-16 08:32:44 +00:00
$ ( svgcontent ) . find ( 'g:data(gsvg)' ) . each ( function ( ) {
2010-07-16 15:46:54 +00:00
var attrs = this . attributes ;
var len = attrs . length ;
2014-02-17 06:48:40 +00:00
var i ;
for ( i = 0 ; i < len ; i ++ ) {
2018-05-16 08:32:44 +00:00
if ( attrs [ i ] . nodeName === 'id' || attrs [ i ] . nodeName === 'style' ) {
2010-07-16 15:46:54 +00:00
len -- ;
}
}
// No significant attributes, so ungroup
2013-02-15 15:51:58 +00:00
if ( len <= 0 ) {
2010-07-16 15:46:54 +00:00
var svg = this . firstChild ;
2018-05-16 08:32:44 +00:00
nakedSvgs . push ( svg ) ;
2010-07-16 15:46:54 +00:00
$ ( this ) . replaceWith ( svg ) ;
}
} ) ;
2010-11-07 05:20:03 +00:00
var output = this . svgToString ( svgcontent , 0 ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// Rewrap gsvg
2018-05-16 08:32:44 +00:00
if ( nakedSvgs . length ) {
$ ( nakedSvgs ) . each ( function ( ) {
2010-07-16 15:46:54 +00:00
groupSvgElem ( this ) ;
} ) ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
return output ;
2010-11-07 05:20:03 +00:00
} ;
2010-06-22 14:52:51 +00:00
// Function: svgToString
// Sub function ran on each SVG element to convert it to a string as desired
2018-05-16 00:53:27 +00:00
//
// Parameters:
2010-06-22 14:52:51 +00:00
// elem - The SVG element to convert
// indent - Integer with the amount of spaces to indent this tag
//
2018-05-16 00:53:27 +00:00
// Returns:
2010-06-22 14:52:51 +00:00
// String with the given element as an SVG tag
2018-05-16 08:32:44 +00:00
this . svgToString = function ( elem , indent ) {
2018-05-16 00:53:27 +00:00
var out = [ ] ,
2013-02-15 15:51:58 +00:00
toXml = svgedit . utilities . toXml ;
2010-11-05 17:00:32 +00:00
var unit = curConfig . baseUnit ;
2018-05-16 08:32:44 +00:00
var unitRe = new RegExp ( '^-?[\\d\\.]+' + unit + '$' ) ;
2010-06-22 14:52:51 +00:00
if ( elem ) {
cleanupElement ( elem ) ;
var attrs = elem . attributes ,
attr ,
i ,
childs = elem . childNodes ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
for ( i = 0 ; i < indent ; i ++ ) { out . push ( ' ' ) ; }
2014-04-08 03:21:22 +00:00
out . push ( '<' ) ; out . push ( elem . nodeName ) ;
2013-02-15 15:51:58 +00:00
if ( elem . id === 'svgcontent' ) {
2010-06-22 14:52:51 +00:00
// Process root element separately
2010-06-30 18:27:36 +00:00
var res = getResolution ( ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var vb = '' ;
2010-10-19 17:20:28 +00:00
// TODO: Allow this by dividing all values by current baseVal
// Note that this also means we should properly deal with this on import
2014-04-08 03:21:22 +00:00
// if (curConfig.baseUnit !== 'px') {
2014-01-31 00:39:35 +00:00
// var unit = curConfig.baseUnit;
// var unit_m = svgedit.units.getTypeMap()[unit];
// res.w = svgedit.units.shortFloat(res.w / unit_m)
// res.h = svgedit.units.shortFloat(res.h / unit_m)
// vb = ' viewBox="' + [0, 0, res.w, res.h].join(' ') + '"';
// res.w += unit;
// res.h += unit;
// }
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
if ( unit !== 'px' ) {
2011-02-02 17:28:43 +00:00
res . w = svgedit . units . convertUnit ( res . w , unit ) + unit ;
res . h = svgedit . units . convertUnit ( res . h , unit ) + unit ;
2010-10-27 18:15:28 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
out . push ( ' width="' + res . w + '" height="' + res . h + '"' + vb + ' xmlns="' + NS . SVG + '"' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var nsuris = { } ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Check elements for namespaces, add if found
2018-05-16 08:32:44 +00:00
$ ( elem ) . find ( '*' ) . andSelf ( ) . each ( function ( ) {
// var el = this;
2013-10-14 01:50:42 +00:00
// for some elements have no attribute
var uri = this . namespaceURI ;
2018-05-16 08:32:44 +00:00
if ( uri && ! nsuris [ uri ] && nsMap [ uri ] && nsMap [ uri ] !== 'xmlns' && nsMap [ uri ] !== 'xml' ) {
2014-01-31 10:40:52 +00:00
nsuris [ uri ] = true ;
2018-05-16 08:32:44 +00:00
out . push ( ' xmlns:' + nsMap [ uri ] + '="' + uri + '"' ) ;
2013-10-14 01:50:42 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( this . attributes , function ( i , attr ) {
2010-06-22 14:52:51 +00:00
var uri = attr . namespaceURI ;
2018-05-16 08:32:44 +00:00
if ( uri && ! nsuris [ uri ] && nsMap [ uri ] !== 'xmlns' && nsMap [ uri ] !== 'xml' ) {
2010-06-22 14:52:51 +00:00
nsuris [ uri ] = true ;
2018-05-16 08:32:44 +00:00
out . push ( ' xmlns:' + nsMap [ uri ] + '="' + uri + '"' ) ;
2010-06-22 14:52:51 +00:00
}
} ) ;
} ) ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
i = attrs . length ;
2018-05-16 08:32:44 +00:00
var attrNames = [ 'width' , 'height' , 'xmlns' , 'x' , 'y' , 'viewBox' , 'id' , 'overflow' ] ;
2010-06-22 14:52:51 +00:00
while ( i -- ) {
attr = attrs . item ( i ) ;
2014-04-09 05:40:22 +00:00
var attrVal = toXml ( attr . value ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Namespaces have already been dealt with, so skip
2018-05-16 08:32:44 +00:00
if ( attr . nodeName . indexOf ( 'xmlns:' ) === 0 ) { continue ; }
2009-12-30 18:06:29 +00:00
2010-06-22 14:52:51 +00:00
// only serialize attributes we don't use internally
2018-05-16 08:32:44 +00:00
if ( attrVal !== '' && attrNames . indexOf ( attr . localName ) === - 1 ) {
2013-02-15 15:51:58 +00:00
if ( ! attr . namespaceURI || nsMap [ attr . namespaceURI ] ) {
2018-05-16 00:53:27 +00:00
out . push ( ' ' ) ;
2014-04-08 03:21:22 +00:00
out . push ( attr . nodeName ) ; out . push ( '="' ) ;
out . push ( attrVal ) ; out . push ( '"' ) ;
2010-06-22 14:52:51 +00:00
}
}
}
2009-12-30 18:06:29 +00:00
} else {
2011-02-02 21:26:41 +00:00
// Skip empty defs
2018-05-16 08:32:44 +00:00
if ( elem . nodeName === 'defs' && ! elem . firstChild ) { return ; }
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var mozAttrs = [ '-moz-math-font-style' , '_moz-math-font-style' ] ;
2014-02-17 06:48:40 +00:00
for ( i = attrs . length - 1 ; i >= 0 ; i -- ) {
2010-06-22 14:52:51 +00:00
attr = attrs . item ( i ) ;
2014-04-09 05:40:22 +00:00
var attrVal = toXml ( attr . value ) ;
2018-05-16 08:32:44 +00:00
// remove bogus attributes added by Gecko
if ( mozAttrs . indexOf ( attr . localName ) >= 0 ) { continue ; }
if ( attrVal !== '' ) {
if ( attrVal . indexOf ( 'pointer-events' ) === 0 ) { continue ; }
if ( attr . localName === 'class' && attrVal . indexOf ( 'se_' ) === 0 ) { continue ; }
2018-05-16 00:53:27 +00:00
out . push ( ' ' ) ;
2018-05-16 08:32:44 +00:00
if ( attr . localName === 'd' ) { attrVal = pathActions . convertPath ( elem , true ) ; }
2013-02-15 15:51:58 +00:00
if ( ! isNaN ( attrVal ) ) {
2011-02-02 17:28:43 +00:00
attrVal = svgedit . units . shortFloat ( attrVal ) ;
2018-05-16 08:32:44 +00:00
} else if ( unitRe . test ( attrVal ) ) {
2011-02-02 17:28:43 +00:00
attrVal = svgedit . units . shortFloat ( attrVal ) + unit ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
// Embed images when saving
2018-05-16 08:32:44 +00:00
if ( saveOptions . apply &&
elem . nodeName === 'image' &&
attr . localName === 'href' &&
saveOptions . images &&
saveOptions . images === 'embed'
) {
2010-06-22 14:52:51 +00:00
var img = encodableImages [ attrVal ] ;
2018-05-16 08:32:44 +00:00
if ( img ) { attrVal = img ; }
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// map various namespaces to our fixed namespace prefixes
// (the default xmlns attribute itself does not get a prefix)
2018-05-16 08:32:44 +00:00
if ( ! attr . namespaceURI || attr . namespaceURI === NS . SVG || nsMap [ attr . namespaceURI ] ) {
2014-04-08 03:21:22 +00:00
out . push ( attr . nodeName ) ; out . push ( '="' ) ;
out . push ( attrVal ) ; out . push ( '"' ) ;
2010-06-22 14:52:51 +00:00
}
}
2009-10-09 15:29:40 +00:00
}
2009-06-01 21:24:30 +00:00
}
2009-12-17 15:12:54 +00:00
2010-06-22 14:52:51 +00:00
if ( elem . hasChildNodes ( ) ) {
2014-04-08 03:21:22 +00:00
out . push ( '>' ) ;
2010-06-22 14:52:51 +00:00
indent ++ ;
var bOneLine = false ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < childs . length ; i ++ ) {
2010-06-22 14:52:51 +00:00
var child = childs . item ( i ) ;
2018-05-16 08:32:44 +00:00
switch ( child . nodeType ) {
2010-06-22 14:52:51 +00:00
case 1 : // element node
2014-04-08 03:21:22 +00:00
out . push ( '\n' ) ;
2010-11-07 05:20:03 +00:00
out . push ( this . svgToString ( childs . item ( i ) , indent ) ) ;
2010-06-22 14:52:51 +00:00
break ;
case 3 : // text node
2014-04-08 03:21:22 +00:00
var str = child . nodeValue . replace ( /^\s+|\s+$/g , '' ) ;
2018-05-16 08:32:44 +00:00
if ( str !== '' ) {
2010-06-22 14:52:51 +00:00
bOneLine = true ;
2014-02-17 06:48:40 +00:00
out . push ( String ( toXml ( str ) ) ) ;
2010-06-22 14:52:51 +00:00
}
break ;
2012-02-27 17:24:03 +00:00
case 4 : // cdata node
2014-04-08 03:21:22 +00:00
out . push ( '\n' ) ;
2018-05-16 08:32:44 +00:00
out . push ( new Array ( indent + 1 ) . join ( ' ' ) ) ;
2014-04-08 03:21:22 +00:00
out . push ( '<![CDATA[' ) ;
2012-02-27 17:24:03 +00:00
out . push ( child . nodeValue ) ;
2014-04-08 03:21:22 +00:00
out . push ( ']]>' ) ;
2012-02-27 17:24:03 +00:00
break ;
2010-06-22 14:52:51 +00:00
case 8 : // comment
2014-04-08 03:21:22 +00:00
out . push ( '\n' ) ;
2018-05-16 08:32:44 +00:00
out . push ( new Array ( indent + 1 ) . join ( ' ' ) ) ;
2014-04-08 03:21:22 +00:00
out . push ( '<!--' ) ;
2010-06-22 14:52:51 +00:00
out . push ( child . data ) ;
2014-04-08 03:21:22 +00:00
out . push ( '-->' ) ;
2010-06-22 14:52:51 +00:00
break ;
} // switch on node type
}
indent -- ;
if ( ! bOneLine ) {
2014-04-08 03:21:22 +00:00
out . push ( '\n' ) ;
2018-05-16 08:32:44 +00:00
for ( i = 0 ; i < indent ; i ++ ) { out . push ( ' ' ) ; }
2010-06-22 14:52:51 +00:00
}
2014-04-08 03:21:22 +00:00
out . push ( '</' ) ; out . push ( elem . nodeName ) ; out . push ( '>' ) ;
2010-06-22 14:52:51 +00:00
} else {
2014-04-08 03:21:22 +00:00
out . push ( '/>' ) ;
2010-06-22 14:52:51 +00:00
}
}
return out . join ( '' ) ;
} ; // end svgToString()
2009-12-30 18:06:29 +00:00
2010-06-22 14:52:51 +00:00
// Function: embedImage
// Converts a given image file to a data URL when possible, then runs a given callback
//
2018-05-16 00:53:27 +00:00
// Parameters:
2010-06-22 14:52:51 +00:00
// val - String with the path/URL of the image
// callback - Optional function to run when image data is found, supplies the
// result (data URL or false) as first parameter.
2018-05-16 08:32:44 +00:00
this . embedImage = function ( val , callback ) {
2010-06-22 14:52:51 +00:00
// load in the image and once it's loaded, get the dimensions
2018-05-16 08:32:44 +00:00
$ ( new Image ( ) ) . load ( function ( ) {
2010-06-22 14:52:51 +00:00
// create a canvas the same size as the raster image
2014-04-08 03:21:22 +00:00
var canvas = document . createElement ( 'canvas' ) ;
2010-06-22 14:52:51 +00:00
canvas . width = this . width ;
canvas . height = this . height ;
// load the raster image into the canvas
2014-04-08 03:21:22 +00:00
canvas . getContext ( '2d' ) . drawImage ( this , 0 , 0 ) ;
2010-06-22 14:52:51 +00:00
// retrieve the data: URL
try {
var urldata = ';svgedit_url=' + encodeURIComponent ( val ) ;
2018-05-16 08:32:44 +00:00
urldata = canvas . toDataURL ( ) . replace ( ';base64' , urldata + ';base64' ) ;
2010-06-22 14:52:51 +00:00
encodableImages [ val ] = urldata ;
2018-05-16 08:32:44 +00:00
} catch ( e ) {
2010-06-22 14:52:51 +00:00
encodableImages [ val ] = false ;
}
2018-05-16 08:32:44 +00:00
lastGoodImgUrl = val ;
if ( callback ) { callback ( encodableImages [ val ] ) ; }
2013-02-15 16:51:48 +00:00
} ) . attr ( 'src' , val ) ;
2013-02-15 15:51:58 +00:00
} ;
2009-06-01 21:24:30 +00:00
2010-07-05 15:38:06 +00:00
// Function: setGoodImage
// Sets a given URL to be a "last good image" URL
2018-05-16 08:32:44 +00:00
this . setGoodImage = function ( val ) {
lastGoodImgUrl = val ;
2013-02-15 15:51:58 +00:00
} ;
2009-06-01 21:24:30 +00:00
2018-05-16 08:32:44 +00:00
this . open = function ( ) {
2010-06-22 14:52:51 +00:00
// Nothing by default, handled by optional widget/extension
} ;
2009-10-13 02:48:27 +00:00
2010-06-22 14:52:51 +00:00
// Function: save
// Serializes the current drawing into SVG XML text and returns it to the 'saved' handler.
2014-01-31 10:40:52 +00:00
// This function also includes the XML prolog. Clients of the SvgCanvas bind their save
2010-06-22 14:52:51 +00:00
// function to the 'saved' event.
//
2018-05-16 00:53:27 +00:00
// Returns:
2010-06-22 14:52:51 +00:00
// Nothing
2018-05-16 08:32:44 +00:00
this . save = function ( opts ) {
2010-06-22 14:52:51 +00:00
// remove the selected outline before serializing
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
// Update save options if provided
2018-05-16 08:32:44 +00:00
if ( opts ) { $ . extend ( saveOptions , opts ) ; }
saveOptions . apply = true ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// no need for doctype, see http://jwatt.org/svg/authoring/#doctype-declaration
2010-11-07 05:20:03 +00:00
var str = this . svgCanvasToString ( ) ;
2014-04-08 03:21:22 +00:00
call ( 'saved' , str ) ;
2010-06-22 14:52:51 +00:00
} ;
2009-08-19 03:42:27 +00:00
2014-05-22 10:21:29 +00:00
function getIssues ( ) {
2014-01-31 10:40:52 +00:00
// remove the selected outline before serializing
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
// Check for known CanVG issues
2010-06-22 14:52:51 +00:00
var issues = [ ] ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Selector and notice
2018-05-16 08:32:44 +00:00
var issueList = {
2010-06-22 14:52:51 +00:00
'feGaussianBlur' : uiStrings . exportNoBlur ,
'foreignObject' : uiStrings . exportNoforeignObject ,
'[stroke-dasharray]' : uiStrings . exportNoDashArray
2009-06-30 06:24:41 +00:00
} ;
2010-06-22 14:52:51 +00:00
var content = $ ( svgcontent ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Add font/text check if Canvas Text API is not implemented
2014-04-08 03:21:22 +00:00
if ( ! ( 'font' in $ ( '<canvas>' ) [ 0 ] . getContext ( '2d' ) ) ) {
2018-05-16 08:32:44 +00:00
issueList . text = uiStrings . exportNoText ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( issueList , function ( sel , descr ) {
2013-02-15 15:51:58 +00:00
if ( content . find ( sel ) . length ) {
2010-06-22 14:52:51 +00:00
issues . push ( descr ) ;
2010-04-20 20:29:30 +00:00
}
2010-06-22 14:52:51 +00:00
} ) ;
2014-05-22 10:21:29 +00:00
return issues ;
}
2010-04-14 17:30:25 +00:00
2014-05-22 10:21:29 +00:00
// Function: rasterExport
2018-05-16 00:53:27 +00:00
// Generates a Data URL based on the current image, then calls "exported"
2014-05-22 10:21:29 +00:00
// with an object including the string, image information, and any issues found
2018-05-16 08:32:44 +00:00
this . rasterExport = function ( imgType , quality , exportWindowName ) {
2014-05-22 10:21:29 +00:00
var mimeType = 'image/' + imgType . toLowerCase ( ) ;
var issues = getIssues ( ) ;
2010-11-07 05:20:03 +00:00
var str = this . svgCanvasToString ( ) ;
2018-05-16 00:53:27 +00:00
2014-06-01 21:29:54 +00:00
svgedit . utilities . buildCanvgCallback ( function ( ) {
var type = imgType || 'PNG' ;
if ( ! $ ( '#export_canvas' ) . length ) {
$ ( '<canvas>' , { id : 'export_canvas' } ) . hide ( ) . appendTo ( 'body' ) ;
}
var c = $ ( '#export_canvas' ) [ 0 ] ;
c . width = svgCanvas . contentW ;
c . height = svgCanvas . contentH ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
canvg ( c , str , { renderCallback : function ( ) {
2014-06-01 21:29:54 +00:00
var dataURLType = ( type === 'ICO' ? 'BMP' : type ) . toLowerCase ( ) ;
var datauri = quality ? c . toDataURL ( 'image/' + dataURLType , quality ) : c . toDataURL ( 'image/' + dataURLType ) ;
2018-04-12 18:00:52 +00:00
if ( c . toBlob ) {
c . toBlob ( function ( blob ) {
var bloburl = svgedit . utilities . createObjectURL ( blob ) ;
call ( 'exported' , { datauri : datauri , bloburl : bloburl , svg : str , issues : issues , type : imgType , mimeType : mimeType , quality : quality , exportWindowName : exportWindowName } ) ;
} , mimeType , quality ) ;
return ;
}
var bloburl = svgedit . utilities . dataURLToObjectURL ( datauri ) ;
call ( 'exported' , { datauri : datauri , bloburl : bloburl , svg : str , issues : issues , type : imgType , mimeType : mimeType , quality : quality , exportWindowName : exportWindowName } ) ;
2014-06-01 21:29:54 +00:00
} } ) ;
} ) ( ) ;
2010-06-22 14:52:51 +00:00
} ;
2014-05-22 10:21:29 +00:00
this . exportPDF = function ( exportWindowName , outputType ) {
var that = this ;
svgedit . utilities . buildJSPDFCallback ( function ( ) {
var res = getResolution ( ) ;
var orientation = res . w > res . h ? 'landscape' : 'portrait' ;
var units = 'pt' ; // curConfig.baseUnit; // We could use baseUnit, but that is presumably not intended for export purposes
var doc = jsPDF ( {
orientation : orientation ,
unit : units ,
format : [ res . w , res . h ]
// , compressPdf: true
} ) ; // Todo: Give options to use predefined jsPDF formats like "a4", etc. from pull-down (with option to keep customizable)
var docTitle = getDocumentTitle ( ) ;
doc . setProperties ( {
2018-05-16 08:32:44 +00:00
title : docTitle / * ,
2014-05-22 10:21:29 +00:00
subject : '' ,
author : '' ,
keywords : '' ,
2018-05-16 08:32:44 +00:00
creator : '' * /
2014-05-22 10:21:29 +00:00
} ) ;
var issues = getIssues ( ) ;
var str = that . svgCanvasToString ( ) ;
2014-06-01 21:29:54 +00:00
doc . addSVG ( str , 0 , 0 ) ;
2014-05-22 10:21:29 +00:00
// doc.output('save'); // Works to open in a new
// window; todo: configure this and other export
// options to optionally work in this manner as
// opposed to opening a new tab
var obj = { svg : str , issues : issues , exportWindowName : exportWindowName } ;
var method = outputType || 'dataurlstring' ;
obj [ method ] = doc . output ( method ) ;
call ( 'exportedPDF' , obj ) ;
} ) ( ) ;
} ;
2010-06-22 14:52:51 +00:00
// Function: getSvgString
// Returns the current drawing as raw SVG XML text.
//
// Returns:
// The current drawing as raw SVG XML text.
2018-05-16 08:32:44 +00:00
this . getSvgString = function ( ) {
saveOptions . apply = false ;
2010-11-07 05:20:03 +00:00
return this . svgCanvasToString ( ) ;
2010-06-22 14:52:51 +00:00
} ;
2011-02-01 07:22:18 +00:00
// Function: randomizeIds
// This function determines whether to use a nonce in the prefix, when
// generating IDs for future documents in SVG-Edit.
2018-05-16 00:53:27 +00:00
//
2014-01-31 10:40:52 +00:00
// Parameters:
// an optional boolean, which, if true, adds a nonce to the prefix. Thus
// svgCanvas.randomizeIds() <==> svgCanvas.randomizeIds(true)
2010-06-22 14:52:51 +00:00
//
// if you're controlling SVG-Edit externally, and want randomized IDs, call
// this BEFORE calling svgCanvas.setSvgString
//
2018-05-16 08:32:44 +00:00
this . randomizeIds = function ( enableRandomization ) {
if ( arguments . length > 0 && enableRandomization === false ) {
2011-02-03 06:45:01 +00:00
svgedit . draw . randomizeIds ( false , getCurrentDrawing ( ) ) ;
2011-02-01 07:22:18 +00:00
} else {
2011-02-03 06:45:01 +00:00
svgedit . draw . randomizeIds ( true , getCurrentDrawing ( ) ) ;
2011-02-01 07:22:18 +00:00
}
} ;
2010-06-22 14:52:51 +00:00
2010-07-16 15:46:54 +00:00
// Function: uniquifyElems
// Ensure each element has a unique ID
//
// Parameters:
// g - The parent element of the tree to give unique IDs
2018-05-16 08:32:44 +00:00
var uniquifyElems = this . uniquifyElems = function ( g ) {
2010-07-16 15:46:54 +00:00
var ids = { } ;
2014-01-31 10:40:52 +00:00
// TODO: Handle markers and connectors. These are not yet re-identified properly
2011-01-12 06:07:28 +00:00
// as their referring elements do not get remapped.
//
// <marker id='se_marker_end_svg_7'/>
// <polyline id='svg_7' se:connector='svg_1 svg_6' marker-end='url(#se_marker_end_svg_7)'/>
2018-05-16 00:53:27 +00:00
//
2011-01-12 06:07:28 +00:00
// Problem #1: if svg_1 gets renamed, we do not update the polyline's se:connector attribute
// Problem #2: if the polyline svg_7 gets renamed, we do not update the marker id nor the polyline's marker-end attribute
2018-05-16 08:32:44 +00:00
var refElems = [ 'filter' , 'linearGradient' , 'pattern' , 'radialGradient' , 'symbol' , 'textPath' , 'use' ] ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
svgedit . utilities . walkTree ( g , function ( n ) {
2010-07-16 15:46:54 +00:00
// if it's an element node
2018-05-16 08:32:44 +00:00
if ( n . nodeType === 1 ) {
2010-07-16 15:46:54 +00:00
// and the element has an ID
if ( n . id ) {
// and we haven't tracked this ID yet
if ( ! ( n . id in ids ) ) {
// add this id to our map
2018-05-16 08:32:44 +00:00
ids [ n . id ] = { elem : null , attrs : [ ] , hrefs : [ ] } ;
2010-07-16 15:46:54 +00:00
}
2014-02-17 06:48:40 +00:00
ids [ n . id ] . elem = n ;
2010-07-16 15:46:54 +00:00
}
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// now search for all attributes on this element that might refer
// to other elements
2018-05-16 08:32:44 +00:00
$ . each ( refAttrs , function ( i , attr ) {
2010-07-16 15:46:54 +00:00
var attrnode = n . getAttributeNode ( attr ) ;
if ( attrnode ) {
// the incoming file has been sanitized, so we should be able to safely just strip off the leading #
2011-01-12 07:34:07 +00:00
var url = svgedit . utilities . getUrlFromAttr ( attrnode . value ) ,
2010-07-16 15:46:54 +00:00
refid = url ? url . substr ( 1 ) : null ;
if ( refid ) {
if ( ! ( refid in ids ) ) {
// add this id to our map
2018-05-16 08:32:44 +00:00
ids [ refid ] = { elem : null , attrs : [ ] , hrefs : [ ] } ;
2010-07-16 15:46:54 +00:00
}
2014-02-17 06:48:40 +00:00
ids [ refid ] . attrs . push ( attrnode ) ;
2010-07-16 15:46:54 +00:00
}
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// check xlink:href now
2011-01-16 16:28:26 +00:00
var href = svgedit . utilities . getHref ( n ) ;
2010-07-16 15:46:54 +00:00
// TODO: what if an <image> or <a> element refers to an element internally?
2018-05-16 08:32:44 +00:00
if ( href && refElems . indexOf ( n . nodeName ) >= 0 ) {
2010-07-16 15:46:54 +00:00
var refid = href . substr ( 1 ) ;
2011-01-12 06:51:34 +00:00
if ( refid ) {
if ( ! ( refid in ids ) ) {
// add this id to our map
2018-05-16 08:32:44 +00:00
ids [ refid ] = { elem : null , attrs : [ ] , hrefs : [ ] } ;
2011-01-12 06:51:34 +00:00
}
2014-02-17 06:48:40 +00:00
ids [ refid ] . hrefs . push ( n ) ;
2010-07-16 15:46:54 +00:00
}
2018-05-16 00:53:27 +00:00
}
2010-07-16 15:46:54 +00:00
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// in ids, we now have a map of ids, elements and attributes, let's re-identify
2014-02-17 06:48:40 +00:00
var oldid ;
for ( oldid in ids ) {
2018-05-16 08:32:44 +00:00
if ( ! oldid ) { continue ; }
2014-02-17 06:48:40 +00:00
var elem = ids [ oldid ] . elem ;
2010-07-16 15:46:54 +00:00
if ( elem ) {
var newid = getNextId ( ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// assign element its new id
elem . id = newid ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// remap all url() attributes
2014-02-17 06:48:40 +00:00
var attrs = ids [ oldid ] . attrs ;
2010-07-16 15:46:54 +00:00
var j = attrs . length ;
while ( j -- ) {
var attr = attrs [ j ] ;
2014-04-08 03:21:22 +00:00
attr . ownerElement . setAttribute ( attr . name , 'url(#' + newid + ')' ) ;
2010-07-16 15:46:54 +00:00
}
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// remap all href attributes
2014-02-17 06:48:40 +00:00
var hreffers = ids [ oldid ] . hrefs ;
2010-07-16 15:46:54 +00:00
var k = hreffers . length ;
while ( k -- ) {
var hreffer = hreffers [ k ] ;
2014-04-08 03:21:22 +00:00
svgedit . utilities . setHref ( hreffer , '#' + newid ) ;
2010-07-16 15:46:54 +00:00
}
}
}
2013-02-15 15:51:58 +00:00
} ;
2010-07-16 15:46:54 +00:00
2011-01-31 18:22:04 +00:00
// Function setUseData
// Assigns reference data for each use element
2018-05-16 08:32:44 +00:00
var setUseData = this . setUseData = function ( parent ) {
2011-02-02 18:24:44 +00:00
var elems = $ ( parent ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( parent . tagName !== 'use' ) {
2011-02-02 18:24:44 +00:00
elems = elems . find ( 'use' ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
elems . each ( function ( ) {
2011-01-31 18:22:04 +00:00
var id = getHref ( this ) . substr ( 1 ) ;
2018-05-16 08:32:44 +00:00
var refElem = svgedit . utilities . getElem ( id ) ;
if ( ! refElem ) { return ; }
$ ( this ) . data ( 'ref' , refElem ) ;
if ( refElem . tagName === 'symbol' || refElem . tagName === 'svg' ) {
$ ( this ) . data ( 'symbol' , refElem ) . data ( 'ref' , refElem ) ;
2011-01-31 18:22:04 +00:00
}
} ) ;
2013-02-15 15:51:58 +00:00
} ;
2011-01-31 18:22:04 +00:00
2010-09-28 18:59:31 +00:00
// Function convertGradients
// Converts gradients from userSpaceOnUse to objectBoundingBox
2018-05-16 08:32:44 +00:00
var convertGradients = this . convertGradients = function ( elem ) {
2010-09-28 20:21:22 +00:00
var elems = $ ( elem ) . find ( 'linearGradient, radialGradient' ) ;
2013-02-15 15:51:58 +00:00
if ( ! elems . length && svgedit . browser . isWebkit ( ) ) {
2010-09-28 20:21:22 +00:00
// Bug in webkit prevents regular *Gradient selector search
2018-05-16 08:32:44 +00:00
elems = $ ( elem ) . find ( '*' ) . filter ( function ( ) {
2010-09-29 18:37:51 +00:00
return ( this . tagName . indexOf ( 'Gradient' ) >= 0 ) ;
2010-09-28 20:21:22 +00:00
} ) ;
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
elems . each ( function ( ) {
2010-09-28 18:59:31 +00:00
var grad = this ;
2013-02-15 15:51:58 +00:00
if ( $ ( grad ) . attr ( 'gradientUnits' ) === 'userSpaceOnUse' ) {
2010-09-28 18:59:31 +00:00
// TODO: Support more than one element with this ref by duplicating parent grad
2012-05-23 10:33:22 +00:00
var elems = $ ( svgcontent ) . find ( '[fill="url(#' + grad . id + ')"],[stroke="url(#' + grad . id + ')"]' ) ;
2018-05-16 08:32:44 +00:00
if ( ! elems . length ) { return ; }
2018-05-16 00:53:27 +00:00
2010-09-28 18:59:31 +00:00
// get object's bounding box
2011-02-24 16:13:26 +00:00
var bb = svgedit . utilities . getBBox ( elems [ 0 ] ) ;
2018-05-16 00:53:27 +00:00
2010-10-13 14:33:20 +00:00
// This will occur if the element is inside a <defs> or a <symbol>,
// in which we shouldn't need to convert anyway.
2018-05-16 08:32:44 +00:00
if ( ! bb ) { return ; }
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( grad . tagName === 'linearGradient' ) {
2018-05-16 08:32:44 +00:00
var gCoords = $ ( grad ) . attr ( [ 'x1' , 'y1' , 'x2' , 'y2' ] ) ;
2018-05-16 00:53:27 +00:00
2010-10-13 14:05:20 +00:00
// If has transform, convert
var tlist = grad . gradientTransform . baseVal ;
2013-02-15 15:51:58 +00:00
if ( tlist && tlist . numberOfItems > 0 ) {
2013-02-14 15:19:46 +00:00
var m = svgedit . math . transformListToTransform ( tlist ) . matrix ;
2018-05-16 08:32:44 +00:00
var pt1 = svgedit . math . transformPoint ( gCoords . x1 , gCoords . y1 , m ) ;
var pt2 = svgedit . math . transformPoint ( gCoords . x2 , gCoords . y2 , m ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
gCoords . x1 = pt1 . x ;
gCoords . y1 = pt1 . y ;
gCoords . x2 = pt2 . x ;
gCoords . y2 = pt2 . y ;
2010-10-13 14:05:20 +00:00
grad . removeAttribute ( 'gradientTransform' ) ;
}
2018-05-16 00:53:27 +00:00
2010-09-28 18:59:31 +00:00
$ ( grad ) . attr ( {
2018-05-16 08:32:44 +00:00
x1 : ( gCoords . x1 - bb . x ) / bb . width ,
y1 : ( gCoords . y1 - bb . y ) / bb . height ,
x2 : ( gCoords . x2 - bb . x ) / bb . width ,
y2 : ( gCoords . y2 - bb . y ) / bb . height
2010-09-28 18:59:31 +00:00
} ) ;
grad . removeAttribute ( 'gradientUnits' ) ;
2014-02-17 06:48:40 +00:00
}
// else {
2018-05-16 08:32:44 +00:00
// Note: radialGradient elements cannot be easily converted
// because userSpaceOnUse will keep circular gradients, while
// objectBoundingBox will x/y scale the gradient according to
// its bbox.
//
// For now we'll do nothing, though we should probably have
// the gradient be updated as the element is moved, as
// inkscape/illustrator do.
//
// var gCoords = $(grad).attr(['cx', 'cy', 'r']);
//
// $(grad).attr({
// cx: (gCoords.cx - bb.x) / bb.width,
// cy: (gCoords.cy - bb.y) / bb.height,
// r: gCoords.r
// });
//
// grad.removeAttribute('gradientUnits');
2014-02-17 06:48:40 +00:00
// }
2010-09-28 18:59:31 +00:00
}
} ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-09-28 18:59:31 +00:00
2010-07-29 15:09:49 +00:00
// Function: convertToGroup
// Converts selected/given <use> or child SVG element to a group
2018-05-16 08:32:44 +00:00
var convertToGroup = this . convertToGroup = function ( elem ) {
2013-02-15 15:51:58 +00:00
if ( ! elem ) {
2010-07-22 19:10:51 +00:00
elem = selectedElements [ 0 ] ;
}
2010-07-29 15:09:49 +00:00
var $elem = $ ( elem ) ;
2013-02-17 08:21:07 +00:00
var batchCmd = new svgedit . history . BatchCommand ( ) ;
2010-07-29 15:09:49 +00:00
var ts ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( $elem . data ( 'gsvg' ) ) {
2010-07-22 19:10:51 +00:00
// Use the gsvg as the new group
var svg = elem . firstChild ;
var pt = $ ( svg ) . attr ( [ 'x' , 'y' ] ) ;
2018-05-16 00:53:27 +00:00
2010-07-22 19:10:51 +00:00
$ ( elem . firstChild . firstChild ) . unwrap ( ) ;
$ ( elem ) . removeData ( 'gsvg' ) ;
2018-05-16 00:53:27 +00:00
2013-02-14 15:19:46 +00:00
var tlist = svgedit . transformlist . getTransformList ( elem ) ;
2010-07-22 19:10:51 +00:00
var xform = svgroot . createSVGTransform ( ) ;
xform . setTranslate ( pt . x , pt . y ) ;
tlist . appendItem ( xform ) ;
2013-02-20 06:29:25 +00:00
svgedit . recalculate . recalculateDimensions ( elem ) ;
2014-04-08 03:21:22 +00:00
call ( 'selected' , [ elem ] ) ;
2013-02-15 15:51:58 +00:00
} else if ( $elem . data ( 'symbol' ) ) {
2010-07-29 15:09:49 +00:00
elem = $elem . data ( 'symbol' ) ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
ts = $elem . attr ( 'transform' ) ;
2013-02-15 16:51:48 +00:00
var pos = $elem . attr ( [ 'x' , 'y' ] ) ;
2010-10-04 16:01:58 +00:00
2011-01-31 18:22:04 +00:00
var vb = elem . getAttribute ( 'viewBox' ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( vb ) {
2011-01-31 18:22:04 +00:00
var nums = vb . split ( ' ' ) ;
pos . x -= + nums [ 0 ] ;
pos . y -= + nums [ 1 ] ;
}
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
// Not ideal, but works
2014-04-08 03:21:22 +00:00
ts += ' translate(' + ( pos . x || 0 ) + ',' + ( pos . y || 0 ) + ')' ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
var prev = $elem . prev ( ) ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
// Remove <use> element
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( $elem [ 0 ] , $elem [ 0 ] . nextSibling , $elem [ 0 ] . parentNode ) ) ;
2010-07-29 15:09:49 +00:00
$elem . remove ( ) ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
// See if other elements reference this symbol
2018-05-16 08:32:44 +00:00
var hasMore = $ ( svgcontent ) . find ( 'use:data(symbol)' ) . length ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var g = svgdoc . createElementNS ( NS . SVG , 'g' ) ;
2010-07-29 17:49:28 +00:00
var childs = elem . childNodes ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
var i ;
for ( i = 0 ; i < childs . length ; i ++ ) {
2010-07-29 15:09:49 +00:00
g . appendChild ( childs [ i ] . cloneNode ( true ) ) ;
}
2018-05-16 00:53:27 +00:00
2010-12-20 20:00:17 +00:00
// Duplicate the gradients for Gecko, since they weren't included in the <symbol>
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isGecko ( ) ) {
2013-02-14 15:19:46 +00:00
var dupeGrads = $ ( svgedit . utilities . findDefs ( ) ) . children ( 'linearGradient,radialGradient,pattern' ) . clone ( ) ;
2010-12-20 20:00:17 +00:00
$ ( g ) . append ( dupeGrads ) ;
}
2018-05-16 00:53:27 +00:00
2011-01-31 18:22:04 +00:00
if ( ts ) {
2014-04-08 03:21:22 +00:00
g . setAttribute ( 'transform' , ts ) ;
2011-01-31 18:22:04 +00:00
}
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
var parent = elem . parentNode ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
uniquifyElems ( g ) ;
2018-05-16 00:53:27 +00:00
2010-12-20 20:00:17 +00:00
// Put the dupe gradients back into <defs> (after uniquifying them)
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isGecko ( ) ) {
2018-05-16 08:32:44 +00:00
$ ( findDefs ( ) ) . append ( $ ( g ) . find ( 'linearGradient,radialGradient,pattern' ) ) ;
2010-12-20 20:00:17 +00:00
}
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
// now give the g itself a new id
g . id = getNextId ( ) ;
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
prev . after ( g ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( parent ) {
2018-05-16 08:32:44 +00:00
if ( ! hasMore ) {
2010-07-29 15:09:49 +00:00
// remove symbol/svg element
2010-11-13 09:58:51 +00:00
var nextSibling = elem . nextSibling ;
2010-07-29 15:09:49 +00:00
parent . removeChild ( elem ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( elem , nextSibling , parent ) ) ;
2010-07-29 15:09:49 +00:00
}
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( g ) ) ;
2010-07-29 15:09:49 +00:00
}
2018-05-16 00:53:27 +00:00
2011-01-31 18:22:04 +00:00
setUseData ( g ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isGecko ( ) ) {
2013-02-14 15:19:46 +00:00
convertGradients ( svgedit . utilities . findDefs ( ) ) ;
2010-12-21 19:36:51 +00:00
} else {
convertGradients ( g ) ;
}
2018-05-16 00:53:27 +00:00
2010-07-29 15:09:49 +00:00
// recalculate dimensions on the top-level children so that unnecessary transforms
// are removed
2018-05-16 08:32:44 +00:00
svgedit . utilities . walkTreePost ( g , function ( n ) {
2013-02-15 15:51:58 +00:00
try {
2013-02-20 06:29:25 +00:00
svgedit . recalculate . recalculateDimensions ( n ) ;
2018-05-16 08:32:44 +00:00
} catch ( e ) {
2013-02-15 15:51:58 +00:00
console . log ( e ) ;
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-08-23 15:33:00 +00:00
// Give ID for any visible element missing one
2018-05-16 08:32:44 +00:00
$ ( g ) . find ( visElems ) . each ( function ( ) {
if ( ! this . id ) { this . id = getNextId ( ) ; }
2010-08-23 15:33:00 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2010-09-17 20:33:33 +00:00
selectOnly ( [ g ] ) ;
2018-05-16 00:53:27 +00:00
2011-02-02 18:24:44 +00:00
var cm = pushGroupProperties ( g , true ) ;
2013-02-15 15:51:58 +00:00
if ( cm ) {
2011-02-02 18:24:44 +00:00
batchCmd . addSubCommand ( cm ) ;
}
2010-07-29 15:09:49 +00:00
addCommandToHistory ( batchCmd ) ;
} else {
console . log ( 'Unexpected element to ungroup:' , elem ) ;
2010-07-22 19:10:51 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2010-07-22 19:10:51 +00:00
2014-01-31 10:40:52 +00:00
//
2010-06-22 14:52:51 +00:00
// Function: setSvgString
// This function sets the current drawing as the input SVG XML.
//
// Parameters:
// xmlString - The SVG as XML text.
2018-02-27 10:21:07 +00:00
// preventUndo - Boolean (defaults to false) indicating if we want to do the
// changes without adding them to the undo stack - e.g. for initializing a
// drawing on page load.
2010-06-22 14:52:51 +00:00
//
// Returns:
// This function returns false if the set was unsuccessful, true otherwise.
2018-05-16 08:32:44 +00:00
this . setSvgString = function ( xmlString , preventUndo ) {
2010-06-22 14:52:51 +00:00
try {
// convert string into XML document
2010-11-05 17:00:32 +00:00
var newDoc = svgedit . utilities . text2xml ( xmlString ) ;
2010-11-07 20:14:05 +00:00
this . prepareSvg ( newDoc ) ;
2010-06-22 14:52:51 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Change Source' ) ;
2010-06-22 14:52:51 +00:00
// remove old svg document
2010-11-13 09:58:51 +00:00
var nextSibling = svgcontent . nextSibling ;
2010-06-22 14:52:51 +00:00
var oldzoom = svgroot . removeChild ( svgcontent ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( oldzoom , nextSibling , svgroot ) ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// set new svg document
2012-02-27 17:24:03 +00:00
// If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode()
2013-02-15 15:51:58 +00:00
if ( svgdoc . adoptNode ) {
2012-02-27 17:24:03 +00:00
svgcontent = svgdoc . adoptNode ( newDoc . documentElement ) ;
2018-05-16 08:32:44 +00:00
} else {
2012-02-27 17:24:03 +00:00
svgcontent = svgdoc . importNode ( newDoc . documentElement , true ) ;
2012-02-26 18:08:34 +00:00
}
2018-05-16 00:53:27 +00:00
2012-02-27 17:24:03 +00:00
svgroot . appendChild ( svgcontent ) ;
2010-08-23 15:33:00 +00:00
var content = $ ( svgcontent ) ;
2018-05-16 00:53:27 +00:00
2011-02-01 07:22:18 +00:00
canvas . current _drawing _ = new svgedit . draw . Drawing ( svgcontent , idprefix ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// retrieve or set the nonce
2011-02-01 07:22:18 +00:00
var nonce = getCurrentDrawing ( ) . getNonce ( ) ;
if ( nonce ) {
2014-04-08 03:21:22 +00:00
call ( 'setnonce' , nonce ) ;
2011-02-01 07:22:18 +00:00
} else {
2014-04-08 03:21:22 +00:00
call ( 'unsetnonce' ) ;
2011-01-14 18:18:29 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// change image href vals if possible
2018-05-16 08:32:44 +00:00
content . find ( 'image' ) . each ( function ( ) {
2010-06-22 14:52:51 +00:00
var image = this ;
2016-05-04 13:38:29 +00:00
svgedit . utilities . preventClickDefault ( image ) ;
2010-08-16 17:53:15 +00:00
var val = getHref ( this ) ;
2013-03-16 14:07:55 +00:00
if ( val ) {
if ( val . indexOf ( 'data:' ) === 0 ) {
// Check if an SVG-edit data URI
var m = val . match ( /svgedit_url=(.*?);/ ) ;
if ( m ) {
var url = decodeURIComponent ( m [ 1 ] ) ;
$ ( new Image ( ) ) . load ( function ( ) {
2013-03-16 14:11:43 +00:00
image . setAttributeNS ( NS . XLINK , 'xlink:href' , url ) ;
2013-03-16 14:07:55 +00:00
} ) . attr ( 'src' , url ) ;
}
2010-06-22 14:52:51 +00:00
}
2013-03-16 14:07:55 +00:00
// Add to encodableImages if it loads
canvas . embedImage ( val ) ;
2010-06-22 14:52:51 +00:00
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// Wrap child SVGs in group elements
2018-05-16 08:32:44 +00:00
content . find ( 'svg' ) . each ( function ( ) {
2010-07-29 19:43:52 +00:00
// Skip if it's in a <defs>
2018-05-16 08:32:44 +00:00
if ( $ ( this ) . closest ( 'defs' ) . length ) { return ; }
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
uniquifyElems ( this ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// Check if it already has a gsvg group
var pa = this . parentNode ;
2013-02-15 15:51:58 +00:00
if ( pa . childNodes . length === 1 && pa . nodeName === 'g' ) {
2010-07-16 15:46:54 +00:00
$ ( pa ) . data ( 'gsvg' , this ) ;
pa . id = pa . id || getNextId ( ) ;
} else {
groupSvgElem ( this ) ;
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-10-13 13:27:39 +00:00
// For Firefox: Put all paint elems in defs
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isGecko ( ) ) {
2013-02-14 15:19:46 +00:00
content . find ( 'linearGradient, radialGradient, pattern' ) . appendTo ( svgedit . utilities . findDefs ( ) ) ;
2010-10-04 16:01:58 +00:00
}
2010-07-29 15:09:49 +00:00
// Set ref element for <use> elements
2018-05-16 00:53:27 +00:00
2010-09-10 13:23:31 +00:00
// TODO: This should also be done if the object is re-added through "redo"
2011-01-31 18:22:04 +00:00
setUseData ( content ) ;
2018-05-16 00:53:27 +00:00
2010-09-28 18:59:31 +00:00
convertGradients ( content [ 0 ] ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var attrs = {
id : 'svgcontent' ,
2014-02-17 06:48:40 +00:00
overflow : curConfig . show _outside _canvas ? 'visible' : 'hidden'
2010-06-22 14:52:51 +00:00
} ;
2018-05-16 00:53:27 +00:00
2010-07-12 20:19:43 +00:00
var percs = false ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// determine proper size
2014-04-08 03:21:22 +00:00
if ( content . attr ( 'viewBox' ) ) {
var vb = content . attr ( 'viewBox' ) . split ( ' ' ) ;
2010-06-22 14:52:51 +00:00
attrs . width = vb [ 2 ] ;
attrs . height = vb [ 3 ] ;
// handle content that doesn't have a viewBox
2018-05-16 08:32:44 +00:00
} else {
$ . each ( [ 'width' , 'height' ] , function ( i , dim ) {
2010-06-22 14:52:51 +00:00
// Set to 100 if not given
2010-07-12 20:19:43 +00:00
var val = content . attr ( dim ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( ! val ) { val = '100%' ; }
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
if ( String ( val ) . substr ( - 1 ) === '%' ) {
2010-06-22 14:52:51 +00:00
// Use user units if percentage given
2010-07-12 20:19:43 +00:00
percs = true ;
2010-06-22 14:52:51 +00:00
} else {
2013-02-14 15:19:46 +00:00
attrs [ dim ] = svgedit . units . convertToNum ( dim , val ) ;
2010-02-22 00:56:19 +00:00
}
2010-06-22 14:52:51 +00:00
} ) ;
}
2018-05-16 00:53:27 +00:00
2010-08-17 18:43:05 +00:00
// identify layers
identifyLayers ( ) ;
2018-05-16 00:53:27 +00:00
2010-08-23 15:33:00 +00:00
// Give ID for any visible layer children missing one
2018-05-16 08:32:44 +00:00
content . children ( ) . find ( visElems ) . each ( function ( ) {
if ( ! this . id ) { this . id = getNextId ( ) ; }
2010-08-23 15:33:00 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2010-07-12 20:19:43 +00:00
// Percentage width/height, so let's base it on visible elements
2013-02-15 15:51:58 +00:00
if ( percs ) {
2010-07-12 20:19:43 +00:00
var bb = getStrokedBBox ( ) ;
2010-08-17 18:43:05 +00:00
attrs . width = bb . width + bb . x ;
attrs . height = bb . height + bb . y ;
2010-07-12 20:19:43 +00:00
}
2018-05-16 00:53:27 +00:00
// Just in case negative numbers are given or
2010-08-17 18:43:05 +00:00
// result from the percs calculation
2018-05-16 08:32:44 +00:00
if ( attrs . width <= 0 ) { attrs . width = 100 ; }
if ( attrs . height <= 0 ) { attrs . height = 100 ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
content . attr ( attrs ) ;
2014-02-17 06:48:40 +00:00
this . contentW = attrs . width ;
this . contentH = attrs . height ;
2018-05-16 00:53:27 +00:00
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( svgcontent ) ) ;
2010-06-22 14:52:51 +00:00
// update root to the correct size
2014-04-08 03:21:22 +00:00
var changes = content . attr ( [ 'width' , 'height' ] ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( svgroot , changes ) ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// reset zoom
2018-05-16 08:32:44 +00:00
currentZoom = 1 ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// reset transform lists
2010-11-05 14:59:59 +00:00
svgedit . transformlist . resetListMap ( ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2011-02-23 16:24:07 +00:00
svgedit . path . clearData ( ) ;
2010-06-22 14:52:51 +00:00
svgroot . appendChild ( selectorManager . selectorParentGroup ) ;
2018-05-16 00:53:27 +00:00
2018-01-26 13:59:39 +00:00
if ( ! preventUndo ) addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ svgcontent ] ) ;
2018-05-16 08:32:44 +00:00
} catch ( e ) {
2010-06-22 14:52:51 +00:00
console . log ( e ) ;
return false ;
}
return true ;
} ;
// Function: importSvgString
2010-07-29 15:09:49 +00:00
// This function imports the input SVG XML as a <symbol> in the <defs>, then adds a
// <use> to the current layer.
2010-06-22 14:52:51 +00:00
//
// Parameters:
// xmlString - The SVG as XML text.
//
// Returns:
2015-09-30 10:24:16 +00:00
// This function returns null if the import was unsuccessful, or the element otherwise.
2018-05-16 00:53:27 +00:00
// TODO:
2010-06-28 13:10:22 +00:00
// * properly handle if namespace is introduced by imported content (must add to svgcontent
// and update all prefixes in the imported node)
// * properly handle recalculating dimensions, recalculateDimensions() doesn't handle
2018-05-16 00:53:27 +00:00
// arbitrary transform lists, but makes some assumptions about how the transform list
2010-06-28 13:10:22 +00:00
// was obtained
2018-05-16 00:53:27 +00:00
// * import should happen in top-left of current zoomed viewport
2018-05-16 08:32:44 +00:00
this . importSvgString = function ( xmlString ) {
2014-02-17 06:48:40 +00:00
var j , ts ;
2010-12-01 21:20:52 +00:00
try {
// Get unique ID
2018-05-16 08:32:44 +00:00
var uid = svgedit . utilities . encode64 ( xmlString . length + xmlString ) . substr ( 0 , 32 ) ;
2018-05-16 00:53:27 +00:00
2010-12-20 15:19:18 +00:00
var useExisting = false ;
// Look for symbol and make sure symbol exists in image
2018-05-16 08:32:44 +00:00
if ( importIds [ uid ] ) {
if ( $ ( importIds [ uid ] . symbol ) . parents ( '#svgroot' ) . length ) {
2010-12-20 15:19:18 +00:00
useExisting = true ;
}
}
2018-05-16 00:53:27 +00:00
2014-05-22 23:55:05 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Import Image' ) ;
2014-02-17 06:48:40 +00:00
var symbol ;
2013-02-15 15:51:58 +00:00
if ( useExisting ) {
2018-05-16 08:32:44 +00:00
symbol = importIds [ uid ] . symbol ;
ts = importIds [ uid ] . xform ;
2010-12-01 21:20:52 +00:00
} else {
// convert string into XML document
var newDoc = svgedit . utilities . text2xml ( xmlString ) ;
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
this . prepareSvg ( newDoc ) ;
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
// import new svg document into our document
2012-02-27 17:24:03 +00:00
var svg ;
// If DOM3 adoptNode() available, use it. Otherwise fall back to DOM2 importNode()
2013-02-15 15:51:58 +00:00
if ( svgdoc . adoptNode ) {
2012-02-27 17:24:03 +00:00
svg = svgdoc . adoptNode ( newDoc . documentElement ) ;
2013-02-15 15:51:58 +00:00
} else {
2012-02-27 17:24:03 +00:00
svg = svgdoc . importNode ( newDoc . documentElement , true ) ;
}
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
uniquifyElems ( svg ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var innerw = svgedit . units . convertToNum ( 'width' , svg . getAttribute ( 'width' ) ) ,
innerh = svgedit . units . convertToNum ( 'height' , svg . getAttribute ( 'height' ) ) ,
innervb = svg . getAttribute ( 'viewBox' ) ,
2010-12-01 21:20:52 +00:00
// if no explicit viewbox, create one out of the width and height
2014-04-08 03:21:22 +00:00
vb = innervb ? innervb . split ( ' ' ) : [ 0 , 0 , innerw , innerh ] ;
2014-02-17 06:48:40 +00:00
for ( j = 0 ; j < 4 ; ++ j ) {
2010-12-01 21:20:52 +00:00
vb [ j ] = + ( vb [ j ] ) ;
2014-02-17 06:48:40 +00:00
}
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
// TODO: properly handle preserveAspectRatio
2018-05-16 08:32:44 +00:00
var // canvasw = +svgcontent.getAttribute('width'),
2014-04-08 03:21:22 +00:00
canvash = + svgcontent . getAttribute ( 'height' ) ;
2010-12-01 21:20:52 +00:00
// imported content should be 1/3 of the canvas on its largest dimension
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
if ( innerh > innerw ) {
2018-05-16 08:32:44 +00:00
ts = 'scale(' + ( canvash / 3 ) / vb [ 3 ] + ')' ;
2013-02-15 15:51:58 +00:00
} else {
2018-05-16 08:32:44 +00:00
ts = 'scale(' + ( canvash / 3 ) / vb [ 2 ] + ')' ;
2010-12-01 21:20:52 +00:00
}
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
// Hack to make recalculateDimensions understand how to scale
2014-04-08 03:21:22 +00:00
ts = 'translate(0) ' + ts + ' translate(0)' ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
symbol = svgdoc . createElementNS ( NS . SVG , 'symbol' ) ;
2013-02-14 15:19:46 +00:00
var defs = svgedit . utilities . findDefs ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isGecko ( ) ) {
2010-12-01 21:20:52 +00:00
// Move all gradients into root for Firefox, workaround for this bug:
// https://bugzilla.mozilla.org/show_bug.cgi?id=353575
2011-01-12 07:03:54 +00:00
// TODO: Make this properly undo-able.
2010-12-01 21:20:52 +00:00
$ ( svg ) . find ( 'linearGradient, radialGradient, pattern' ) . appendTo ( defs ) ;
}
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
while ( svg . firstChild ) {
var first = svg . firstChild ;
symbol . appendChild ( first ) ;
}
var attrs = svg . attributes ;
2014-02-17 06:48:40 +00:00
var i ;
for ( i = 0 ; i < attrs . length ; i ++ ) {
2010-12-01 21:20:52 +00:00
var attr = attrs [ i ] ;
2014-04-09 05:40:22 +00:00
symbol . setAttribute ( attr . nodeName , attr . value ) ;
2010-12-01 21:20:52 +00:00
}
symbol . id = getNextId ( ) ;
2018-05-16 00:53:27 +00:00
2010-12-01 21:20:52 +00:00
// Store data
2018-05-16 08:32:44 +00:00
importIds [ uid ] = {
2010-12-01 21:20:52 +00:00
symbol : symbol ,
xform : ts
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2013-02-14 15:19:46 +00:00
svgedit . utilities . findDefs ( ) . appendChild ( symbol ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( symbol ) ) ;
2010-07-16 15:46:54 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var useEl = svgdoc . createElementNS ( NS . SVG , 'use' ) ;
useEl . id = getNextId ( ) ;
setHref ( useEl , '#' + symbol . id ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
( currentGroup || getCurrentDrawing ( ) . getCurrentLayer ( ) ) . appendChild ( useEl ) ;
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( useEl ) ) ;
2010-07-29 15:09:49 +00:00
clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
useEl . setAttribute ( 'transform' , ts ) ;
svgedit . recalculate . recalculateDimensions ( useEl ) ;
$ ( useEl ) . data ( 'symbol' , symbol ) . data ( 'ref' , symbol ) ;
addToSelection ( [ useEl ] ) ;
2018-05-16 00:53:27 +00:00
2010-07-16 15:46:54 +00:00
// TODO: Find way to add this in a recalculateDimensions-parsable way
2018-05-16 08:32:44 +00:00
// if (vb[0] != 0 || vb[1] != 0) {
// ts = 'translate(' + (-vb[0]) + ',' + (-vb[1]) + ') ' + ts;
// }
2011-01-12 07:03:54 +00:00
addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ svgcontent ] ) ;
2018-05-16 08:32:44 +00:00
} catch ( e ) {
2010-06-22 14:52:51 +00:00
console . log ( e ) ;
2015-09-30 10:24:16 +00:00
return null ;
2010-06-22 14:52:51 +00:00
}
2010-02-21 01:12:59 +00:00
2015-07-16 13:45:48 +00:00
// we want to return the element so we can automatically select it
2018-05-16 08:32:44 +00:00
return useEl ;
2010-06-22 14:52:51 +00:00
} ;
2011-02-10 04:10:03 +00:00
// TODO(codedread): Move all layer/context functions in draw.js
2010-06-22 14:52:51 +00:00
// Layer API Functions
// Group: Layers
2010-06-28 13:10:22 +00:00
// Function: identifyLayers
// Updates layer system
2018-05-16 08:32:44 +00:00
var identifyLayers = canvas . identifyLayers = function ( ) {
2010-09-23 19:53:43 +00:00
leaveContext ( ) ;
2011-01-26 03:18:59 +00:00
getCurrentDrawing ( ) . identifyLayers ( ) ;
2010-06-22 14:52:51 +00:00
} ;
// Function: createLayer
2018-05-16 00:53:27 +00:00
// Creates a new top-level layer in the drawing with the given name, sets the current layer
2014-01-31 10:40:52 +00:00
// to it, and then clears the selection. This function then calls the 'changed' handler.
2010-06-22 14:52:51 +00:00
// This is an undoable action.
//
// Parameters:
// name - The given name
2018-05-16 08:32:44 +00:00
this . createLayer = function ( name , hrService ) {
var newLayer = getCurrentDrawing ( ) . createLayer ( name , historyRecordingService ( hrService ) ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2018-05-16 08:32:44 +00:00
call ( 'changed' , [ newLayer ] ) ;
2010-06-22 14:52:51 +00:00
} ;
2016-05-04 13:38:29 +00:00
/ * *
* Creates a new top - level layer in the drawing with the given name , copies all the current layer ' s contents
* to it , and then clears the selection . This function then calls the 'changed' handler .
* This is an undoable action .
* @ param { string } name - The given name . If the layer name exists , a new name will be generated .
* @ param { svgedit . history . HistoryRecordingService } hrService - History recording service
* /
2018-05-16 08:32:44 +00:00
this . cloneLayer = function ( name , hrService ) {
2016-05-04 13:38:29 +00:00
// Clone the current layer and make the cloned layer the new current layer
2018-05-16 08:32:44 +00:00
var newLayer = getCurrentDrawing ( ) . cloneLayer ( name , historyRecordingService ( hrService ) ) ;
2010-08-19 18:30:19 +00:00
2016-05-04 13:38:29 +00:00
clearSelection ( ) ;
leaveContext ( ) ;
2018-05-16 08:32:44 +00:00
call ( 'changed' , [ newLayer ] ) ;
2010-08-19 18:30:19 +00:00
} ;
2010-06-22 14:52:51 +00:00
// Function: deleteCurrentLayer
2018-05-16 00:53:27 +00:00
// Deletes the current layer from the drawing and then clears the selection. This function
2014-01-31 10:40:52 +00:00
// then calls the 'changed' handler. This is an undoable action.
2018-05-16 08:32:44 +00:00
this . deleteCurrentLayer = function ( ) {
var currentLayer = getCurrentDrawing ( ) . getCurrentLayer ( ) ;
var nextSibling = currentLayer . nextSibling ;
var parent = currentLayer . parentNode ;
currentLayer = getCurrentDrawing ( ) . deleteCurrentLayer ( ) ;
if ( currentLayer ) {
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Delete Layer' ) ;
2011-01-30 17:11:56 +00:00
// store in our Undo History
2018-05-16 08:32:44 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( currentLayer , nextSibling , parent ) ) ;
2009-09-22 01:00:24 +00:00
addCommandToHistory ( batchCmd ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ parent ] ) ;
2010-06-22 14:52:51 +00:00
return true ;
}
return false ;
} ;
// Function: setCurrentLayer
// Sets the current layer. If the name is not a valid layer name, then this function returns
// false. Otherwise it returns true. This is not an undo-able action.
//
// Parameters:
// name - the name of the layer you want to switch to.
//
// Returns:
// true if the current layer was switched, otherwise false
2018-05-16 08:32:44 +00:00
this . setCurrentLayer = function ( name ) {
2011-01-26 03:18:59 +00:00
var result = getCurrentDrawing ( ) . setCurrentLayer ( svgedit . utilities . toXml ( name ) ) ;
if ( result ) {
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
}
2011-01-26 03:18:59 +00:00
return result ;
2010-06-22 14:52:51 +00:00
} ;
// Function: renameCurrentLayer
2018-05-16 00:53:27 +00:00
// Renames the current layer. If the layer name is not valid (i.e. unique), then this function
2010-06-22 14:52:51 +00:00
// does nothing and returns false, otherwise it returns true. This is an undo-able action.
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// Parameters:
2018-05-16 00:53:27 +00:00
// newname - the new name you want to give the current layer. This name must be unique
2010-06-22 14:52:51 +00:00
// among all layer names.
//
// Returns:
// true if the rename succeeded, false otherwise.
2018-05-16 08:32:44 +00:00
this . renameCurrentLayer = function ( newname ) {
2011-02-01 07:22:18 +00:00
var drawing = getCurrentDrawing ( ) ;
2016-05-02 02:58:41 +00:00
var layer = drawing . getCurrentLayer ( ) ;
if ( layer ) {
2016-05-04 13:38:29 +00:00
var result = drawing . setCurrentLayerName ( newname , historyRecordingService ( ) ) ;
2016-05-02 02:58:41 +00:00
if ( result ) {
call ( 'changed' , [ layer ] ) ;
return true ;
2009-09-21 22:16:44 +00:00
}
2010-06-22 14:52:51 +00:00
}
return false ;
} ;
// Function: setCurrentLayerPosition
2018-05-16 00:53:27 +00:00
// Changes the position of the current layer to the new value. If the new index is not valid,
2010-06-22 14:52:51 +00:00
// this function does nothing and returns false, otherwise it returns true. This is an
// undo-able action.
//
// Parameters:
2014-01-31 10:40:52 +00:00
// newpos - The zero-based index of the new position of the layer. This should be between
2010-06-22 14:52:51 +00:00
// 0 and (number of layers - 1)
2018-05-16 00:53:27 +00:00
//
2010-06-22 14:52:51 +00:00
// Returns:
// true if the current layer position was changed, false otherwise.
2018-05-16 08:32:44 +00:00
this . setCurrentLayerPosition = function ( newpos ) {
var drawing = getCurrentDrawing ( ) ;
2016-05-02 02:58:41 +00:00
var result = drawing . setCurrentLayerPosition ( newpos ) ;
if ( result ) {
addCommandToHistory ( new svgedit . history . MoveElementCommand ( result . currentGroup , result . oldNextSibling , svgcontent ) ) ;
return true ;
2010-06-22 14:52:51 +00:00
}
return false ;
} ;
// Function: setLayerVisibility
2018-05-16 00:53:27 +00:00
// Sets the visibility of the layer. If the layer name is not valid, this function return
2010-06-22 14:52:51 +00:00
// false, otherwise it returns true. This is an undo-able action.
//
// Parameters:
// layername - the name of the layer to change the visibility
// bVisible - true/false, whether the layer should be visible
//
// Returns:
// true if the layer's visibility was set, false otherwise
2018-05-16 08:32:44 +00:00
this . setLayerVisibility = function ( layername , bVisible ) {
2011-01-28 07:39:30 +00:00
var drawing = getCurrentDrawing ( ) ;
var prevVisibility = drawing . getLayerVisibility ( layername ) ;
var layer = drawing . setLayerVisibility ( layername , bVisible ) ;
if ( layer ) {
var oldDisplay = prevVisibility ? 'inline' : 'none' ;
2018-05-16 08:32:44 +00:00
addCommandToHistory ( new svgedit . history . ChangeElementCommand ( layer , { 'display' : oldDisplay } , 'Layer Visibility' ) ) ;
2011-01-28 07:39:30 +00:00
} else {
return false ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( layer === drawing . getCurrentLayer ( ) ) {
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
pathActions . clear ( ) ;
}
2018-05-16 08:32:44 +00:00
// call('changed', [selected]);
2010-06-22 14:52:51 +00:00
return true ;
} ;
// Function: moveSelectedToLayer
2018-05-16 00:53:27 +00:00
// Moves the selected elements to layername. If the name is not a valid layer name, then false
2014-01-31 10:40:52 +00:00
// is returned. Otherwise it returns true. This is an undo-able action.
2010-06-22 14:52:51 +00:00
//
// Parameters:
// layername - the name of the layer you want to which you want to move the selected elements
//
// Returns:
// true if the selected elements were moved to the layer, false otherwise.
2018-05-16 08:32:44 +00:00
this . moveSelectedToLayer = function ( layername ) {
2010-06-22 14:52:51 +00:00
// find the layer
2014-02-17 06:48:40 +00:00
var i ;
2011-02-01 07:22:18 +00:00
var drawing = getCurrentDrawing ( ) ;
2016-05-02 02:58:41 +00:00
var layer = drawing . getLayerByName ( layername ) ;
2018-05-16 08:32:44 +00:00
if ( ! layer ) { return false ; }
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Move Elements to Layer' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// loop for each selected element and move it
var selElems = selectedElements ;
2014-02-17 06:48:40 +00:00
i = selElems . length ;
2010-06-22 14:52:51 +00:00
while ( i -- ) {
var elem = selElems [ i ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { continue ; }
2010-06-22 14:52:51 +00:00
var oldNextSibling = elem . nextSibling ;
// TODO: this is pretty brittle!
var oldLayer = elem . parentNode ;
layer . appendChild ( elem ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . MoveElementCommand ( elem , oldNextSibling , oldLayer ) ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
return true ;
} ;
2018-05-16 08:32:44 +00:00
this . mergeLayer = function ( hrService ) {
2016-05-04 13:38:29 +00:00
getCurrentDrawing ( ) . mergeLayer ( historyRecordingService ( hrService ) ) ;
2016-05-02 02:58:41 +00:00
clearSelection ( ) ;
leaveContext ( ) ;
call ( 'changed' , [ svgcontent ] ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-08-19 18:30:19 +00:00
2018-05-16 08:32:44 +00:00
this . mergeAllLayers = function ( hrService ) {
2016-05-04 13:38:29 +00:00
getCurrentDrawing ( ) . mergeAllLayers ( historyRecordingService ( hrService ) ) ;
2010-08-19 18:30:19 +00:00
clearSelection ( ) ;
2016-05-02 02:58:41 +00:00
leaveContext ( ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ svgcontent ] ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-08-19 18:30:19 +00:00
2010-09-23 19:53:43 +00:00
// Function: leaveContext
// Return from a group context to the regular kind, make any previously
// disabled elements enabled again
2018-05-16 08:32:44 +00:00
var leaveContext = this . leaveContext = function ( ) {
var i , len = disabledElems . length ;
2013-02-15 15:51:58 +00:00
if ( len ) {
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; i ++ ) {
2018-05-16 08:32:44 +00:00
var elem = disabledElems [ i ] ;
2010-09-22 18:42:24 +00:00
var orig = elData ( elem , 'orig_opac' ) ;
2013-02-15 15:51:58 +00:00
if ( orig !== 1 ) {
2010-09-22 18:42:24 +00:00
elem . setAttribute ( 'opacity' , orig ) ;
} else {
elem . removeAttribute ( 'opacity' ) ;
}
elem . setAttribute ( 'style' , 'pointer-events: inherit' ) ;
}
2018-05-16 08:32:44 +00:00
disabledElems = [ ] ;
2010-09-22 18:42:24 +00:00
clearSelection ( true ) ;
2014-04-08 03:21:22 +00:00
call ( 'contextset' , null ) ;
2010-09-22 18:42:24 +00:00
}
2018-05-16 08:32:44 +00:00
currentGroup = null ;
2013-02-15 15:51:58 +00:00
} ;
2010-09-22 18:42:24 +00:00
2010-09-23 19:53:43 +00:00
// Function: setContext
// Set the current context (for in-group editing)
2018-05-16 08:32:44 +00:00
var setContext = this . setContext = function ( elem ) {
2010-09-23 20:04:44 +00:00
leaveContext ( ) ;
2013-02-15 15:51:58 +00:00
if ( typeof elem === 'string' ) {
2013-02-14 15:19:46 +00:00
elem = svgedit . utilities . getElem ( elem ) ;
2010-09-23 19:53:43 +00:00
}
// Edit inside this group
2018-05-16 08:32:44 +00:00
currentGroup = elem ;
2018-05-16 00:53:27 +00:00
2010-09-23 19:53:43 +00:00
// Disable other elements
2018-05-16 08:32:44 +00:00
$ ( elem ) . parentsUntil ( '#svgcontent' ) . andSelf ( ) . siblings ( ) . each ( function ( ) {
2010-09-23 19:53:43 +00:00
var opac = this . getAttribute ( 'opacity' ) || 1 ;
// Store the original's opacity
elData ( this , 'orig_opac' , opac ) ;
2014-02-17 06:48:40 +00:00
this . setAttribute ( 'opacity' , opac * 0.33 ) ;
2010-09-23 19:53:43 +00:00
this . setAttribute ( 'style' , 'pointer-events: none' ) ;
2018-05-16 08:32:44 +00:00
disabledElems . push ( this ) ;
2010-09-23 19:53:43 +00:00
} ) ;
clearSelection ( ) ;
2018-05-16 08:32:44 +00:00
call ( 'contextset' , currentGroup ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-09-23 19:53:43 +00:00
2010-06-28 20:09:34 +00:00
// Group: Document functions
2009-09-21 02:05:58 +00:00
2010-06-22 14:52:51 +00:00
// Function: clear
2014-01-31 10:40:52 +00:00
// Clears the current document. This is not an undoable action.
2018-05-16 08:32:44 +00:00
this . clear = function ( ) {
2010-06-22 14:52:51 +00:00
pathActions . clear ( ) ;
2011-02-01 15:36:45 +00:00
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
// clear the svgcontent node
2011-02-01 07:22:18 +00:00
canvas . clearSvgContentElement ( ) ;
// create new document
canvas . current _drawing _ = new svgedit . draw . Drawing ( svgcontent ) ;
2010-06-22 14:52:51 +00:00
// create empty first layer
2014-04-08 03:21:22 +00:00
canvas . createLayer ( 'Layer 1' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// clear the undo stack
2010-09-24 16:44:20 +00:00
canvas . undoMgr . resetUndoStack ( ) ;
2011-02-01 07:22:18 +00:00
2010-06-22 14:52:51 +00:00
// reset the selector manager
selectorManager . initGroup ( ) ;
2011-02-01 07:22:18 +00:00
2010-06-22 14:52:51 +00:00
// reset the rubber band box
rubberBox = selectorManager . getRubberBandBox ( ) ;
2011-02-01 07:22:18 +00:00
2014-04-08 03:21:22 +00:00
call ( 'cleared' ) ;
2010-06-22 14:52:51 +00:00
} ;
2010-06-28 13:10:22 +00:00
// Function: linkControlPoints
// Alias function
this . linkControlPoints = pathActions . linkControlPoints ;
2009-06-01 21:24:30 +00:00
2010-06-28 13:10:22 +00:00
// Function: getContentElem
// Returns the content DOM element
2018-05-16 08:32:44 +00:00
this . getContentElem = function ( ) { return svgcontent ; } ;
2010-06-28 13:10:22 +00:00
// Function: getRootElem
// Returns the root DOM element
2018-05-16 08:32:44 +00:00
this . getRootElem = function ( ) { return svgroot ; } ;
2010-06-28 13:10:22 +00:00
// Function: getSelectedElems
// Returns the array with selected DOM elements
2018-05-16 08:32:44 +00:00
this . getSelectedElems = function ( ) { return selectedElements ; } ;
2010-03-11 15:27:18 +00:00
2010-06-28 13:10:22 +00:00
// Function: getResolution
// Returns the current dimensions and zoom level in an object
2018-05-16 08:32:44 +00:00
var getResolution = this . getResolution = function ( ) {
2014-04-08 03:21:22 +00:00
// var vb = svgcontent.getAttribute('viewBox').split(' ');
2018-05-16 08:32:44 +00:00
// return {'w':vb[2], 'h':vb[3], 'zoom': currentZoom};
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var width = svgcontent . getAttribute ( 'width' ) / currentZoom ;
var height = svgcontent . getAttribute ( 'height' ) / currentZoom ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
return {
2010-10-27 18:15:28 +00:00
'w' : width ,
'h' : height ,
2018-05-16 08:32:44 +00:00
'zoom' : currentZoom
2009-07-10 05:04:06 +00:00
} ;
2010-06-22 14:52:51 +00:00
} ;
2010-06-30 18:27:36 +00:00
// Function: getZoom
// Returns the current zoom level
2018-05-16 08:32:44 +00:00
this . getZoom = function ( ) { return currentZoom ; } ;
2010-06-30 18:27:36 +00:00
2016-03-11 13:33:43 +00:00
// Function: getSnapToGrid
// Returns the current snap to grid setting
2018-05-16 08:32:44 +00:00
this . getSnapToGrid = function ( ) { return curConfig . gridSnapping ; } ;
2016-03-11 13:33:43 +00:00
2010-06-30 18:27:36 +00:00
// Function: getVersion
// Returns a string which describes the revision number of SvgCanvas.
2018-05-16 08:32:44 +00:00
this . getVersion = function ( ) {
2014-04-08 03:21:22 +00:00
return 'svgcanvas.js ($Rev$)' ;
2010-06-30 18:27:36 +00:00
} ;
// Function: setUiStrings
// Update interface strings with given values
//
// Parameters:
// strs - Object with strings (see uiStrings for examples)
2018-05-16 08:32:44 +00:00
this . setUiStrings = function ( strs ) {
2011-01-03 18:45:36 +00:00
$ . extend ( uiStrings , strs . notification ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-30 18:27:36 +00:00
// Function: setConfig
// Update configuration options with given values
//
// Parameters:
// opts - Object with options (see curConfig for examples)
2018-05-16 08:32:44 +00:00
this . setConfig = function ( opts ) {
2010-06-30 18:27:36 +00:00
$ . extend ( curConfig , opts ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-30 18:27:36 +00:00
2011-01-30 17:11:56 +00:00
// Function: getTitle
2010-07-22 19:10:51 +00:00
// Returns the current group/SVG's title contents
2018-05-16 08:32:44 +00:00
this . getTitle = function ( elem ) {
2014-02-17 06:48:40 +00:00
var i ;
2010-07-22 19:10:51 +00:00
elem = elem || selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { return ; }
2010-07-29 15:09:49 +00:00
elem = $ ( elem ) . data ( 'gsvg' ) || $ ( elem ) . data ( 'symbol' ) || elem ;
2010-07-22 19:10:51 +00:00
var childs = elem . childNodes ;
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < childs . length ; i ++ ) {
2018-05-16 08:32:44 +00:00
if ( childs [ i ] . nodeName === 'title' ) {
2010-06-22 14:52:51 +00:00
return childs [ i ] . textContent ;
2009-10-19 19:06:07 +00:00
}
}
2010-06-22 14:52:51 +00:00
return '' ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-07-22 19:10:51 +00:00
// Function: setGroupTitle
// Sets the group/SVG's title content
// TODO: Combine this with setDocumentTitle
2018-05-16 08:32:44 +00:00
this . setGroupTitle = function ( val ) {
2010-07-22 19:10:51 +00:00
var elem = selectedElements [ 0 ] ;
elem = $ ( elem ) . data ( 'gsvg' ) || elem ;
2018-05-16 00:53:27 +00:00
2010-07-22 19:10:51 +00:00
var ts = $ ( elem ) . children ( 'title' ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Set Label' ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! val . length ) {
2010-07-22 19:10:51 +00:00
// Remove title element
2010-11-13 09:58:51 +00:00
var tsNextSibling = ts . nextSibling ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( ts [ 0 ] , tsNextSibling , elem ) ) ;
2010-07-22 19:10:51 +00:00
ts . remove ( ) ;
2013-02-15 15:51:58 +00:00
} else if ( ts . length ) {
2010-07-22 19:10:51 +00:00
// Change title contents
var title = ts [ 0 ] ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( title , { '#text' : title . textContent } ) ) ;
2010-07-22 19:10:51 +00:00
title . textContent = val ;
} else {
// Add title element
2014-04-08 03:21:22 +00:00
title = svgdoc . createElementNS ( NS . SVG , 'title' ) ;
2010-07-22 19:10:51 +00:00
title . textContent = val ;
$ ( elem ) . prepend ( title ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( title ) ) ;
2010-07-22 19:10:51 +00:00
}
addCommandToHistory ( batchCmd ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-07-22 19:10:51 +00:00
// Function: getDocumentTitle
// Returns the current document title or an empty string if not found
2018-05-16 08:32:44 +00:00
var getDocumentTitle = this . getDocumentTitle = function ( ) {
2010-07-22 19:10:51 +00:00
return canvas . getTitle ( svgcontent ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-07-22 19:10:51 +00:00
2010-06-28 13:10:22 +00:00
// Function: setDocumentTitle
// Adds/updates a title element for the document with the given name.
// This is an undoable action
//
// Parameters:
// newtitle - String with the new title
2018-05-16 08:32:44 +00:00
this . setDocumentTitle = function ( newtitle ) {
2014-02-17 06:48:40 +00:00
var i ;
2018-05-16 08:32:44 +00:00
var childs = svgcontent . childNodes , docTitle = false , oldTitle = '' ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Change Image Title' ) ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < childs . length ; i ++ ) {
2018-05-16 08:32:44 +00:00
if ( childs [ i ] . nodeName === 'title' ) {
docTitle = childs [ i ] ;
oldTitle = docTitle . textContent ;
2010-06-22 14:52:51 +00:00
break ;
2010-02-02 18:42:43 +00:00
}
}
2018-05-16 08:32:44 +00:00
if ( ! docTitle ) {
docTitle = svgdoc . createElementNS ( NS . SVG , 'title' ) ;
svgcontent . insertBefore ( docTitle , svgcontent . firstChild ) ;
2018-05-16 00:53:27 +00:00
}
2013-02-15 15:51:58 +00:00
if ( newtitle . length ) {
2018-05-16 08:32:44 +00:00
docTitle . textContent = newtitle ;
2010-06-22 14:52:51 +00:00
} else {
// No title given, so element is not necessary
2018-05-16 08:32:44 +00:00
docTitle . parentNode . removeChild ( docTitle ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( docTitle , { '#text' : oldTitle } ) ) ;
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
2013-02-15 15:51:58 +00:00
} ;
2009-07-30 22:45:59 +00:00
2010-06-28 20:09:34 +00:00
// Function: getEditorNS
// Returns the editor's namespace URL, optionally adds it to root element
//
// Parameters:
// add - Boolean to indicate whether or not to add the namespace value
2018-05-16 08:32:44 +00:00
this . getEditorNS = function ( add ) {
2013-02-15 15:51:58 +00:00
if ( add ) {
2013-02-16 15:02:26 +00:00
svgcontent . setAttribute ( 'xmlns:se' , NS . SE ) ;
2010-06-22 14:52:51 +00:00
}
2013-02-16 15:02:26 +00:00
return NS . SE ;
2013-02-15 15:51:58 +00:00
} ;
2009-09-14 00:04:53 +00:00
2010-06-28 20:09:34 +00:00
// Function: setResolution
// Changes the document's dimensions to the given size
//
2018-05-16 00:53:27 +00:00
// Parameters:
// x - Number with the width of the new dimensions in user units.
2010-06-28 20:09:34 +00:00
// Can also be the string "fit" to indicate "fit to content"
2018-05-16 00:53:27 +00:00
// y - Number with the height of the new dimensions in user units.
2010-06-28 20:09:34 +00:00
//
// Returns:
2018-05-16 00:53:27 +00:00
// Boolean to indicate if resolution change was succesful.
2010-06-28 20:09:34 +00:00
// It will fail on "fit to content" option with no content to fit to.
2018-05-16 08:32:44 +00:00
this . setResolution = function ( x , y ) {
2010-06-30 18:27:36 +00:00
var res = getResolution ( ) ;
2010-06-22 14:52:51 +00:00
var w = res . w , h = res . h ;
var batchCmd ;
2018-05-16 08:32:44 +00:00
if ( x === 'fit' ) {
2010-06-22 14:52:51 +00:00
// Get bounding box
2010-06-30 18:27:36 +00:00
var bbox = getStrokedBBox ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( bbox ) {
2014-04-08 03:21:22 +00:00
batchCmd = new svgedit . history . BatchCommand ( 'Fit Canvas to Content' ) ;
2010-06-30 18:27:36 +00:00
var visEls = getVisibleElements ( ) ;
addToSelection ( visEls ) ;
2010-06-22 14:52:51 +00:00
var dx = [ ] , dy = [ ] ;
2018-05-16 08:32:44 +00:00
$ . each ( visEls , function ( i , item ) {
dx . push ( bbox . x * - 1 ) ;
dy . push ( bbox . y * - 1 ) ;
2010-06-22 14:52:51 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var cmd = canvas . moveSelectedElements ( dx , dy , true ) ;
batchCmd . addSubCommand ( cmd ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
x = Math . round ( bbox . width ) ;
y = Math . round ( bbox . height ) ;
} else {
return false ;
2009-09-14 00:04:53 +00:00
}
2010-01-18 19:14:08 +00:00
}
2018-05-16 08:32:44 +00:00
if ( x !== w || y !== h ) {
2013-02-15 15:51:58 +00:00
if ( ! batchCmd ) {
2014-04-08 03:21:22 +00:00
batchCmd = new svgedit . history . BatchCommand ( 'Change Image Dimensions' ) ;
2009-09-23 15:29:55 +00:00
}
2010-10-27 18:15:28 +00:00
2013-02-14 15:19:46 +00:00
x = svgedit . units . convertToNum ( 'width' , x ) ;
y = svgedit . units . convertToNum ( 'height' , y ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
svgcontent . setAttribute ( 'width' , x ) ;
svgcontent . setAttribute ( 'height' , y ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
this . contentW = x ;
this . contentH = y ;
2018-05-16 08:32:44 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( svgcontent , { 'width' : w , 'height' : h } ) ) ;
2010-06-22 14:52:51 +00:00
2018-05-16 08:32:44 +00:00
svgcontent . setAttribute ( 'viewBox' , [ 0 , 0 , x / currentZoom , y / currentZoom ] . join ( ' ' ) ) ;
2014-04-08 03:21:22 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( svgcontent , { 'viewBox' : [ '0 0' , w , h ] . join ( ' ' ) } ) ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ svgcontent ] ) ;
2010-06-22 14:52:51 +00:00
}
return true ;
} ;
2010-06-28 20:09:34 +00:00
// Function: getOffset
// Returns an object with x, y values indicating the svgcontent element's
// position in the editor's canvas.
2018-05-16 08:32:44 +00:00
this . getOffset = function ( ) {
2010-06-22 14:52:51 +00:00
return $ ( svgcontent ) . attr ( [ 'x' , 'y' ] ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-06-28 20:09:34 +00:00
// Function: setBBoxZoom
// Sets the zoom level on the canvas-side based on the given value
2018-05-16 00:53:27 +00:00
//
2010-06-28 20:09:34 +00:00
// Parameters:
2018-05-16 00:53:27 +00:00
// val - Bounding box object to zoom to or string indicating zoom option
2018-05-16 08:32:44 +00:00
// editorW - Integer with the editor's workarea box's width
// editorH - Integer with the editor's workarea box's height
this . setBBoxZoom = function ( val , editorW , editorH ) {
2014-02-17 06:48:40 +00:00
var spacer = 0.85 ;
2010-06-22 14:52:51 +00:00
var bb ;
2018-05-16 08:32:44 +00:00
var calcZoom = function ( bb ) {
if ( ! bb ) { return false ; }
var wZoom = Math . round ( ( editorW / bb . width ) * 100 * spacer ) / 100 ;
var hZoom = Math . round ( ( editorH / bb . height ) * 100 * spacer ) / 100 ;
var zoomlevel = Math . min ( wZoom , hZoom ) ;
2010-06-22 14:52:51 +00:00
canvas . setZoom ( zoomlevel ) ;
return { 'zoom' : zoomlevel , 'bbox' : bb } ;
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( typeof val === 'object' ) {
2010-06-22 14:52:51 +00:00
bb = val ;
2018-05-16 08:32:44 +00:00
if ( bb . width === 0 || bb . height === 0 ) {
var newzoom = bb . zoom ? bb . zoom : currentZoom * bb . factor ;
2010-06-22 14:52:51 +00:00
canvas . setZoom ( newzoom ) ;
2018-05-16 08:32:44 +00:00
return { 'zoom' : currentZoom , 'bbox' : bb } ;
2009-09-16 14:12:51 +00:00
}
2009-09-23 15:29:55 +00:00
return calcZoom ( bb ) ;
2009-09-16 14:12:51 +00:00
}
2010-06-22 14:52:51 +00:00
switch ( val ) {
2018-05-16 00:53:27 +00:00
case 'selection' :
2018-05-16 08:32:44 +00:00
if ( ! selectedElements [ 0 ] ) { return ; }
var selectedElems = $ . map ( selectedElements , function ( n ) { if ( n ) { return n ; } } ) ;
bb = getStrokedBBox ( selectedElems ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'canvas' :
var res = getResolution ( ) ;
spacer = 0.95 ;
2018-05-16 08:32:44 +00:00
bb = { width : res . w , height : res . h , x : 0 , y : 0 } ;
2018-05-16 00:53:27 +00:00
break ;
case 'content' :
bb = getStrokedBBox ( ) ;
break ;
case 'layer' :
bb = getStrokedBBox ( getVisibleElements ( getCurrentDrawing ( ) . getCurrentLayer ( ) ) ) ;
break ;
default :
return ;
2009-09-09 14:18:24 +00:00
}
2010-06-22 14:52:51 +00:00
return calcZoom ( bb ) ;
2013-02-15 15:51:58 +00:00
} ;
2009-09-09 14:18:24 +00:00
2010-06-28 20:09:34 +00:00
// Function: setZoom
// Sets the zoom to the given level
//
// Parameters:
// zoomlevel - Float indicating the zoom level to change to
2018-05-16 08:32:44 +00:00
this . setZoom = function ( zoomlevel ) {
2010-06-30 18:27:36 +00:00
var res = getResolution ( ) ;
2018-05-16 08:32:44 +00:00
svgcontent . setAttribute ( 'viewBox' , '0 0 ' + res . w / zoomlevel + ' ' + res . h / zoomlevel ) ;
currentZoom = zoomlevel ;
$ . each ( selectedElements , function ( i , elem ) {
if ( ! elem ) { return ; }
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( elem ) . resize ( ) ;
} ) ;
pathActions . zoomChange ( ) ;
2014-04-08 03:21:22 +00:00
runExtensions ( 'zoomChanged' , zoomlevel ) ;
2013-02-15 15:51:58 +00:00
} ;
2009-06-06 12:25:26 +00:00
2010-06-28 20:09:34 +00:00
// Function: getMode
// Returns the current editor mode string
2018-05-16 08:32:44 +00:00
this . getMode = function ( ) {
return currentMode ;
2010-06-22 14:52:51 +00:00
} ;
2009-06-01 21:24:30 +00:00
2010-06-28 20:09:34 +00:00
// Function: setMode
// Sets the editor's mode to the given string
//
// Parameters:
// name - String with the new mode to change to
2018-05-16 08:32:44 +00:00
this . setMode = function ( name ) {
2010-06-22 14:52:51 +00:00
pathActions . clear ( true ) ;
textActions . clear ( ) ;
2018-05-16 08:32:44 +00:00
curProperties = ( selectedElements [ 0 ] && selectedElements [ 0 ] . nodeName === 'text' ) ? curText : curShape ;
currentMode = name ;
2010-06-22 14:52:51 +00:00
} ;
2009-06-06 12:25:26 +00:00
2010-06-28 20:09:34 +00:00
// Group: Element Styling
// Function: getColor
// Returns the current fill/stroke option
2018-05-16 08:32:44 +00:00
this . getColor = function ( type ) {
return curProperties [ type ] ;
2010-06-22 14:52:51 +00:00
} ;
2010-02-02 05:48:33 +00:00
2010-06-28 20:09:34 +00:00
// Function: setColor
// Change the current stroke/fill color/gradient value
2018-05-16 00:53:27 +00:00
//
2010-06-28 20:09:34 +00:00
// Parameters:
// type - String indicating fill or stroke
// val - The value to set the stroke attribute to
// preventUndo - Boolean indicating whether or not this should be and undoable option
2018-05-16 08:32:44 +00:00
this . setColor = function ( type , val , preventUndo ) {
curShape [ type ] = val ;
curProperties [ type + '_paint' ] = { type : 'solidColor' } ;
2010-06-22 14:52:51 +00:00
var elems = [ ] ;
2014-02-17 06:48:40 +00:00
function addNonG ( e ) {
2018-05-16 08:32:44 +00:00
if ( e . nodeName !== 'g' ) {
2014-02-17 06:48:40 +00:00
elems . push ( e ) ;
}
}
2010-06-22 14:52:51 +00:00
var i = selectedElements . length ;
while ( i -- ) {
var elem = selectedElements [ i ] ;
if ( elem ) {
2018-05-16 08:32:44 +00:00
if ( elem . tagName === 'g' ) {
2014-02-17 06:48:40 +00:00
svgedit . utilities . walkTree ( elem , addNonG ) ;
2013-02-15 15:51:58 +00:00
} else {
2018-05-16 08:32:44 +00:00
if ( type === 'fill' ) {
if ( elem . tagName !== 'polyline' && elem . tagName !== 'line' ) {
2010-06-28 20:09:34 +00:00
elems . push ( elem ) ;
}
} else {
elems . push ( elem ) ;
}
}
2010-02-02 05:48:33 +00:00
}
2010-06-22 14:52:51 +00:00
}
if ( elems . length > 0 ) {
if ( ! preventUndo ) {
2010-06-30 18:27:36 +00:00
changeSelectedAttribute ( type , val , elems ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , elems ) ;
2013-02-15 15:51:58 +00:00
} else {
2010-06-30 18:27:36 +00:00
changeSelectedAttributeNoUndo ( type , val , elems ) ;
2013-02-15 15:51:58 +00:00
}
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2009-06-01 21:24:30 +00:00
2010-06-28 20:09:34 +00:00
// Function: setGradient
// Apply the current gradient to selected element's fill or stroke
//
// Parameters
// type - String indicating "fill" or "stroke" to apply to an element
2018-05-16 08:32:44 +00:00
var setGradient = this . setGradient = function ( type ) {
if ( ! curProperties [ type + '_paint' ] || curProperties [ type + '_paint' ] . type === 'solidColor' ) { return ; }
2010-06-28 20:09:34 +00:00
var grad = canvas [ type + 'Grad' ] ;
// find out if there is a duplicate gradient already in the defs
2018-05-16 08:32:44 +00:00
var duplicateGrad = findDuplicateGradient ( grad ) ;
2013-02-14 15:19:46 +00:00
var defs = svgedit . utilities . findDefs ( ) ;
2010-06-28 20:09:34 +00:00
// no duplicate found, so import gradient into defs
2018-05-16 08:32:44 +00:00
if ( ! duplicateGrad ) {
// var origGrad = grad;
grad = defs . appendChild ( svgdoc . importNode ( grad , true ) ) ;
2010-06-28 20:09:34 +00:00
// get next id and set it on the grad
grad . id = getNextId ( ) ;
2013-02-15 15:51:58 +00:00
} else { // use existing gradient
2018-05-16 08:32:44 +00:00
grad = duplicateGrad ;
2010-06-28 20:09:34 +00:00
}
2014-04-08 03:21:22 +00:00
canvas . setColor ( type , 'url(#' + grad . id + ')' ) ;
2013-02-15 15:51:58 +00:00
} ;
2009-08-17 06:56:55 +00:00
2010-06-28 20:09:34 +00:00
// Function: findDuplicateGradient
// Check if exact gradient already exists
//
// Parameters:
// grad - The gradient DOM element to compare to others
//
// Returns:
// The existing gradient if found, null if not
2018-05-16 08:32:44 +00:00
var findDuplicateGradient = function ( grad ) {
2013-02-14 15:19:46 +00:00
var defs = svgedit . utilities . findDefs ( ) ;
2018-05-16 08:32:44 +00:00
var existingGrads = $ ( defs ) . find ( 'linearGradient, radialGradient' ) ;
var i = existingGrads . length ;
var radAttrs = [ 'r' , 'cx' , 'cy' , 'fx' , 'fy' ] ;
2010-06-22 14:52:51 +00:00
while ( i -- ) {
2018-05-16 08:32:44 +00:00
var og = existingGrads [ i ] ;
if ( grad . tagName === 'linearGradient' ) {
if ( grad . getAttribute ( 'x1' ) !== og . getAttribute ( 'x1' ) ||
grad . getAttribute ( 'y1' ) !== og . getAttribute ( 'y1' ) ||
grad . getAttribute ( 'x2' ) !== og . getAttribute ( 'x2' ) ||
grad . getAttribute ( 'y2' ) !== og . getAttribute ( 'y2' )
) {
2009-07-30 22:45:59 +00:00
continue ;
}
2010-06-22 14:52:51 +00:00
} else {
2018-05-16 08:32:44 +00:00
var gradAttrs = $ ( grad ) . attr ( radAttrs ) ;
var ogAttrs = $ ( og ) . attr ( radAttrs ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var diff = false ;
2018-05-16 08:32:44 +00:00
$ . each ( radAttrs , function ( i , attr ) {
if ( gradAttrs [ attr ] !== ogAttrs [ attr ] ) { diff = true ; }
2010-06-22 14:52:51 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( diff ) { continue ; }
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// else could be a duplicate, iterate through stops
2014-04-08 03:21:22 +00:00
var stops = grad . getElementsByTagNameNS ( NS . SVG , 'stop' ) ;
var ostops = og . getElementsByTagNameNS ( NS . SVG , 'stop' ) ;
2009-08-17 06:56:55 +00:00
2018-05-16 08:32:44 +00:00
if ( stops . length !== ostops . length ) {
2010-06-22 14:52:51 +00:00
continue ;
}
2009-08-17 06:56:55 +00:00
2010-06-22 14:52:51 +00:00
var j = stops . length ;
2013-02-15 15:51:58 +00:00
while ( j -- ) {
2010-06-22 14:52:51 +00:00
var stop = stops [ j ] ;
var ostop = ostops [ j ] ;
2009-08-17 06:56:55 +00:00
2018-05-16 08:32:44 +00:00
if ( stop . getAttribute ( 'offset' ) !== ostop . getAttribute ( 'offset' ) ||
stop . getAttribute ( 'stop-opacity' ) !== ostop . getAttribute ( 'stop-opacity' ) ||
stop . getAttribute ( 'stop-color' ) !== ostop . getAttribute ( 'stop-color' ) ) {
2010-06-22 14:52:51 +00:00
break ;
2009-07-30 22:45:59 +00:00
}
2010-06-22 14:52:51 +00:00
}
2009-08-17 06:56:55 +00:00
2018-05-16 08:32:44 +00:00
if ( j === - 1 ) {
2010-06-22 14:52:51 +00:00
return og ;
}
} // for each gradient in defs
2009-08-17 06:56:55 +00:00
2010-06-22 14:52:51 +00:00
return null ;
} ;
2009-09-04 06:30:18 +00:00
2018-05-16 08:32:44 +00:00
function reorientGrads ( elem , m ) {
2014-02-17 06:48:40 +00:00
var i ;
2011-02-24 16:13:26 +00:00
var bb = svgedit . utilities . getBBox ( elem ) ;
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < 2 ; i ++ ) {
2010-12-21 19:36:51 +00:00
var type = i === 0 ? 'fill' : 'stroke' ;
var attrVal = elem . getAttribute ( type ) ;
2013-02-15 15:51:58 +00:00
if ( attrVal && attrVal . indexOf ( 'url(' ) === 0 ) {
2013-02-14 15:19:46 +00:00
var grad = svgedit . utilities . getRefElem ( attrVal ) ;
2013-02-15 15:51:58 +00:00
if ( grad . tagName === 'linearGradient' ) {
2010-12-21 19:36:51 +00:00
var x1 = grad . getAttribute ( 'x1' ) || 0 ;
var y1 = grad . getAttribute ( 'y1' ) || 0 ;
var x2 = grad . getAttribute ( 'x2' ) || 1 ;
var y2 = grad . getAttribute ( 'y2' ) || 0 ;
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
// Convert to USOU points
x1 = ( bb . width * x1 ) + bb . x ;
y1 = ( bb . height * y1 ) + bb . y ;
x2 = ( bb . width * x2 ) + bb . x ;
y2 = ( bb . height * y2 ) + bb . y ;
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
// Transform those points
2013-02-14 15:19:46 +00:00
var pt1 = svgedit . math . transformPoint ( x1 , y1 , m ) ;
var pt2 = svgedit . math . transformPoint ( x2 , y2 , m ) ;
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
// Convert back to BB points
2018-05-16 08:32:44 +00:00
var gCoords = { } ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
gCoords . x1 = ( pt1 . x - bb . x ) / bb . width ;
gCoords . y1 = ( pt1 . y - bb . y ) / bb . height ;
gCoords . x2 = ( pt2 . x - bb . x ) / bb . width ;
gCoords . y2 = ( pt2 . y - bb . y ) / bb . height ;
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
var newgrad = grad . cloneNode ( true ) ;
2018-05-16 08:32:44 +00:00
$ ( newgrad ) . attr ( gCoords ) ;
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
newgrad . id = getNextId ( ) ;
2013-02-14 15:19:46 +00:00
svgedit . utilities . findDefs ( ) . appendChild ( newgrad ) ;
2010-12-21 19:36:51 +00:00
elem . setAttribute ( type , 'url(#' + newgrad . id + ')' ) ;
}
}
}
}
2010-06-28 20:09:34 +00:00
// Function: setPaint
// Set a color/gradient to a fill/stroke
//
2013-02-25 13:05:25 +00:00
// Parameters:
2010-06-28 20:09:34 +00:00
// type - String with "fill" or "stroke"
// paint - The jGraduate paint object to apply
2018-05-16 08:32:44 +00:00
this . setPaint = function ( type , paint ) {
2010-06-22 14:52:51 +00:00
// make a copy
2010-06-28 20:09:34 +00:00
var p = new $ . jGraduate . Paint ( paint ) ;
2013-02-25 13:05:25 +00:00
this . setPaintOpacity ( type , p . alpha / 100 , true ) ;
2009-09-04 06:30:18 +00:00
2010-06-22 14:52:51 +00:00
// now set the current paint object
2018-05-16 08:32:44 +00:00
curProperties [ type + '_paint' ] = p ;
2013-02-25 13:05:25 +00:00
switch ( p . type ) {
2018-05-16 00:53:27 +00:00
case 'solidColor' :
2018-05-16 08:32:44 +00:00
this . setColor ( type , p . solidColor !== 'none' ? '#' + p . solidColor : 'none' ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'linearGradient' :
case 'radialGradient' :
canvas [ type + 'Grad' ] = p [ p . type ] ;
setGradient ( type ) ;
break ;
2010-06-22 14:52:51 +00:00
}
} ;
2009-07-30 22:45:59 +00:00
2013-02-25 13:05:25 +00:00
// alias
2018-05-16 08:32:44 +00:00
this . setStrokePaint = function ( paint ) {
2013-02-25 13:05:25 +00:00
this . setPaint ( 'stroke' , paint ) ;
} ;
2009-06-01 21:24:30 +00:00
2018-05-16 08:32:44 +00:00
this . setFillPaint = function ( paint ) {
2013-02-25 13:05:25 +00:00
this . setPaint ( 'fill' , paint ) ;
} ;
2010-06-28 20:09:34 +00:00
// Function: getStrokeWidth
// Returns the current stroke-width value
2018-05-16 08:32:44 +00:00
this . getStrokeWidth = function ( ) {
return curProperties . stroke _width ;
2010-06-22 14:52:51 +00:00
} ;
2010-06-28 20:09:34 +00:00
// Function: setStrokeWidth
// Sets the stroke width for the current selected elements
// When attempting to set a line's width to 0, this changes it to 1 instead
//
// Parameters:
// val - A Float indicating the new stroke width value
2018-05-16 08:32:44 +00:00
this . setStrokeWidth = function ( val ) {
if ( val === 0 && [ 'line' , 'path' ] . indexOf ( currentMode ) >= 0 ) {
2010-06-22 14:52:51 +00:00
canvas . setStrokeWidth ( 1 ) ;
return ;
}
2018-05-16 08:32:44 +00:00
curProperties . stroke _width = val ;
2013-02-25 13:05:25 +00:00
2010-06-22 14:52:51 +00:00
var elems = [ ] ;
2014-02-17 06:48:40 +00:00
function addNonG ( e ) {
2018-05-16 08:32:44 +00:00
if ( e . nodeName !== 'g' ) {
2014-02-17 06:48:40 +00:00
elems . push ( e ) ;
}
}
2010-06-22 14:52:51 +00:00
var i = selectedElements . length ;
while ( i -- ) {
var elem = selectedElements [ i ] ;
if ( elem ) {
2018-05-16 08:32:44 +00:00
if ( elem . tagName === 'g' ) {
2014-02-17 06:48:40 +00:00
svgedit . utilities . walkTree ( elem , addNonG ) ;
2018-05-16 08:32:44 +00:00
} else {
2010-06-22 14:52:51 +00:00
elems . push ( elem ) ;
2014-02-17 06:48:40 +00:00
}
2010-06-22 14:52:51 +00:00
}
2013-02-25 13:05:25 +00:00
}
2010-06-22 14:52:51 +00:00
if ( elems . length > 0 ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'stroke-width' , val , elems ) ;
call ( 'changed' , selectedElements ) ;
2010-06-22 14:52:51 +00:00
}
} ;
2009-06-06 12:25:26 +00:00
2010-06-28 20:09:34 +00:00
// Function: setStrokeAttr
// Set the given stroke-related attribute the given value for selected elements
//
// Parameters:
// attr - String with the attribute name
// val - String or number with the attribute value
2018-05-16 08:32:44 +00:00
this . setStrokeAttr = function ( attr , val ) {
curShape [ attr . replace ( '-' , '_' ) ] = val ;
2010-06-22 14:52:51 +00:00
var elems = [ ] ;
2018-05-16 08:32:44 +00:00
2010-06-22 14:52:51 +00:00
var i = selectedElements . length ;
while ( i -- ) {
var elem = selectedElements [ i ] ;
if ( elem ) {
2018-05-16 08:32:44 +00:00
if ( elem . tagName === 'g' ) {
svgedit . utilities . walkTree ( elem , function ( e ) { if ( e . nodeName !== 'g' ) { elems . push ( e ) ; } } ) ;
} else {
2010-06-22 14:52:51 +00:00
elems . push ( elem ) ;
2014-02-17 06:48:40 +00:00
}
2010-06-22 14:52:51 +00:00
}
2013-02-25 13:05:25 +00:00
}
2010-06-22 14:52:51 +00:00
if ( elems . length > 0 ) {
2010-06-30 18:27:36 +00:00
changeSelectedAttribute ( attr , val , elems ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , selectedElements ) ;
2010-06-22 14:52:51 +00:00
}
} ;
2010-07-09 19:09:37 +00:00
// Function: getStyle
// Returns current style options
2018-05-16 08:32:44 +00:00
this . getStyle = function ( ) {
return curShape ;
2013-02-15 15:51:58 +00:00
} ;
2010-07-09 19:09:37 +00:00
2010-06-28 20:09:34 +00:00
// Function: getOpacity
// Returns the current opacity
2018-05-16 08:32:44 +00:00
this . getOpacity = function ( ) {
return curShape . opacity ;
2010-06-22 14:52:51 +00:00
} ;
2010-06-28 20:09:34 +00:00
// Function: setOpacity
// Sets the given opacity to the current selected elements
2018-05-16 08:32:44 +00:00
this . setOpacity = function ( val ) {
curShape . opacity = val ;
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'opacity' , val ) ;
2010-06-22 14:52:51 +00:00
} ;
2009-06-04 13:43:58 +00:00
2010-06-28 20:09:34 +00:00
// Function: getOpacity
// Returns the current fill opacity
2018-05-16 08:32:44 +00:00
this . getFillOpacity = function ( ) {
return curShape . fill _opacity ;
2010-06-28 20:09:34 +00:00
} ;
// Function: getStrokeOpacity
// Returns the current stroke opacity
2018-05-16 08:32:44 +00:00
this . getStrokeOpacity = function ( ) {
return curShape . stroke _opacity ;
2010-06-28 20:09:34 +00:00
} ;
// Function: setPaintOpacity
// Sets the current fill/stroke opacity
//
// Parameters:
// type - String with "fill" or "stroke"
// val - Float with the new opacity value
// preventUndo - Boolean indicating whether or not this should be an undoable action
2018-05-16 08:32:44 +00:00
this . setPaintOpacity = function ( type , val , preventUndo ) {
curShape [ type + '_opacity' ] = val ;
2014-02-17 06:48:40 +00:00
if ( ! preventUndo ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( type + '-opacity' , val ) ;
2018-05-16 08:32:44 +00:00
} else {
2014-04-08 03:21:22 +00:00
changeSelectedAttributeNoUndo ( type + '-opacity' , val ) ;
2014-02-17 06:48:40 +00:00
}
2010-06-28 20:09:34 +00:00
} ;
2013-02-19 13:17:27 +00:00
// Function: getPaintOpacity
// Gets the current fill/stroke opacity
//
// Parameters:
// type - String with "fill" or "stroke"
2018-05-16 08:32:44 +00:00
this . getPaintOpacity = function ( type ) {
2013-02-19 13:17:27 +00:00
return type === 'fill' ? this . getFillOpacity ( ) : this . getStrokeOpacity ( ) ;
} ;
2010-06-29 20:43:44 +00:00
// Function: getBlur
// Gets the stdDeviation blur value of the given element
//
// Parameters:
// elem - The element to check the blur value for
2018-05-16 08:32:44 +00:00
this . getBlur = function ( elem ) {
2010-06-22 14:52:51 +00:00
var val = 0 ;
2013-02-19 13:17:27 +00:00
// var elem = selectedElements[0];
2013-02-15 15:51:58 +00:00
if ( elem ) {
2018-05-16 08:32:44 +00:00
var filterUrl = elem . getAttribute ( 'filter' ) ;
if ( filterUrl ) {
2013-02-14 15:19:46 +00:00
var blur = svgedit . utilities . getElem ( elem . id + '_blur' ) ;
2013-02-15 15:51:58 +00:00
if ( blur ) {
2010-06-22 14:52:51 +00:00
val = blur . firstChild . getAttribute ( 'stdDeviation' ) ;
2010-04-06 18:40:10 +00:00
}
}
2010-06-22 14:52:51 +00:00
}
return val ;
} ;
2010-04-06 18:40:10 +00:00
2018-05-16 08:32:44 +00:00
( function ( ) {
var curCommand = null ;
2010-06-22 14:52:51 +00:00
var filter = null ;
var filterHidden = false ;
2018-05-16 00:53:27 +00:00
2010-06-29 20:43:44 +00:00
// Function: setBlurNoUndo
// Sets the stdDeviation blur value on the selected element without being undoable
//
// Parameters:
// val - The new stdDeviation value
2018-05-16 08:32:44 +00:00
canvas . setBlurNoUndo = function ( val ) {
2013-02-15 15:51:58 +00:00
if ( ! filter ) {
2010-06-22 14:52:51 +00:00
canvas . setBlur ( val ) ;
return ;
2010-04-28 16:34:02 +00:00
}
2013-02-15 15:51:58 +00:00
if ( val === 0 ) {
2010-06-22 14:52:51 +00:00
// Don't change the StdDev, as that will hide the element.
// Instead, just remove the value for "filter"
2014-04-08 03:21:22 +00:00
changeSelectedAttributeNoUndo ( 'filter' , '' ) ;
2010-06-22 14:52:51 +00:00
filterHidden = true ;
} else {
2010-10-27 18:58:34 +00:00
var elem = selectedElements [ 0 ] ;
2013-02-15 15:51:58 +00:00
if ( filterHidden ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttributeNoUndo ( 'filter' , 'url(#' + elem . id + '_blur)' ) ;
2010-10-27 18:58:34 +00:00
}
2013-02-15 15:51:58 +00:00
if ( svgedit . browser . isWebkit ( ) ) {
2010-10-27 18:58:34 +00:00
console . log ( 'e' , elem ) ;
elem . removeAttribute ( 'filter' ) ;
elem . setAttribute ( 'filter' , 'url(#' + elem . id + '_blur)' ) ;
2010-06-22 14:52:51 +00:00
}
2014-04-08 03:21:22 +00:00
changeSelectedAttributeNoUndo ( 'stdDeviation' , val , [ filter . firstChild ] ) ;
2010-06-22 14:52:51 +00:00
canvas . setBlurOffsets ( filter , val ) ;
2010-04-28 16:34:02 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
function finishChange ( ) {
2010-11-12 19:08:29 +00:00
var bCmd = canvas . undoMgr . finishUndoableChange ( ) ;
2018-05-16 08:32:44 +00:00
curCommand . addSubCommand ( bCmd ) ;
addCommandToHistory ( curCommand ) ;
curCommand = null ;
2010-06-22 14:52:51 +00:00
filter = null ;
}
2010-06-29 20:43:44 +00:00
// Function: setBlurOffsets
// Sets the x, y, with, height values of the filter element in order to
// make the blur not be clipped. Removes them if not neeeded
//
// Parameters:
// filter - The filter DOM element to update
// stdDev - The standard deviation value on which to base the offset size
2018-05-16 08:32:44 +00:00
canvas . setBlurOffsets = function ( filter , stdDev ) {
2013-02-15 15:51:58 +00:00
if ( stdDev > 3 ) {
2010-06-22 14:52:51 +00:00
// TODO: Create algorithm here where size is based on expected blur
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( filter , {
2010-06-22 14:52:51 +00:00
x : '-50%' ,
y : '-50%' ,
width : '200%' ,
2010-10-28 16:47:39 +00:00
height : '200%'
2010-06-22 14:52:51 +00:00
} , 100 ) ;
} else {
2010-10-27 18:58:34 +00:00
// Removing these attributes hides text in Chrome (see Issue 579)
2013-02-15 15:51:58 +00:00
if ( ! svgedit . browser . isWebkit ( ) ) {
2010-10-27 18:58:34 +00:00
filter . removeAttribute ( 'x' ) ;
filter . removeAttribute ( 'y' ) ;
filter . removeAttribute ( 'width' ) ;
filter . removeAttribute ( 'height' ) ;
}
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2018-05-16 00:53:27 +00:00
// Function: setBlur
2010-06-29 20:43:44 +00:00
// Adds/updates the blur filter to the selected element
//
// Parameters:
// val - Float with the new stdDeviation blur value
// complete - Boolean indicating whether or not the action should be completed (to add to the undo manager)
2018-05-16 08:32:44 +00:00
canvas . setBlur = function ( val , complete ) {
if ( curCommand ) {
2010-06-22 14:52:51 +00:00
finishChange ( ) ;
return ;
2010-05-28 19:17:30 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Looks for associated blur, creates one if not found
var elem = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
var elemId = elem . id ;
filter = svgedit . utilities . getElem ( elemId + '_blur' ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
val -= 0 ;
2018-05-16 00:53:27 +00:00
2013-02-17 08:21:07 +00:00
var batchCmd = new svgedit . history . BatchCommand ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Blur found!
2013-02-15 15:51:58 +00:00
if ( filter ) {
if ( val === 0 ) {
2010-06-22 14:52:51 +00:00
filter = null ;
}
} else {
// Not found, so create
2014-04-08 03:21:22 +00:00
var newblur = addSvgElementFromJson ( { 'element' : 'feGaussianBlur' ,
'attr' : {
'in' : 'SourceGraphic' ,
'stdDeviation' : val
2010-06-22 14:52:51 +00:00
}
} ) ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
filter = addSvgElementFromJson ( { 'element' : 'filter' ,
'attr' : {
2018-05-16 08:32:44 +00:00
'id' : elemId + '_blur'
2010-06-22 14:52:51 +00:00
}
} ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
filter . appendChild ( newblur ) ;
2013-02-14 15:19:46 +00:00
svgedit . utilities . findDefs ( ) . appendChild ( filter ) ;
2018-05-16 00:53:27 +00:00
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( filter ) ) ;
2010-06-22 14:52:51 +00:00
}
var changes = { filter : elem . getAttribute ( 'filter' ) } ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( val === 0 ) {
2014-04-08 03:21:22 +00:00
elem . removeAttribute ( 'filter' ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , changes ) ) ;
2010-06-22 14:52:51 +00:00
return ;
}
2013-02-15 15:51:58 +00:00
2018-05-16 08:32:44 +00:00
changeSelectedAttribute ( 'filter' , 'url(#' + elemId + '_blur)' ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , changes ) ) ;
2013-02-15 15:51:58 +00:00
canvas . setBlurOffsets ( filter , val ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
curCommand = batchCmd ;
canvas . undoMgr . beginUndoableChange ( 'stdDeviation' , [ filter ? filter . firstChild : null ] ) ;
2013-02-15 15:51:58 +00:00
if ( complete ) {
2010-06-22 14:52:51 +00:00
canvas . setBlurNoUndo ( val ) ;
finishChange ( ) ;
}
2009-06-30 06:24:41 +00:00
} ;
2010-06-22 14:52:51 +00:00
} ( ) ) ;
2009-06-06 12:25:26 +00:00
2010-06-29 20:43:44 +00:00
// Function: getBold
// Check whether selected element is bold or not
//
// Returns:
// Boolean indicating whether or not element is bold
2018-05-16 08:32:44 +00:00
this . getBold = function ( ) {
2010-06-22 14:52:51 +00:00
// should only have one element selected
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( selected != null && selected . tagName === 'text' &&
selectedElements [ 1 ] == null ) {
return ( selected . getAttribute ( 'font-weight' ) === 'bold' ) ;
2010-06-22 14:52:51 +00:00
}
return false ;
} ;
2009-08-17 06:56:55 +00:00
2010-06-29 20:43:44 +00:00
// Function: setBold
// Make the selected element bold or normal
//
// Parameters:
// b - Boolean indicating bold (true) or normal (false)
2018-05-16 08:32:44 +00:00
this . setBold = function ( b ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( selected != null && selected . tagName === 'text' &&
selectedElements [ 1 ] == null ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'font-weight' , b ? 'bold' : 'normal' ) ;
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
if ( ! selectedElements [ 0 ] . textContent ) {
2010-07-20 13:35:11 +00:00
textActions . setCursor ( ) ;
}
2010-06-22 14:52:51 +00:00
} ;
2009-08-17 06:56:55 +00:00
2010-06-29 20:43:44 +00:00
// Function: getItalic
// Check whether selected element is italic or not
//
// Returns:
// Boolean indicating whether or not element is italic
2018-05-16 08:32:44 +00:00
this . getItalic = function ( ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( selected != null && selected . tagName === 'text' &&
selectedElements [ 1 ] == null ) {
return ( selected . getAttribute ( 'font-style' ) === 'italic' ) ;
2010-06-22 14:52:51 +00:00
}
return false ;
} ;
2009-08-17 06:56:55 +00:00
2010-06-29 20:43:44 +00:00
// Function: setItalic
// Make the selected element italic or normal
//
// Parameters:
// b - Boolean indicating italic (true) or normal (false)
2018-05-16 08:32:44 +00:00
this . setItalic = function ( i ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( selected != null && selected . tagName === 'text' &&
selectedElements [ 1 ] == null ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'font-style' , i ? 'italic' : 'normal' ) ;
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
if ( ! selectedElements [ 0 ] . textContent ) {
2010-07-20 13:35:11 +00:00
textActions . setCursor ( ) ;
}
2010-06-22 14:52:51 +00:00
} ;
2009-08-17 06:56:55 +00:00
2010-06-29 20:43:44 +00:00
// Function: getFontFamily
// Returns the current font family
2018-05-16 08:32:44 +00:00
this . getFontFamily = function ( ) {
return curText . font _family ;
2010-06-22 14:52:51 +00:00
} ;
2009-06-12 06:45:37 +00:00
2010-06-29 20:43:44 +00:00
// Function: setFontFamily
// Set the new font family
//
// Parameters:
// val - String with the new font family
2018-05-16 08:32:44 +00:00
this . setFontFamily = function ( val ) {
curText . font _family = val ;
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'font-family' , val ) ;
2013-02-15 15:51:58 +00:00
if ( selectedElements [ 0 ] && ! selectedElements [ 0 ] . textContent ) {
2010-07-20 13:35:11 +00:00
textActions . setCursor ( ) ;
}
2010-06-22 14:52:51 +00:00
} ;
2009-06-23 07:48:15 +00:00
2011-03-09 18:55:48 +00:00
// Function: setFontColor
// Set the new font color
//
// Parameters:
// val - String with the new font color
2018-05-16 08:32:44 +00:00
this . setFontColor = function ( val ) {
curText . fill = val ;
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'fill' , val ) ;
2011-03-09 18:55:48 +00:00
} ;
// Function: getFontColor
// Returns the current font color
2018-05-16 08:32:44 +00:00
this . getFontColor = function ( ) {
return curText . fill ;
2011-03-09 18:55:48 +00:00
} ;
2010-06-29 20:43:44 +00:00
// Function: getFontSize
// Returns the current font size
2018-05-16 08:32:44 +00:00
this . getFontSize = function ( ) {
return curText . font _size ;
2010-06-22 14:52:51 +00:00
} ;
2009-06-10 03:12:19 +00:00
2010-06-29 20:43:44 +00:00
// Function: setFontSize
// Applies the given font size to the selected element
//
// Parameters:
// val - Float with the new font size
2018-05-16 08:32:44 +00:00
this . setFontSize = function ( val ) {
curText . font _size = val ;
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( 'font-size' , val ) ;
2013-02-15 15:51:58 +00:00
if ( ! selectedElements [ 0 ] . textContent ) {
2010-07-20 13:35:11 +00:00
textActions . setCursor ( ) ;
}
2010-06-22 14:52:51 +00:00
} ;
2009-06-23 07:48:15 +00:00
2010-06-29 20:43:44 +00:00
// Function: getText
// Returns the current text (textContent) of the selected element
2018-05-16 08:32:44 +00:00
this . getText = function ( ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ 0 ] ;
2014-04-08 03:21:22 +00:00
if ( selected == null ) { return '' ; }
2010-06-22 14:52:51 +00:00
return selected . textContent ;
} ;
2009-06-23 07:48:15 +00:00
2010-06-29 20:43:44 +00:00
// Function: setTextContent
// Updates the text element with the given string
//
// Parameters:
// val - String with the new text
2018-05-16 08:32:44 +00:00
this . setTextContent = function ( val ) {
2014-04-08 03:21:22 +00:00
changeSelectedAttribute ( '#text' , val ) ;
2010-06-22 14:52:51 +00:00
textActions . init ( val ) ;
textActions . setCursor ( ) ;
} ;
2009-06-23 07:48:15 +00:00
2010-06-29 20:43:44 +00:00
// Function: setImageURL
// Sets the new image URL for the selected image element. Updates its size if
// a new URL is given
2018-05-16 00:53:27 +00:00
//
2010-06-29 20:43:44 +00:00
// Parameters:
// val - String with the image URL/path
2018-05-16 08:32:44 +00:00
this . setImageURL = function ( val ) {
2010-06-22 14:52:51 +00:00
var elem = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { return ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var attrs = $ ( elem ) . attr ( [ 'width' , 'height' ] ) ;
var setsize = ( ! attrs . width || ! attrs . height ) ;
2010-04-09 16:01:09 +00:00
2018-05-16 08:32:44 +00:00
var curHref = getHref ( elem ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Do nothing if no URL change or size change
2018-05-16 08:32:44 +00:00
if ( curHref !== val ) {
2010-06-22 14:52:51 +00:00
setsize = true ;
2018-05-16 08:32:44 +00:00
} else if ( ! setsize ) { return ; }
2010-06-22 14:52:51 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Change Image URL' ) ;
2010-06-22 14:52:51 +00:00
2010-08-16 17:53:15 +00:00
setHref ( elem , val ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , {
2018-05-16 08:32:44 +00:00
'#href' : curHref
2010-06-22 14:52:51 +00:00
} ) ) ;
2013-02-15 15:51:58 +00:00
if ( setsize ) {
2018-05-16 08:32:44 +00:00
$ ( new Image ( ) ) . load ( function ( ) {
2010-06-22 14:52:51 +00:00
var changes = $ ( elem ) . attr ( [ 'width' , 'height' ] ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
$ ( elem ) . attr ( {
width : this . width ,
height : this . height
} ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( elem ) . resize ( ) ;
2018-05-16 00:53:27 +00:00
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , changes ) ) ;
2010-04-09 16:01:09 +00:00
addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ elem ] ) ;
2013-02-15 16:51:48 +00:00
} ) . attr ( 'src' , val ) ;
2010-06-22 14:52:51 +00:00
} else {
addCommandToHistory ( batchCmd ) ;
}
} ;
2009-09-04 22:22:40 +00:00
2010-12-01 17:54:11 +00:00
// Function: setLinkURL
// Sets the new link URL for the selected anchor element.
2018-05-16 00:53:27 +00:00
//
2010-12-01 17:54:11 +00:00
// Parameters:
// val - String with the link URL/path
2018-05-16 08:32:44 +00:00
this . setLinkURL = function ( val ) {
2010-12-01 17:54:11 +00:00
var elem = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { return ; }
2013-02-15 15:51:58 +00:00
if ( elem . tagName !== 'a' ) {
2010-12-01 17:54:11 +00:00
// See if parent is an anchor
2018-05-16 08:32:44 +00:00
var parentsA = $ ( elem ) . parents ( 'a' ) ;
if ( parentsA . length ) {
elem = parentsA [ 0 ] ;
2010-12-01 17:54:11 +00:00
} else {
return ;
}
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var curHref = getHref ( elem ) ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( curHref === val ) { return ; }
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Change Link URL' ) ;
2010-12-01 17:54:11 +00:00
setHref ( elem , val ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( elem , {
2018-05-16 08:32:44 +00:00
'#href' : curHref
2010-12-01 17:54:11 +00:00
} ) ) ;
addCommandToHistory ( batchCmd ) ;
} ;
2010-06-29 20:43:44 +00:00
// Function: setRectRadius
// Sets the rx & ry values to the selected rect element to change its corner radius
2018-05-16 00:53:27 +00:00
//
2010-06-29 20:43:44 +00:00
// Parameters:
// val - The new radius
2018-05-16 08:32:44 +00:00
this . setRectRadius = function ( val ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( selected != null && selected . tagName === 'rect' ) {
2014-04-08 03:21:22 +00:00
var r = selected . getAttribute ( 'rx' ) ;
2018-05-16 08:32:44 +00:00
if ( r !== String ( val ) ) {
2014-04-08 03:21:22 +00:00
selected . setAttribute ( 'rx' , val ) ;
selected . setAttribute ( 'ry' , val ) ;
2018-05-16 08:32:44 +00:00
addCommandToHistory ( new svgedit . history . ChangeElementCommand ( selected , { 'rx' : r , 'ry' : r } , 'Radius' ) ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , [ selected ] ) ;
2009-06-11 15:18:46 +00:00
}
2009-08-28 12:37:52 +00:00
}
2010-06-22 14:52:51 +00:00
} ;
2009-06-29 14:38:30 +00:00
2010-12-01 17:54:11 +00:00
// Function: makeHyperlink
// Wraps the selected element(s) in an anchor element or converts group to one
2018-05-16 08:32:44 +00:00
this . makeHyperlink = function ( url ) {
2010-12-01 17:54:11 +00:00
canvas . groupSelectedElements ( 'a' , url ) ;
2018-05-16 00:53:27 +00:00
2010-12-01 17:54:11 +00:00
// TODO: If element is a single "g", convert to "a"
2013-02-15 15:51:58 +00:00
// if (selectedElements.length > 1 && selectedElements[1]) {
} ;
2010-12-01 17:54:11 +00:00
// Function: removeHyperlink
2018-05-16 08:32:44 +00:00
this . removeHyperlink = function ( ) {
2010-12-01 17:54:11 +00:00
canvas . ungroupSelectedElement ( ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-12-01 17:54:11 +00:00
2010-06-30 18:27:36 +00:00
// Group: Element manipulation
2010-06-29 20:43:44 +00:00
// Function: setSegType
2018-05-16 00:53:27 +00:00
// Sets the new segment type to the selected segment(s).
2010-06-29 20:43:44 +00:00
//
// Parameters:
// new_type - Integer with the new segment type
// See http://www.w3.org/TR/SVG/paths.html#InterfaceSVGPathSeg for list
2018-05-16 08:32:44 +00:00
this . setSegType = function ( newType ) {
pathActions . setSegType ( newType ) ;
2014-02-17 06:48:40 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-11-15 09:25:49 +00:00
// TODO(codedread): Remove the getBBox argument and split this function into two.
2010-06-30 18:27:36 +00:00
// Function: convertToPath
// Convert selected element to a path, or get the BBox of an element-as-path
//
2018-05-16 00:53:27 +00:00
// Parameters:
2010-06-30 18:27:36 +00:00
// elem - The DOM element to be converted
// getBBox - Boolean on whether or not to only return the path's BBox
//
// Returns:
// If the getBBox flag is true, the resulting path's bounding box object.
// Otherwise the resulting path element is returned.
2018-05-16 08:32:44 +00:00
this . convertToPath = function ( elem , getBBox ) {
2013-02-15 15:51:58 +00:00
if ( elem == null ) {
2010-06-30 18:27:36 +00:00
var elems = selectedElements ;
2018-05-16 08:32:44 +00:00
$ . each ( elems , function ( i , elem ) {
if ( elem ) { canvas . convertToPath ( elem ) ; }
2010-06-30 18:27:36 +00:00
} ) ;
return ;
2010-06-22 14:52:51 +00:00
}
2016-04-26 20:01:39 +00:00
if ( getBBox ) {
2018-05-16 08:32:44 +00:00
return svgedit . utilities . getBBoxOfElementAsPath ( elem , addSvgElementFromJson , pathActions ) ;
2016-04-22 16:24:52 +00:00
} else {
2018-05-16 08:32:44 +00:00
// TODO: Why is this applying attributes from curShape, then inside utilities.convertToPath it's pulling addition attributes from elem?
// TODO: If convertToPath is called with one elem, curShape and elem are probably the same; but calling with multiple is a bug or cool feature.
2016-04-22 16:24:52 +00:00
var attrs = {
2018-05-16 08:32:44 +00:00
'fill' : curShape . fill ,
'fill-opacity' : curShape . fill _opacity ,
'stroke' : curShape . stroke ,
'stroke-width' : curShape . stroke _width ,
'stroke-dasharray' : curShape . stroke _dasharray ,
'stroke-linejoin' : curShape . stroke _linejoin ,
'stroke-linecap' : curShape . stroke _linecap ,
'stroke-opacity' : curShape . stroke _opacity ,
'opacity' : curShape . opacity ,
'visibility' : 'hidden'
2016-04-26 20:01:39 +00:00
} ;
return svgedit . utilities . convertToPath ( elem , attrs , addSvgElementFromJson , pathActions , clearSelection , addToSelection , svgedit . history , addCommandToHistory ) ;
2010-06-30 18:27:36 +00:00
}
2010-11-08 07:23:26 +00:00
} ;
2010-06-30 18:27:36 +00:00
// Function: changeSelectedAttributeNoUndo
// This function makes the changes to the elements. It does not add the change
2018-05-16 00:53:27 +00:00
// to the history stack.
//
2010-06-30 18:27:36 +00:00
// Parameters:
// attr - String with the attribute name
// newValue - String or number with the new attribute value
// elems - The DOM elements to apply the change to
2018-05-16 08:32:44 +00:00
var changeSelectedAttributeNoUndo = function ( attr , newValue , elems ) {
if ( currentMode === 'pathedit' ) {
2010-06-30 18:27:36 +00:00
// Editing node
pathActions . moveNode ( attr , newValue ) ;
}
2014-02-17 06:48:40 +00:00
elems = elems || selectedElements ;
2010-06-30 18:27:36 +00:00
var i = elems . length ;
2018-05-16 08:32:44 +00:00
var noXYElems = [ 'g' , 'polyline' , 'path' ] ;
var goodGAttrs = [ 'transform' , 'opacity' , 'filter' ] ;
2018-05-16 00:53:27 +00:00
2010-06-30 18:27:36 +00:00
while ( i -- ) {
var elem = elems [ i ] ;
2018-05-16 08:32:44 +00:00
if ( elem == null ) { continue ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// Set x,y vals on elements that don't have them
2018-05-16 08:32:44 +00:00
if ( ( attr === 'x' || attr === 'y' ) && noXYElems . indexOf ( elem . tagName ) >= 0 ) {
2010-06-30 18:27:36 +00:00
var bbox = getStrokedBBox ( [ elem ] ) ;
2018-05-16 08:32:44 +00:00
var diffX = attr === 'x' ? newValue - bbox . x : 0 ;
var diffY = attr === 'y' ? newValue - bbox . y : 0 ;
canvas . moveSelectedElements ( diffX * currentZoom , diffY * currentZoom , true ) ;
2010-06-22 14:52:51 +00:00
continue ;
}
2018-05-16 00:53:27 +00:00
2010-10-05 18:50:21 +00:00
// only allow the transform/opacity/filter attribute to change on <g> elements, slightly hacky
2014-01-31 10:40:52 +00:00
// TODO: FIXME: This doesn't seem right. Where's the body of this if statement?
2018-05-16 08:32:44 +00:00
if ( elem . tagName === 'g' && goodGAttrs . indexOf ( attr ) >= 0 ) { }
2014-04-08 03:21:22 +00:00
var oldval = attr === '#text' ? elem . textContent : elem . getAttribute ( attr ) ;
2018-05-16 08:32:44 +00:00
if ( oldval == null ) { oldval = '' ; }
2010-10-05 18:50:21 +00:00
if ( oldval !== String ( newValue ) ) {
2018-05-16 08:32:44 +00:00
if ( attr === '#text' ) {
// var oldW = svgedit.utilities.getBBox(elem).width;
2010-06-22 14:52:51 +00:00
elem . textContent = newValue ;
2018-05-16 00:53:27 +00:00
2011-01-31 20:27:05 +00:00
// FF bug occurs on on rotated elements
2018-05-16 08:32:44 +00:00
if ( ( /rotate/ ) . test ( elem . getAttribute ( 'transform' ) ) ) {
2011-01-31 20:02:24 +00:00
elem = ffClone ( elem ) ;
}
2010-06-22 14:52:51 +00:00
// Hoped to solve the issue of moving text with text-anchor="start",
// but this doesn't actually fix it. Hopefully on the right track, though. -Fyrd
2018-05-16 08:32:44 +00:00
// var box = getBBox(elem), left = box.x, top = box.y, width = box.width,
// height = box.height, dx = width - oldW, dy = 0;
// var angle = svgedit.utilities.getRotationAngle(elem, true);
// if (angle) {
// var r = Math.sqrt(dx * dx + dy * dy);
// var theta = Math.atan2(dy, dx) - angle;
// dx = r * Math.cos(theta);
// dy = r * Math.sin(theta);
//
// elem.setAttribute('x', elem.getAttribute('x') - dx);
// elem.setAttribute('y', elem.getAttribute('y') - dy);
// }
} else if ( attr === '#href' ) {
2010-08-16 17:53:15 +00:00
setHref ( elem , newValue ) ;
2018-05-16 08:32:44 +00:00
} else { elem . setAttribute ( attr , newValue ) ; }
2013-09-21 22:19:24 +00:00
// Go into "select" mode for text changes
// NOTE: Important that this happens AFTER elem.setAttribute() or else attributes like
// font-size can get reset to their old value, ultimately by svgEditor.updateContextPanel(),
// after calling textActions.toSelectMode() below
2018-05-16 08:32:44 +00:00
if ( currentMode === 'textedit' && attr !== '#text' && elem . textContent . length ) {
2013-09-21 22:19:24 +00:00
textActions . toSelectMode ( elem ) ;
}
2018-05-16 08:32:44 +00:00
// if (i === 0) {
// selectedBBoxes[0] = svgedit.utilities.getBBox(elem);
// }
2010-06-22 14:52:51 +00:00
// Use the Firefox ffClone hack for text elements with gradients or
2018-05-16 00:53:27 +00:00
// where other text attributes are changed.
2018-05-16 08:32:44 +00:00
if ( svgedit . browser . isGecko ( ) && elem . nodeName === 'text' && ( /rotate/ ) . test ( elem . getAttribute ( 'transform' ) ) ) {
2014-02-17 06:48:40 +00:00
if ( String ( newValue ) . indexOf ( 'url' ) === 0 || ( [ 'font-size' , 'font-family' , 'x' , 'y' ] . indexOf ( attr ) >= 0 && elem . textContent ) ) {
2010-06-22 14:52:51 +00:00
elem = ffClone ( elem ) ;
2010-02-02 05:48:33 +00:00
}
2010-06-22 14:52:51 +00:00
}
// Timeout needed for Opera & Firefox
// codedread: it is now possible for this function to be called with elements
// that are not in the selectedElements array, we need to only request a
// selector if the element is in that array
2010-09-29 18:37:51 +00:00
if ( selectedElements . indexOf ( elem ) >= 0 ) {
2018-05-16 08:32:44 +00:00
setTimeout ( function ( ) {
2010-06-22 14:52:51 +00:00
// Due to element replacement, this element may no longer
// be part of the DOM
2018-05-16 08:32:44 +00:00
if ( ! elem . parentNode ) { return ; }
2010-06-22 14:52:51 +00:00
selectorManager . requestSelector ( elem ) . resize ( ) ;
2013-02-15 15:51:58 +00:00
} , 0 ) ;
2010-06-22 14:52:51 +00:00
}
// if this element was rotated, and we changed the position of this element
2018-05-16 00:53:27 +00:00
// we need to update the rotational transform attribute
2013-02-14 15:19:46 +00:00
var angle = svgedit . utilities . getRotationAngle ( elem ) ;
2018-05-16 08:32:44 +00:00
if ( angle !== 0 && attr !== 'transform' ) {
2013-02-14 15:19:46 +00:00
var tlist = svgedit . transformlist . getTransformList ( elem ) ;
2010-06-22 14:52:51 +00:00
var n = tlist . numberOfItems ;
while ( n -- ) {
var xform = tlist . getItem ( n ) ;
2018-05-16 08:32:44 +00:00
if ( xform . type === 4 ) {
2010-06-22 14:52:51 +00:00
// remove old rotate
tlist . removeItem ( n ) ;
2018-05-16 00:53:27 +00:00
2011-02-24 16:13:26 +00:00
var box = svgedit . utilities . getBBox ( elem ) ;
2018-05-16 08:32:44 +00:00
var center = svgedit . math . transformPoint ( box . x + box . width / 2 , box . y + box . height / 2 , svgedit . math . transformListToTransform ( tlist ) . matrix ) ;
2010-06-22 14:52:51 +00:00
var cx = center . x ,
cy = center . y ;
var newrot = svgroot . createSVGTransform ( ) ;
newrot . setRotate ( angle , cx , cy ) ;
tlist . insertItemBefore ( newrot , n ) ;
break ;
2009-08-23 03:18:59 +00:00
}
}
2010-06-22 14:52:51 +00:00
}
} // if oldValue != newValue
} // for each elem
} ;
2010-06-21 18:06:21 +00:00
2010-06-29 20:43:44 +00:00
// Function: changeSelectedAttribute
// Change the given/selected element and add the original value to the history stack
2010-06-22 14:52:51 +00:00
// If you want to change all selectedElements, ignore the elems argument.
// If you want to change only a subset of selectedElements, then send the
// subset to this function in the elems argument.
2018-05-16 00:53:27 +00:00
//
2010-06-29 20:43:44 +00:00
// Parameters:
// attr - String with the attribute name
// newValue - String or number with the new attribute value
// elems - The DOM elements to apply the change to
2018-05-16 08:32:44 +00:00
var changeSelectedAttribute = this . changeSelectedAttribute = function ( attr , val , elems ) {
2014-02-17 06:48:40 +00:00
elems = elems || selectedElements ;
2010-11-12 19:08:29 +00:00
canvas . undoMgr . beginUndoableChange ( attr , elems ) ;
2018-05-16 08:32:44 +00:00
// var i = elems.length;
2009-09-04 05:10:48 +00:00
2010-06-30 18:27:36 +00:00
changeSelectedAttributeNoUndo ( attr , val , elems ) ;
2010-06-22 14:52:51 +00:00
2010-11-12 19:08:29 +00:00
var batchCmd = canvas . undoMgr . finishUndoableChange ( ) ;
2018-05-16 00:53:27 +00:00
if ( ! batchCmd . isEmpty ( ) ) {
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
}
} ;
2010-06-29 20:43:44 +00:00
// Function: deleteSelectedElements
2018-05-16 00:53:27 +00:00
// Removes all selected elements from the DOM and adds the change to the
2010-06-29 20:43:44 +00:00
// history stack
2018-05-16 08:32:44 +00:00
this . deleteSelectedElements = function ( ) {
2014-02-17 06:48:40 +00:00
var i ;
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Delete Elements' ) ;
2010-06-22 14:52:51 +00:00
var len = selectedElements . length ;
2018-05-16 08:32:44 +00:00
var selectedCopy = [ ] ; // selectedElements is being deleted
2018-01-18 12:37:49 +00:00
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2010-06-22 14:52:51 +00:00
var selected = selectedElements [ i ] ;
2018-05-16 08:32:44 +00:00
if ( selected == null ) { break ; }
2010-06-22 14:52:51 +00:00
var parent = selected . parentNode ;
var t = selected ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// this will unselect the element and remove the selectedOutline
selectorManager . releaseSelector ( t ) ;
2018-05-16 00:53:27 +00:00
2011-02-23 16:34:59 +00:00
// Remove the path if present.
svgedit . path . removePath _ ( t . id ) ;
2018-05-16 00:53:27 +00:00
2010-12-15 19:42:48 +00:00
// Get the parent if it's a single-child anchor
2013-02-15 15:51:58 +00:00
if ( parent . tagName === 'a' && parent . childNodes . length === 1 ) {
2010-12-15 19:42:48 +00:00
t = parent ;
parent = parent . parentNode ;
}
2018-05-16 00:53:27 +00:00
2010-11-13 09:58:51 +00:00
var nextSibling = t . nextSibling ;
2010-06-22 14:52:51 +00:00
var elem = parent . removeChild ( t ) ;
2018-05-16 08:32:44 +00:00
selectedCopy . push ( selected ) ; // for the copy
2010-11-13 09:58:51 +00:00
batchCmd . addSubCommand ( new RemoveElementCommand ( elem , nextSibling , parent ) ) ;
2010-06-22 14:52:51 +00:00
}
2018-01-18 12:37:49 +00:00
selectedElements = [ ] ;
2018-05-16 08:32:44 +00:00
if ( ! batchCmd . isEmpty ( ) ) { addCommandToHistory ( batchCmd ) ; }
2014-04-08 03:21:22 +00:00
call ( 'changed' , selectedCopy ) ;
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2010-06-22 14:52:51 +00:00
} ;
2010-08-17 18:09:50 +00:00
// Function: cutSelectedElements
2018-05-16 00:53:27 +00:00
// Removes all selected elements from the DOM and adds the change to the
2010-08-17 18:09:50 +00:00
// history stack. Remembers removed elements on the clipboard
2018-05-16 08:32:44 +00:00
this . cutSelectedElements = function ( ) {
2018-01-18 12:37:49 +00:00
svgCanvas . copySelectedElements ( ) ;
svgCanvas . deleteSelectedElements ( ) ;
2010-08-17 18:09:50 +00:00
} ;
// Function: copySelectedElements
// Remembers the current selected elements on the clipboard
2018-05-16 08:32:44 +00:00
this . copySelectedElements = function ( ) {
2018-01-18 12:37:49 +00:00
localStorage . setItem ( 'svgedit_clipboard' , JSON . stringify (
2018-05-16 08:32:44 +00:00
selectedElements . map ( function ( x ) { return getJsonFromSvgElement ( x ) ; } )
2018-01-18 12:37:49 +00:00
) ) ;
$ ( '#cmenu_canvas' ) . enableContextMenuItems ( '#paste,#paste_in_place' ) ;
2010-08-17 18:09:50 +00:00
} ;
2018-05-16 08:32:44 +00:00
this . pasteElements = function ( type , x , y ) {
2018-01-18 12:37:49 +00:00
var cb = JSON . parse ( localStorage . getItem ( 'svgedit_clipboard' ) ) ;
2010-08-17 18:09:50 +00:00
var len = cb . length ;
2018-05-16 08:32:44 +00:00
if ( ! len ) { return ; }
2018-05-16 00:53:27 +00:00
2010-08-17 18:09:50 +00:00
var pasted = [ ] ;
2013-02-17 08:21:07 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Paste elements' ) ;
2018-05-16 08:32:44 +00:00
// var drawing = getCurrentDrawing();
2018-01-18 12:37:49 +00:00
var changedIDs = { } ;
// Recursively replace IDs and record the changes
2018-05-16 08:32:44 +00:00
function checkIDs ( elem ) {
if ( elem . attr && elem . attr . id ) {
2018-01-18 12:37:49 +00:00
changedIDs [ elem . attr . id ] = getNextId ( ) ;
elem . attr . id = changedIDs [ elem . attr . id ] ;
}
2018-05-16 08:32:44 +00:00
if ( elem . children ) elem . children . forEach ( checkIDs ) ;
2018-01-18 12:37:49 +00:00
}
cb . forEach ( checkIDs ) ;
// Give extensions like the connector extension a chance to reflect new IDs and remove invalid elements
2018-05-16 08:32:44 +00:00
runExtensions ( 'IDsUpdated' , { elems : cb , changes : changedIDs } , true ) . forEach ( function ( extChanges ) {
if ( ! extChanges || ! ( 'remove' in extChanges ) ) return ;
2018-01-18 12:37:49 +00:00
2018-05-16 08:32:44 +00:00
extChanges . remove . forEach ( function ( removeID ) {
cb = cb . filter ( function ( cbItem ) {
return cbItem . attr . id !== removeID ;
2018-01-18 12:37:49 +00:00
} ) ;
} ) ;
} ) ;
2010-08-17 18:09:50 +00:00
2016-05-04 13:38:29 +00:00
// Move elements to lastClickPoint
2010-08-17 18:09:50 +00:00
while ( len -- ) {
var elem = cb [ len ] ;
2018-05-16 08:32:44 +00:00
if ( ! elem ) { continue ; }
2010-08-17 18:09:50 +00:00
2018-01-18 12:37:49 +00:00
var copy = addSvgElementFromJson ( elem ) ;
2010-08-17 18:09:50 +00:00
pasted . push ( copy ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( copy ) ) ;
2016-03-14 12:16:30 +00:00
restoreRefElems ( copy ) ;
2010-08-17 18:09:50 +00:00
}
2018-05-16 00:53:27 +00:00
2010-09-17 20:33:33 +00:00
selectOnly ( pasted ) ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( type !== 'in_place' ) {
2018-05-16 08:32:44 +00:00
var ctrX , ctrY ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( ! type ) {
2018-05-16 08:32:44 +00:00
ctrX = lastClickPoint . x ;
ctrY = lastClickPoint . y ;
2013-02-15 15:51:58 +00:00
} else if ( type === 'point' ) {
2018-05-16 08:32:44 +00:00
ctrX = x ;
ctrY = y ;
2018-05-16 00:53:27 +00:00
}
2010-08-17 18:09:50 +00:00
var bbox = getStrokedBBox ( pasted ) ;
2018-05-16 08:32:44 +00:00
var cx = ctrX - ( bbox . x + bbox . width / 2 ) ,
cy = ctrY - ( bbox . y + bbox . height / 2 ) ,
2010-08-17 18:09:50 +00:00
dx = [ ] ,
dy = [ ] ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( pasted , function ( i , item ) {
2010-08-17 18:09:50 +00:00
dx . push ( cx ) ;
dy . push ( cy ) ;
} ) ;
2018-05-16 00:53:27 +00:00
2010-08-17 18:09:50 +00:00
var cmd = canvas . moveSelectedElements ( dx , dy , false ) ;
2018-05-16 08:32:44 +00:00
if ( cmd ) batchCmd . addSubCommand ( cmd ) ;
2010-08-17 18:09:50 +00:00
}
2011-01-28 20:11:18 +00:00
2010-08-17 18:09:50 +00:00
addCommandToHistory ( batchCmd ) ;
2014-04-08 03:21:22 +00:00
call ( 'changed' , pasted ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-08-17 18:09:50 +00:00
2010-06-29 20:43:44 +00:00
// Function: groupSelectedElements
// Wraps all the selected elements in a group (g) element
2010-12-01 17:54:11 +00:00
2018-05-16 00:53:27 +00:00
// Parameters:
2010-12-01 17:54:11 +00:00
// type - type of element to group into, defaults to <g>
2018-05-16 08:32:44 +00:00
this . groupSelectedElements = function ( type , urlArg ) {
if ( ! type ) { type = 'g' ; }
var cmdStr = '' ;
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
switch ( type ) {
2018-05-16 00:53:27 +00:00
case 'a' :
2018-05-16 08:32:44 +00:00
cmdStr = 'Make hyperlink' ;
2018-05-16 00:53:27 +00:00
var url = '' ;
if ( arguments . length > 1 ) {
url = urlArg ;
}
break ;
default :
type = 'g' ;
2018-05-16 08:32:44 +00:00
cmdStr = 'Group Elements' ;
2018-05-16 00:53:27 +00:00
break ;
2010-12-01 17:54:11 +00:00
}
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
var batchCmd = new svgedit . history . BatchCommand ( cmdStr ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// create and insert the group element
var g = addSvgElementFromJson ( {
2018-05-16 08:32:44 +00:00
'element' : type ,
'attr' : {
'id' : getNextId ( )
}
} ) ;
2013-02-15 15:51:58 +00:00
if ( type === 'a' ) {
2010-12-01 17:54:11 +00:00
setHref ( g , url ) ;
}
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( g ) ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// now move all children into the group
var i = selectedElements . length ;
while ( i -- ) {
var elem = selectedElements [ i ] ;
2018-05-16 08:32:44 +00:00
if ( elem == null ) { continue ; }
2018-05-16 00:53:27 +00:00
2010-12-01 17:54:11 +00:00
if ( elem . parentNode . tagName === 'a' && elem . parentNode . childNodes . length === 1 ) {
elem = elem . parentNode ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var oldNextSibling = elem . nextSibling ;
var oldParent = elem . parentNode ;
g . appendChild ( elem ) ;
2018-05-16 00:53:27 +00:00
batchCmd . addSubCommand ( new svgedit . history . MoveElementCommand ( elem , oldNextSibling , oldParent ) ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 08:32:44 +00:00
if ( ! batchCmd . isEmpty ( ) ) { addCommandToHistory ( batchCmd ) ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// update selection
2010-09-17 20:33:33 +00:00
selectOnly ( [ g ] , true ) ;
2010-06-22 14:52:51 +00:00
} ;
2010-10-20 18:07:30 +00:00
// Function: pushGroupProperties
// Pushes all appropriate parent group properties down to its children, then
// removes them from the group
2018-05-16 08:32:44 +00:00
var pushGroupProperties = this . pushGroupProperties = function ( g , undoable ) {
2010-10-20 18:07:30 +00:00
var children = g . childNodes ;
var len = children . length ;
2014-04-08 03:21:22 +00:00
var xform = g . getAttribute ( 'transform' ) ;
2010-10-20 18:07:30 +00:00
2013-02-14 15:19:46 +00:00
var glist = svgedit . transformlist . getTransformList ( g ) ;
var m = svgedit . math . transformListToTransform ( glist ) . matrix ;
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Push group properties' ) ;
2010-10-20 18:07:30 +00:00
// TODO: get all fill/stroke properties from the group that we are about to destroy
2018-05-16 00:53:27 +00:00
// "fill", "fill-opacity", "fill-rule", "stroke", "stroke-dasharray", "stroke-dashoffset",
// "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity",
2010-10-20 18:07:30 +00:00
// "stroke-width"
// and then for each child, if they do not have the attribute (or the value is 'inherit')
// then set the child's attribute
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
var i = 0 ;
2013-02-14 15:19:46 +00:00
var gangle = svgedit . utilities . getRotationAngle ( g ) ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
var gattrs = $ ( g ) . attr ( [ 'filter' , 'opacity' ] ) ;
2014-02-17 06:48:40 +00:00
var gfilter , gblur , changes ;
2016-05-04 13:38:29 +00:00
var drawing = getCurrentDrawing ( ) ;
2018-05-16 00:53:27 +00:00
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; i ++ ) {
2010-10-20 18:07:30 +00:00
var elem = children [ i ] ;
2018-05-16 00:53:27 +00:00
2018-05-16 08:32:44 +00:00
if ( elem . nodeType !== 1 ) { continue ; }
2018-05-16 00:53:27 +00:00
2013-02-15 15:51:58 +00:00
if ( gattrs . opacity !== null && gattrs . opacity !== 1 ) {
2018-05-16 08:32:44 +00:00
// var c_opac = elem.getAttribute('opacity') || 1;
var newOpac = Math . round ( ( elem . getAttribute ( 'opacity' ) || 1 ) * gattrs . opacity * 100 ) / 100 ;
changeSelectedAttribute ( 'opacity' , newOpac , [ elem ] ) ;
2010-10-20 18:07:30 +00:00
}
2013-02-15 15:51:58 +00:00
if ( gattrs . filter ) {
2010-10-20 18:07:30 +00:00
var cblur = this . getBlur ( elem ) ;
2018-05-16 08:32:44 +00:00
var origCblur = cblur ;
if ( ! gblur ) { gblur = this . getBlur ( g ) ; }
2013-02-15 15:51:58 +00:00
if ( cblur ) {
2010-10-20 18:07:30 +00:00
// Is this formula correct?
2014-02-17 06:48:40 +00:00
cblur = Number ( gblur ) + Number ( cblur ) ;
2013-02-15 15:51:58 +00:00
} else if ( cblur === 0 ) {
2010-10-20 18:07:30 +00:00
cblur = gblur ;
}
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// If child has no current filter, get group's filter or clone it.
2018-05-16 08:32:44 +00:00
if ( ! origCblur ) {
2010-10-20 18:07:30 +00:00
// Set group's filter to use first child's ID
2013-02-15 15:51:58 +00:00
if ( ! gfilter ) {
2013-02-14 15:19:46 +00:00
gfilter = svgedit . utilities . getRefElem ( gattrs . filter ) ;
2010-10-20 18:07:30 +00:00
} else {
// Clone the group's filter
2016-05-04 13:38:29 +00:00
gfilter = drawing . copyElem ( gfilter ) ;
2013-02-14 15:19:46 +00:00
svgedit . utilities . findDefs ( ) . appendChild ( gfilter ) ;
2010-10-20 18:07:30 +00:00
}
} else {
2013-02-14 15:19:46 +00:00
gfilter = svgedit . utilities . getRefElem ( elem . getAttribute ( 'filter' ) ) ;
2010-10-20 18:07:30 +00:00
}
// Change this in future for different filters
2018-05-16 08:32:44 +00:00
var suffix = ( gfilter . firstChild . tagName === 'feGaussianBlur' ) ? 'blur' : 'filter' ;
2010-10-20 18:07:30 +00:00
gfilter . id = elem . id + '_' + suffix ;
changeSelectedAttribute ( 'filter' , 'url(#' + gfilter . id + ')' , [ elem ] ) ;
2018-05-16 00:53:27 +00:00
// Update blur value
2013-02-15 15:51:58 +00:00
if ( cblur ) {
2010-10-20 18:07:30 +00:00
changeSelectedAttribute ( 'stdDeviation' , cblur , [ gfilter . firstChild ] ) ;
canvas . setBlurOffsets ( gfilter , cblur ) ;
}
}
2018-05-16 00:53:27 +00:00
2013-02-14 15:19:46 +00:00
var chtlist = svgedit . transformlist . getTransformList ( elem ) ;
2010-10-20 18:07:30 +00:00
// Don't process gradient transforms
2018-05-16 08:32:44 +00:00
if ( ~ elem . tagName . indexOf ( 'Gradient' ) ) { chtlist = null ; }
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// Hopefully not a problem to add this. Necessary for elements like <desc/>
2018-05-16 08:32:44 +00:00
if ( ! chtlist ) { continue ; }
2018-05-16 00:53:27 +00:00
2010-12-21 19:36:51 +00:00
// Apparently <defs> can get get a transformlist, but we don't want it to have one!
2018-05-16 08:32:44 +00:00
if ( elem . tagName === 'defs' ) { continue ; }
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
if ( glist . numberOfItems ) {
// TODO: if the group's transform is just a rotate, we can always transfer the
// rotate() down to the children (collapsing consecutive rotates and factoring
// out any translates)
2018-05-16 08:32:44 +00:00
if ( gangle && glist . numberOfItems === 1 ) {
2010-10-20 18:07:30 +00:00
// [Rg] [Rc] [Mc]
// we want [Tr] [Rc2] [Mc] where:
2018-05-16 00:53:27 +00:00
// - [Rc2] is at the child's current center but has the
2014-01-31 10:40:52 +00:00
// sum of the group and child's rotation angles
2018-05-16 00:53:27 +00:00
// - [Tr] is the equivalent translation that this child
2014-01-31 10:40:52 +00:00
// undergoes if the group wasn't there
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// [Tr] = [Rg] [Rc] [Rc2_inv]
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// get group's rotation matrix (Rg)
var rgm = glist . getItem ( 0 ) . matrix ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// get child's rotation matrix (Rc)
var rcm = svgroot . createSVGMatrix ( ) ;
2013-02-14 15:19:46 +00:00
var cangle = svgedit . utilities . getRotationAngle ( elem ) ;
2010-10-20 18:07:30 +00:00
if ( cangle ) {
rcm = chtlist . getItem ( 0 ) . matrix ;
}
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// get child's old center of rotation
2011-02-24 16:13:26 +00:00
var cbox = svgedit . utilities . getBBox ( elem ) ;
2013-02-14 15:19:46 +00:00
var ceqm = svgedit . math . transformListToTransform ( chtlist ) . matrix ;
2018-05-16 08:32:44 +00:00
var coldc = svgedit . math . transformPoint ( cbox . x + cbox . width / 2 , cbox . y + cbox . height / 2 , ceqm ) ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// sum group and child's angles
var sangle = gangle + cangle ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// get child's rotation at the old center (Rc2_inv)
var r2 = svgroot . createSVGTransform ( ) ;
r2 . setRotate ( sangle , coldc . x , coldc . y ) ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// calculate equivalent translate
2013-02-14 15:19:46 +00:00
var trm = svgedit . math . matrixMultiply ( rgm , rcm , r2 . matrix . inverse ( ) ) ;
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
// set up tlist
if ( cangle ) {
chtlist . removeItem ( 0 ) ;
}
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
if ( sangle ) {
2013-02-15 15:51:58 +00:00
if ( chtlist . numberOfItems ) {
2010-10-20 18:07:30 +00:00
chtlist . insertItemBefore ( r2 , 0 ) ;
} else {
chtlist . appendItem ( r2 ) ;
}
}
if ( trm . e || trm . f ) {
var tr = svgroot . createSVGTransform ( ) ;
tr . setTranslate ( trm . e , trm . f ) ;
2013-02-15 15:51:58 +00:00
if ( chtlist . numberOfItems ) {
2010-10-20 18:07:30 +00:00
chtlist . insertItemBefore ( tr , 0 ) ;
} else {
chtlist . appendItem ( tr ) ;
}
}
2013-02-15 15:51:58 +00:00
} else { // more complicated than just a rotate
2010-10-20 18:07:30 +00:00
// transfer the group's transform down to each child and then
2018-05-16 00:53:27 +00:00
// call svgedit.recalculate.recalculateDimensions()
2014-04-08 03:21:22 +00:00
var oldxform = elem . getAttribute ( 'transform' ) ;
2014-02-17 06:48:40 +00:00
changes = { } ;
changes . transform = oldxform || '' ;
2010-10-20 18:07:30 +00:00
var newxform = svgroot . createSVGTransform ( ) ;
// [ gm ] [ chm ] = [ chm ] [ gm' ]
2018-05-16 08:32:44 +00:00
// [ gm' ] = [ chmInv ] [ gm ] [ chm ]
2013-02-14 15:19:46 +00:00
var chm = svgedit . math . transformListToTransform ( chtlist ) . matrix ,
2018-05-16 08:32:44 +00:00
chmInv = chm . inverse ( ) ;
var gm = svgedit . math . matrixMultiply ( chmInv , m , chm ) ;
2010-10-20 18:07:30 +00:00
newxform . setMatrix ( gm ) ;
chtlist . appendItem ( newxform ) ;
}
2013-02-20 06:29:25 +00:00
var cmd = svgedit . recalculate . recalculateDimensions ( elem ) ;
2018-05-16 08:32:44 +00:00
if ( cmd ) { batchCmd . addSubCommand ( cmd ) ; }
2010-10-20 18:07:30 +00:00
}
}
// remove transform and make it undo-able
if ( xform ) {
2014-02-17 06:48:40 +00:00
changes = { } ;
2014-04-08 03:21:22 +00:00
changes . transform = xform ;
g . setAttribute ( 'transform' , '' ) ;
2018-05-16 00:53:27 +00:00
g . removeAttribute ( 'transform' ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . ChangeElementCommand ( g , changes ) ) ;
2010-10-20 18:07:30 +00:00
}
2018-05-16 00:53:27 +00:00
2010-10-20 18:07:30 +00:00
if ( undoable && ! batchCmd . isEmpty ( ) ) {
return batchCmd ;
}
2013-02-15 15:51:58 +00:00
} ;
2010-10-20 18:07:30 +00:00
2010-06-29 20:43:44 +00:00
// Function: ungroupSelectedElement
// Unwraps all the elements in a selected group (g) element. This requires
// significant recalculations to apply group's transforms, etc to its children
2018-05-16 08:32:44 +00:00
this . ungroupSelectedElement = function ( ) {
2010-06-22 14:52:51 +00:00
var g = selectedElements [ 0 ] ;
2014-05-22 01:28:32 +00:00
if ( ! g ) {
return ;
}
2013-02-15 15:51:58 +00:00
if ( $ ( g ) . data ( 'gsvg' ) || $ ( g ) . data ( 'symbol' ) ) {
2010-07-29 15:09:49 +00:00
// Is svg, so actually convert to group
2010-10-04 16:01:58 +00:00
convertToGroup ( g ) ;
return ;
2013-02-15 15:51:58 +00:00
}
if ( g . tagName === 'use' ) {
2010-10-04 16:01:58 +00:00
// Somehow doesn't have data set, so retrieve
2013-02-14 15:19:46 +00:00
var symbol = svgedit . utilities . getElem ( getHref ( g ) . substr ( 1 ) ) ;
2011-02-10 18:10:05 +00:00
$ ( g ) . data ( 'symbol' , symbol ) . data ( 'ref' , symbol ) ;
2010-07-29 15:09:49 +00:00
convertToGroup ( g ) ;
return ;
}
2018-05-16 08:32:44 +00:00
var parentsA = $ ( g ) . parents ( 'a' ) ;
if ( parentsA . length ) {
g = parentsA [ 0 ] ;
2010-12-01 17:54:11 +00:00
}
2018-05-16 00:53:27 +00:00
2010-12-01 17:54:11 +00:00
// Look for parent "a"
2014-04-08 03:21:22 +00:00
if ( g . tagName === 'g' || g . tagName === 'a' ) {
var batchCmd = new svgedit . history . BatchCommand ( 'Ungroup Elements' ) ;
2010-10-20 18:07:30 +00:00
var cmd = pushGroupProperties ( g , true ) ;
2018-05-16 08:32:44 +00:00
if ( cmd ) { batchCmd . addSubCommand ( cmd ) ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var parent = g . parentNode ;
2010-07-29 15:09:49 +00:00
var anchor = g . nextSibling ;
2010-06-22 14:52:51 +00:00
var children = new Array ( g . childNodes . length ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var i = 0 ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
while ( g . firstChild ) {
var elem = g . firstChild ;
2009-09-05 22:09:42 +00:00
var oldNextSibling = elem . nextSibling ;
var oldParent = elem . parentNode ;
2018-05-16 00:53:27 +00:00
2010-07-22 19:10:51 +00:00
// Remove child title elements
2013-02-15 15:51:58 +00:00
if ( elem . tagName === 'title' ) {
2010-11-13 09:58:51 +00:00
var nextSibling = elem . nextSibling ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( elem , nextSibling , oldParent ) ) ;
2010-07-22 19:10:51 +00:00
oldParent . removeChild ( elem ) ;
continue ;
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
children [ i ++ ] = elem = parent . insertBefore ( elem , anchor ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . MoveElementCommand ( elem , oldNextSibling , oldParent ) ) ;
2010-06-22 14:52:51 +00:00
}
2010-05-28 19:17:30 +00:00
2018-05-16 00:53:27 +00:00
// remove the group from the selection
2010-06-30 18:27:36 +00:00
clearSelection ( ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// delete the group element (but make undo-able)
2010-11-13 09:58:51 +00:00
var gNextSibling = g . nextSibling ;
2010-06-22 14:52:51 +00:00
g = parent . removeChild ( g ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . RemoveElementCommand ( g , gNextSibling , parent ) ) ;
2010-06-22 14:52:51 +00:00
2018-05-16 08:32:44 +00:00
if ( ! batchCmd . isEmpty ( ) ) { addCommandToHistory ( batchCmd ) ; }
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// update selection
2010-06-30 18:27:36 +00:00
addToSelection ( children ) ;
}
} ;
// Function: moveToTopSelectedElement
// Repositions the selected element to the bottom in the DOM to appear on top of
// other elements
2018-05-16 08:32:44 +00:00
this . moveToTopSelectedElement = function ( ) {
2010-06-30 18:27:36 +00:00
var selected = selectedElements [ 0 ] ;
if ( selected != null ) {
var t = selected ;
var oldParent = t . parentNode ;
var oldNextSibling = t . nextSibling ;
t = t . parentNode . appendChild ( t ) ;
2011-01-30 16:45:29 +00:00
// If the element actually moved position, add the command and fire the changed
// event handler.
2018-05-16 08:32:44 +00:00
if ( oldNextSibling !== t . nextSibling ) {
2014-04-08 03:21:22 +00:00
addCommandToHistory ( new svgedit . history . MoveElementCommand ( t , oldNextSibling , oldParent , 'top' ) ) ;
call ( 'changed' , [ t ] ) ;
2011-01-30 16:45:29 +00:00
}
2010-06-22 14:52:51 +00:00
}
} ;
2010-06-30 18:27:36 +00:00
// Function: moveToBottomSelectedElement
2018-05-16 00:53:27 +00:00
// Repositions the selected element to the top in the DOM to appear under
2010-06-30 18:27:36 +00:00
// other elements
2018-05-16 08:32:44 +00:00
this . moveToBottomSelectedElement = function ( ) {
2010-06-30 18:27:36 +00:00
var selected = selectedElements [ 0 ] ;
if ( selected != null ) {
var t = selected ;
var oldParent = t . parentNode ;
var oldNextSibling = t . nextSibling ;
var firstChild = t . parentNode . firstChild ;
2018-05-16 08:32:44 +00:00
if ( firstChild . tagName === 'title' ) {
2010-06-30 18:27:36 +00:00
firstChild = firstChild . nextSibling ;
}
// This can probably be removed, as the defs should not ever apppear
// inside a layer group
2018-05-16 08:32:44 +00:00
if ( firstChild . tagName === 'defs' ) {
2010-06-30 18:27:36 +00:00
firstChild = firstChild . nextSibling ;
}
t = t . parentNode . insertBefore ( t , firstChild ) ;
2011-01-30 16:45:29 +00:00
// If the element actually moved position, add the command and fire the changed
// event handler.
2018-05-16 08:32:44 +00:00
if ( oldNextSibling !== t . nextSibling ) {
2014-04-08 03:21:22 +00:00
addCommandToHistory ( new svgedit . history . MoveElementCommand ( t , oldNextSibling , oldParent , 'bottom' ) ) ;
call ( 'changed' , [ t ] ) ;
2011-01-30 16:45:29 +00:00
}
2010-06-30 18:27:36 +00:00
}
} ;
2010-06-22 18:17:42 +00:00
2010-08-20 14:52:55 +00:00
// Function: moveUpDownSelected
// Moves the select element up or down the stack, based on the visibly
// intersecting elements
//
2018-05-16 00:53:27 +00:00
// Parameters:
2010-08-20 14:52:55 +00:00
// dir - String that's either 'Up' or 'Down'
2018-05-16 08:32:44 +00:00
this . moveUpDownSelected = function ( dir ) {
2010-08-20 14:52:55 +00:00
var selected = selectedElements [ 0 ] ;
2018-05-16 08:32:44 +00:00
if ( ! selected ) { return ; }
2018-05-16 00:53:27 +00:00
2010-08-20 14:52:55 +00:00
curBBoxes = [ ] ;
2018-05-16 08:32:44 +00:00
var closest , foundCur ;
2010-08-20 14:52:55 +00:00
// jQuery sorts this list
var list = $ ( getIntersectionList ( getStrokedBBox ( [ selected ] ) ) ) . toArray ( ) ;
2018-05-16 08:32:44 +00:00
if ( dir === 'Down' ) { list . reverse ( ) ; }
2010-08-20 14:52:55 +00:00
2018-05-16 08:32:44 +00:00
$ . each ( list , function ( ) {
if ( ! foundCur ) {
if ( this === selected ) {
foundCur = true ;
2010-08-20 14:52:55 +00:00
}
return ;
}
closest = this ;
return false ;
} ) ;
2018-05-16 08:32:44 +00:00
if ( ! closest ) { return ; }
2018-05-16 00:53:27 +00:00
2010-08-20 14:52:55 +00:00
var t = selected ;
var oldParent = t . parentNode ;
var oldNextSibling = t . nextSibling ;
2018-05-16 08:32:44 +00:00
$ ( closest ) [ dir === 'Down' ? 'before' : 'after' ] ( t ) ;
2011-01-30 16:45:29 +00:00
// If the element actually moved position, add the command and fire the changed
// event handler.
2018-05-16 08:32:44 +00:00
if ( oldNextSibling !== t . nextSibling ) {
2014-04-08 03:21:22 +00:00
addCommandToHistory ( new svgedit . history . MoveElementCommand ( t , oldNextSibling , oldParent , 'Move ' + dir ) ) ;
call ( 'changed' , [ t ] ) ;
2011-01-30 16:45:29 +00:00
}
} ;
2010-08-20 14:52:55 +00:00
2010-06-30 18:27:36 +00:00
// Function: moveSelectedElements
2018-05-16 00:53:27 +00:00
// Moves selected elements on the X/Y axis
2010-06-22 18:17:42 +00:00
//
2010-06-30 18:27:36 +00:00
// Parameters:
// dx - Float with the distance to move on the x-axis
// dy - Float with the distance to move on the y-axis
// undoable - Boolean indicating whether or not the action should be undoable
2010-06-22 18:17:42 +00:00
//
// Returns:
2010-06-30 18:27:36 +00:00
// Batch command for the move
2018-05-16 08:32:44 +00:00
this . moveSelectedElements = function ( dx , dy , undoable ) {
2010-06-30 18:27:36 +00:00
// if undoable is not sent, default to true
// if single values, scale them to the zoom
2018-05-16 08:32:44 +00:00
if ( dx . constructor !== Array ) {
dx /= currentZoom ;
dy /= currentZoom ;
2010-06-22 14:52:51 +00:00
}
2013-02-15 15:51:58 +00:00
undoable = undoable || true ;
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'position' ) ;
2010-06-22 14:52:51 +00:00
var i = selectedElements . length ;
while ( i -- ) {
var selected = selectedElements [ i ] ;
if ( selected != null ) {
2018-05-16 08:32:44 +00:00
// if (i === 0) {
// selectedBBoxes[0] = svgedit.utilities.getBBox(selected);
// }
// var b = {};
// for (var j in selectedBBoxes[i]) b[j] = selectedBBoxes[i][j];
// selectedBBoxes[i] = b;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
var xform = svgroot . createSVGTransform ( ) ;
2013-02-14 15:19:46 +00:00
var tlist = svgedit . transformlist . getTransformList ( selected ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// dx and dy could be arrays
2018-05-16 08:32:44 +00:00
if ( dx . constructor === Array ) {
// if (i === 0) {
// selectedBBoxes[0].x += dx[0];
// selectedBBoxes[0].y += dy[0];
// }
2013-02-15 16:51:48 +00:00
xform . setTranslate ( dx [ i ] , dy [ i ] ) ;
2010-06-22 14:52:51 +00:00
} else {
2018-05-16 08:32:44 +00:00
// if (i === 0) {
// selectedBBoxes[0].x += dx;
// selectedBBoxes[0].y += dy;
// }
2013-02-15 16:51:48 +00:00
xform . setTranslate ( dx , dy ) ;
2010-06-30 18:27:36 +00:00
}
2010-08-25 19:06:23 +00:00
2013-02-15 15:51:58 +00:00
if ( tlist . numberOfItems ) {
2010-08-25 19:06:23 +00:00
tlist . insertItemBefore ( xform , 0 ) ;
} else {
tlist . appendItem ( xform ) ;
}
2018-05-16 00:53:27 +00:00
2013-02-20 06:29:25 +00:00
var cmd = svgedit . recalculate . recalculateDimensions ( selected ) ;
2010-06-30 18:27:36 +00:00
if ( cmd ) {
batchCmd . addSubCommand ( cmd ) ;
}
2018-05-16 00:53:27 +00:00
2010-06-30 18:27:36 +00:00
selectorManager . requestSelector ( selected ) . resize ( ) ;
2009-12-09 21:00:35 +00:00
}
}
2010-06-30 18:27:36 +00:00
if ( ! batchCmd . isEmpty ( ) ) {
2014-02-17 06:48:40 +00:00
if ( undoable ) {
2010-06-30 18:27:36 +00:00
addCommandToHistory ( batchCmd ) ;
2014-02-17 06:48:40 +00:00
}
2014-04-08 03:21:22 +00:00
call ( 'changed' , selectedElements ) ;
2010-06-30 18:27:36 +00:00
return batchCmd ;
2010-06-22 14:52:51 +00:00
}
} ;
2010-06-30 18:27:36 +00:00
// Function: cloneSelectedElements
2018-05-16 00:53:27 +00:00
// Create deep DOM copies (clones) of all selected elements and move them slightly
2010-06-30 18:27:36 +00:00
// from their originals
2018-05-16 08:32:44 +00:00
this . cloneSelectedElements = function ( x , y ) {
2014-02-17 06:48:40 +00:00
var i , elem ;
2014-04-08 03:21:22 +00:00
var batchCmd = new svgedit . history . BatchCommand ( 'Clone Elements' ) ;
2010-06-22 14:52:51 +00:00
// find all the elements selected (stop at first null)
var len = selectedElements . length ;
2018-05-16 08:32:44 +00:00
function sortfunction ( a , b ) {
return ( $ ( b ) . index ( ) - $ ( a ) . index ( ) ) ; // causes an array to be sorted numerically and ascending
2013-06-01 08:00:10 +00:00
}
selectedElements . sort ( sortfunction ) ;
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; ++ i ) {
elem = selectedElements [ i ] ;
2018-05-16 08:32:44 +00:00
if ( elem == null ) { break ; }
2010-06-22 14:52:51 +00:00
}
// use slice to quickly get the subset of elements we need
2013-02-15 16:51:48 +00:00
var copiedElements = selectedElements . slice ( 0 , i ) ;
2010-09-17 20:33:33 +00:00
this . clearSelection ( true ) ;
2010-06-22 14:52:51 +00:00
// note that we loop in the reverse way because of the way elements are added
// to the selectedElements array (top-first)
2016-05-04 13:38:29 +00:00
var drawing = getCurrentDrawing ( ) ;
2014-02-17 06:48:40 +00:00
i = copiedElements . length ;
2010-06-22 14:52:51 +00:00
while ( i -- ) {
// clone each element and replace it within copiedElements
2016-05-04 13:38:29 +00:00
elem = copiedElements [ i ] = drawing . copyElem ( copiedElements [ i ] ) ;
2018-05-16 08:32:44 +00:00
( currentGroup || drawing . getCurrentLayer ( ) ) . appendChild ( elem ) ;
2013-02-17 08:21:07 +00:00
batchCmd . addSubCommand ( new svgedit . history . InsertElementCommand ( elem ) ) ;
2010-06-22 14:52:51 +00:00
}
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
if ( ! batchCmd . isEmpty ( ) ) {
2010-06-30 18:27:36 +00:00
addToSelection ( copiedElements . reverse ( ) ) ; // Need to reverse for correct selection-adding
2013-02-15 16:51:48 +00:00
this . moveSelectedElements ( x , y , false ) ;
2010-06-22 14:52:51 +00:00
addCommandToHistory ( batchCmd ) ;
}
} ;
2010-06-30 18:27:36 +00:00
// Function: alignSelectedElements
// Aligns selected elements
//
// Parameters:
// type - String with single character indicating the alignment type
2018-05-16 08:32:44 +00:00
// relativeTo - String that must be one of the following:
2010-06-30 18:27:36 +00:00
// "selected", "largest", "smallest", "page"
2018-05-16 08:32:44 +00:00
this . alignSelectedElements = function ( type , relativeTo ) {
2014-02-17 06:48:40 +00:00
var i , elem ;
2018-05-16 08:32:44 +00:00
var bboxes = [ ] ; // angles = [];
2010-06-22 14:52:51 +00:00
var minx = Number . MAX _VALUE , maxx = Number . MIN _VALUE , miny = Number . MAX _VALUE , maxy = Number . MIN _VALUE ;
var curwidth = Number . MIN _VALUE , curheight = Number . MIN _VALUE ;
var len = selectedElements . length ;
2018-05-16 08:32:44 +00:00
if ( ! len ) { return ; }
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2018-05-16 08:32:44 +00:00
if ( selectedElements [ i ] == null ) { break ; }
2014-02-17 06:48:40 +00:00
elem = selectedElements [ i ] ;
2010-06-30 18:27:36 +00:00
bboxes [ i ] = getStrokedBBox ( [ elem ] ) ;
2018-05-16 00:53:27 +00:00
2010-06-22 14:52:51 +00:00
// now bbox is axis-aligned and handles rotation
2018-05-16 08:32:44 +00:00
switch ( relativeTo ) {
2018-05-16 00:53:27 +00:00
case 'smallest' :
2018-05-16 08:32:44 +00:00
if ( ( ( type === 'l' || type === 'c' || type === 'r' ) &&
( curwidth === Number . MIN _VALUE || curwidth > bboxes [ i ] . width ) ) ||
( ( type === 't' || type === 'm' || type === 'b' ) &&
( curheight === Number . MIN _VALUE || curheight > bboxes [ i ] . height ) )
) {
2018-05-16 00:53:27 +00:00
minx = bboxes [ i ] . x ;
miny = bboxes [ i ] . y ;
maxx = bboxes [ i ] . x + bboxes [ i ] . width ;
maxy = bboxes [ i ] . y + bboxes [ i ] . height ;
curwidth = bboxes [ i ] . width ;
curheight = bboxes [ i ] . height ;
}
break ;
case 'largest' :
2018-05-16 08:32:44 +00:00
if ( ( ( type === 'l' || type === 'c' || type === 'r' ) &&
( curwidth === Number . MIN _VALUE || curwidth < bboxes [ i ] . width ) ) ||
( ( type === 't' || type === 'm' || type === 'b' ) &&
( curheight === Number . MIN _VALUE || curheight < bboxes [ i ] . height ) )
) {
2018-05-16 00:53:27 +00:00
minx = bboxes [ i ] . x ;
miny = bboxes [ i ] . y ;
maxx = bboxes [ i ] . x + bboxes [ i ] . width ;
maxy = bboxes [ i ] . y + bboxes [ i ] . height ;
curwidth = bboxes [ i ] . width ;
curheight = bboxes [ i ] . height ;
}
break ;
default : // 'selected'
2018-05-16 08:32:44 +00:00
if ( bboxes [ i ] . x < minx ) { minx = bboxes [ i ] . x ; }
if ( bboxes [ i ] . y < miny ) { miny = bboxes [ i ] . y ; }
if ( bboxes [ i ] . x + bboxes [ i ] . width > maxx ) { maxx = bboxes [ i ] . x + bboxes [ i ] . width ; }
if ( bboxes [ i ] . y + bboxes [ i ] . height > maxy ) { maxy = bboxes [ i ] . y + bboxes [ i ] . height ; }
2018-05-16 00:53:27 +00:00
break ;
2009-08-17 06:56:55 +00:00
}
2010-06-22 14:52:51 +00:00
} // loop for each element to find the bbox and adjust min/max
2009-08-17 06:56:55 +00:00
2018-05-16 08:32:44 +00:00
if ( relativeTo === 'page' ) {
2010-06-22 14:52:51 +00:00
minx = 0 ;
miny = 0 ;
maxx = canvas . contentW ;
maxy = canvas . contentH ;
}
var dx = new Array ( len ) ;
var dy = new Array ( len ) ;
2014-02-17 06:48:40 +00:00
for ( i = 0 ; i < len ; ++ i ) {
2018-05-16 08:32:44 +00:00
if ( selectedElements [ i ] == null ) { break ; }
2014-02-17 06:48:40 +00:00
elem = selectedElements [ i ] ;
2010-06-22 14:52:51 +00:00
var bbox = bboxes [ i ] ;
dx [ i ] = 0 ;
dy [ i ] = 0 ;
switch ( type ) {
2018-05-16 00:53:27 +00:00
case 'l' : // left (horizontal)
dx [ i ] = minx - bbox . x ;
break ;
case 'c' : // center (horizontal)
2018-05-16 08:32:44 +00:00
dx [ i ] = ( minx + maxx ) / 2 - ( bbox . x + bbox . width / 2 ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'r' : // right (horizontal)
dx [ i ] = maxx - ( bbox . x + bbox . width ) ;
break ;
case 't' : // top (vertical)
dy [ i ] = miny - bbox . y ;
break ;
case 'm' : // middle (vertical)
2018-05-16 08:32:44 +00:00
dy [ i ] = ( miny + maxy ) / 2 - ( bbox . y + bbox . height / 2 ) ;
2018-05-16 00:53:27 +00:00
break ;
case 'b' : // bottom (vertical)
dy [ i ] = maxy - ( bbox . y + bbox . height ) ;
break ;
2010-01-18 20:30:59 +00:00
}
}
2013-02-15 16:51:48 +00:00
this . moveSelectedElements ( dx , dy ) ;
2010-06-22 14:52:51 +00:00
} ;
2010-01-18 20:30:59 +00:00
2010-06-30 18:27:36 +00:00
// Group: Additional editor tools
2009-08-25 07:04:19 +00:00
2010-06-30 18:27:36 +00:00
this . contentW = getResolution ( ) . w ;
this . contentH = getResolution ( ) . h ;
2009-08-25 07:04:19 +00:00
2010-06-30 18:27:36 +00:00
// Function: updateCanvas
2018-05-16 00:53:27 +00:00
// Updates the editor canvas width/height/position after a zoom has occurred
2010-06-30 18:27:36 +00:00
//
// Parameters:
// w - Float with the new width
// h - Float with the new height
//
2018-05-16 00:53:27 +00:00
// Returns:
2010-06-30 18:27:36 +00:00
// Object with the following values:
// * x - The canvas' new x coordinate
// * y - The canvas' new y coordinate
2018-05-16 08:32:44 +00:00
// * oldX - The canvas' old x coordinate
// * oldY - The canvas' old y coordinate
2010-06-30 18:27:36 +00:00
// * d_x - The x position difference
// * d_y - The y position difference
2018-05-16 00:53:27 +00:00
this . updateCanvas = function ( w , h ) {
2014-04-08 03:21:22 +00:00
svgroot . setAttribute ( 'width' , w ) ;
svgroot . setAttribute ( 'height' , h ) ;
2010-06-30 18:27:36 +00:00
var bg = $ ( '#canvasBackground' ) [ 0 ] ;
2018-05-16 08:32:44 +00:00
var oldX = svgcontent . getAttribute ( 'x' ) ;
var oldY = svgcontent . getAttribute ( 'y' ) ;
var x = ( w / 2 - this . contentW * currentZoom / 2 ) ;
var y = ( h / 2 - this . contentH * currentZoom / 2 ) ;
2010-06-30 18:27:36 +00:00
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( svgcontent , {
2018-05-16 08:32:44 +00:00
width : this . contentW * currentZoom ,
height : this . contentH * currentZoom ,
2010-06-30 18:27:36 +00:00
'x' : x ,
'y' : y ,
2018-05-16 08:32:44 +00:00
'viewBox' : '0 0 ' + this . contentW + ' ' + this . contentH
2010-06-30 18:27:36 +00:00
} ) ;
2018-05-16 00:53:27 +00:00
2013-02-17 04:58:04 +00:00
svgedit . utilities . assignAttributes ( bg , {
2010-06-30 18:27:36 +00:00
width : svgcontent . getAttribute ( 'width' ) ,
height : svgcontent . getAttribute ( 'height' ) ,
x : x ,
y : y
} ) ;
2011-09-22 23:58:12 +00:00
2018-05-16 08:32:44 +00:00
var bgImg = svgedit . utilities . getElem ( 'background_image' ) ;
if ( bgImg ) {
svgedit . utilities . assignAttributes ( bgImg , {
2011-09-22 23:58:12 +00:00
'width' : '100%' ,
'height' : '100%'
} ) ;
}
2018-05-16 00:53:27 +00:00
2014-04-08 03:21:22 +00:00
selectorManager . selectorParentGroup . setAttribute ( 'transform' , 'translate(' + x + ',' + y + ')' ) ;
2018-05-16 08:32:44 +00:00
runExtensions ( 'canvasUpdated' , { new _x : x , new _y : y , old _x : oldX , old _y : oldY , d _x : x - oldX , d _y : y - oldY } ) ;
return { x : x , y : y , old _x : oldX , old _y : oldY , d _x : x - oldX , d _y : y - oldY } ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-06-30 18:27:36 +00:00
// Function: setBackground
// Set the background of the editor (NOT the actual document)
//
// Parameters:
// color - String with fill color to apply
// url - URL or path to image to use
2018-05-16 08:32:44 +00:00
this . setBackground = function ( color , url ) {
2014-01-31 10:40:52 +00:00
var bg = svgedit . utilities . getElem ( 'canvasBackground' ) ;
2010-06-30 18:27:36 +00:00
var border = $ ( bg ) . find ( 'rect' ) [ 0 ] ;
2018-05-16 08:32:44 +00:00
var bgImg = svgedit . utilities . getElem ( 'background_image' ) ;
2013-02-15 16:51:48 +00:00
border . setAttribute ( 'fill' , color ) ;
2013-02-15 15:51:58 +00:00
if ( url ) {
2018-05-16 08:32:44 +00:00
if ( ! bgImg ) {
bgImg = svgdoc . createElementNS ( NS . SVG , 'image' ) ;
svgedit . utilities . assignAttributes ( bgImg , {
2010-06-30 18:27:36 +00:00
'id' : 'background_image' ,
'width' : '100%' ,
'height' : '100%' ,
'preserveAspectRatio' : 'xMinYMin' ,
2018-05-16 08:32:44 +00:00
'style' : 'pointer-events:none'
2010-06-30 18:27:36 +00:00
} ) ;
}
2018-05-16 08:32:44 +00:00
setHref ( bgImg , url ) ;
bg . appendChild ( bgImg ) ;
} else if ( bgImg ) {
bgImg . parentNode . removeChild ( bgImg ) ;
2010-06-30 18:27:36 +00:00
}
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-06-30 18:27:36 +00:00
// Function: cycleElement
// Select the next/previous element within the current layer
//
// Parameters:
// next - Boolean where true = next and false = previous element
2018-05-16 08:32:44 +00:00
this . cycleElement = function ( next ) {
2014-02-17 06:48:40 +00:00
var num ;
2018-05-16 08:32:44 +00:00
var curElem = selectedElements [ 0 ] ;
2010-06-30 18:27:36 +00:00
var elem = false ;
2018-05-16 08:32:44 +00:00
var allElems = getVisibleElements ( currentGroup || getCurrentDrawing ( ) . getCurrentLayer ( ) ) ;
if ( ! allElems . length ) { return ; }
if ( curElem == null ) {
num = next ? allElems . length - 1 : 0 ;
elem = allElems [ num ] ;
2010-06-22 14:52:51 +00:00
} else {
2018-05-16 08:32:44 +00:00
var i = allElems . length ;
2013-02-15 15:51:58 +00:00
while ( i -- ) {
2018-05-16 08:32:44 +00:00
if ( allElems [ i ] === curElem ) {
2014-02-17 06:48:40 +00:00
num = next ? i - 1 : i + 1 ;
2018-05-16 08:32:44 +00:00
if ( num >= allElems . length ) {
2010-06-30 18:27:36 +00:00
num = 0 ;
2013-02-15 15:51:58 +00:00
} else if ( num < 0 ) {
2018-05-16 08:32:44 +00:00
num = allElems . length - 1 ;
2018-05-16 00:53:27 +00:00
}
2018-05-16 08:32:44 +00:00
elem = allElems [ num ] ;
2010-06-30 18:27:36 +00:00
break ;
2018-05-16 00:53:27 +00:00
}
2010-06-30 18:27:36 +00:00
}
2018-05-16 00:53:27 +00:00
}
2010-09-17 20:33:33 +00:00
selectOnly ( [ elem ] , true ) ;
2014-04-08 03:21:22 +00:00
call ( 'selected' , selectedElements ) ;
2013-02-15 15:51:58 +00:00
} ;
2010-06-22 14:52:51 +00:00
2010-06-30 18:27:36 +00:00
this . clear ( ) ;
2018-05-16 00:53:27 +00:00
// DEPRECATED: getPrivateMethods
2010-06-30 18:27:36 +00:00
// Since all methods are/should be public somehow, this function should be removed
2010-06-22 14:52:51 +00:00
// Being able to access private methods publicly seems wrong somehow,
// but currently appears to be the best way to allow testing and provide
// access to them to plugins.
2018-05-16 08:32:44 +00:00
this . getPrivateMethods = function ( ) {
2010-06-30 18:27:36 +00:00
var obj = {
2010-06-22 14:52:51 +00:00
addCommandToHistory : addCommandToHistory ,
2010-06-28 20:09:34 +00:00
setGradient : setGradient ,
2010-06-22 14:52:51 +00:00
addSvgElementFromJson : addSvgElementFromJson ,
assignAttributes : assignAttributes ,
BatchCommand : BatchCommand ,
call : call ,
ChangeElementCommand : ChangeElementCommand ,
2018-05-16 08:32:44 +00:00
copyElem : function ( elem ) { return getCurrentDrawing ( ) . copyElem ( elem ) ; } ,
2010-06-22 14:52:51 +00:00
ffClone : ffClone ,
findDefs : findDefs ,
findDuplicateGradient : findDuplicateGradient ,
getElem : getElem ,
getId : getId ,
getIntersectionList : getIntersectionList ,
getMouseTarget : getMouseTarget ,
getNextId : getNextId ,
getPathBBox : getPathBBox ,
getUrlFromAttr : getUrlFromAttr ,
hasMatrixTransform : hasMatrixTransform ,
identifyLayers : identifyLayers ,
InsertElementCommand : InsertElementCommand ,
2010-11-07 16:46:57 +00:00
isIdentity : svgedit . math . isIdentity ,
2010-06-22 14:52:51 +00:00
logMatrix : logMatrix ,
matrixMultiply : matrixMultiply ,
MoveElementCommand : MoveElementCommand ,
2016-05-04 13:38:29 +00:00
preventClickDefault : svgedit . utilities . preventClickDefault ,
2010-06-22 14:52:51 +00:00
recalculateAllSelectedDimensions : recalculateAllSelectedDimensions ,
recalculateDimensions : recalculateDimensions ,
remapElement : remapElement ,
RemoveElementCommand : RemoveElementCommand ,
removeUnusedDefElems : removeUnusedDefElems ,
round : round ,
runExtensions : runExtensions ,
sanitizeSvg : sanitizeSvg ,
2010-11-05 15:59:30 +00:00
SVGEditTransformList : svgedit . transformlist . SVGTransformList ,
2010-06-22 14:52:51 +00:00
toString : toString ,
2010-11-07 16:46:57 +00:00
transformBox : svgedit . math . transformBox ,
2010-06-22 14:52:51 +00:00
transformListToTransform : transformListToTransform ,
transformPoint : transformPoint ,
2010-11-05 17:00:32 +00:00
walkTree : svgedit . utilities . walkTree
2013-02-15 15:51:58 +00:00
} ;
2010-06-30 18:27:36 +00:00
return obj ;
2010-06-22 14:52:51 +00:00
} ;
2013-02-15 15:51:58 +00:00
} ;