2018-05-16 00:53:27 +00:00
/ *
2009-11-22 20:13:06 +00:00
Embedded SVG - edit API
General usage :
- Have an iframe somewhere pointing to a version of svg - edit > r1000
- Initialize the magic with :
2018-05-18 03:25:45 +00:00
const svgCanvas = new EmbeddedSVGEdit ( window . frames . svgedit ) ;
2009-11-22 20:13:06 +00:00
- Pass functions in this format :
2014-02-22 04:08:24 +00:00
svgCanvas . setSvgString ( 'string' )
2009-11-22 20:13:06 +00:00
- Or if a callback is needed :
2014-02-22 04:08:24 +00:00
svgCanvas . setSvgString ( 'string' ) ( function ( data , error ) {
2018-05-18 04:02:30 +00:00
if ( error ) {
// There was an error
} else {
// Handle data
}
2009-11-22 20:13:06 +00:00
} )
2013-02-23 17:37:05 +00:00
Everything is done with the same API as the real svg - edit ,
2013-10-14 03:25:35 +00:00
and all documentation is unchanged .
2014-01-31 04:54:49 +00:00
However , this file depends on the postMessage API which
can only support JSON - serializable arguments and
2013-10-14 03:25:35 +00:00
return values , so , for example , arguments whose value is
2014-02-22 04:08:24 +00:00
'undefined' , a function , a non - finite number , or a built - in
2013-10-14 03:25:35 +00:00
object like Date ( ) , RegExp ( ) , etc . will most likely not behave
as expected . In such a case one may need to host
the SVG editor on the same domain and reference the
JavaScript methods on the frame itself .
The only other difference is
when handling returns : the callback notation is used instead .
2009-11-22 20:13:06 +00:00
2018-05-18 03:25:45 +00:00
const blah = new EmbeddedSVGEdit ( window . frames . svgedit ) ;
2014-02-22 04:08:24 +00:00
blah . clearSelection ( 'woot' , 'blah' , 1337 , [ 1 , 2 , 3 , 4 , 5 , 'moo' ] , - 42 , { a : 'tree' , b : 6 , c : 9 } ) ( function ( ) { console . log ( 'GET DATA' , arguments ) } )
2009-11-22 20:13:06 +00:00
* /
2018-05-18 03:25:45 +00:00
let cbid = 0 ;
2013-10-13 23:59:32 +00:00
function getCallbackSetter ( d ) {
2018-05-18 04:02:30 +00:00
return function ( ) {
2018-05-18 03:25:45 +00:00
const t = this , // New callback
2018-05-18 04:02:30 +00:00
args = [ ] . slice . call ( arguments ) ,
2018-05-18 07:39:36 +00:00
cbid = t . send ( d , args , function ( ) { } ) ; // The callback (currently it's nothing, but will be set later)
2018-05-18 04:02:30 +00:00
return function ( newcallback ) {
t . callbacks [ cbid ] = newcallback ; // Set callback
} ;
} ;
2013-10-13 23:59:32 +00:00
}
2014-02-22 04:08:24 +00:00
/ *
* Having this separate from messageListener allows us to
* avoid using JSON parsing ( and its limitations ) in the case
* of same domain control
* /
function addCallback ( t , data ) {
2018-05-18 03:25:45 +00:00
const result = data . result || data . error ,
2018-05-18 04:02:30 +00:00
cbid = data . id ;
if ( t . callbacks [ cbid ] ) {
if ( data . result ) {
t . callbacks [ cbid ] ( result ) ;
} else {
t . callbacks [ cbid ] ( result , 'error' ) ;
}
}
2014-02-22 04:08:24 +00:00
}
function messageListener ( e ) {
2018-05-18 04:02:30 +00:00
// We accept and post strings as opposed to objects for the sake of IE9 support; this
2018-05-18 06:41:43 +00:00
// will most likely be changed in the future
2018-05-18 04:02:30 +00:00
if ( typeof e . data !== 'string' ) {
return ;
}
2018-05-18 03:25:45 +00:00
const { allowedOrigins } = this ,
2018-05-18 04:02:30 +00:00
data = e . data && JSON . parse ( e . data ) ;
if ( ! data || typeof data !== 'object' || data . namespace !== 'svg-edit' ||
e . source !== this . frame . contentWindow ||
2018-05-18 03:25:45 +00:00
( ! allowedOrigins . includes ( '*' ) && ! allowedOrigins . includes ( e . origin ) )
2018-05-18 04:02:30 +00:00
) {
return ;
}
addCallback ( this , data ) ;
2014-02-22 04:08:24 +00:00
}
function getMessageListener ( t ) {
2018-05-18 04:02:30 +00:00
return function ( e ) {
messageListener . call ( t , e ) ;
} ;
2014-02-22 04:08:24 +00:00
}
2014-03-02 01:25:39 +00:00
/ * *
2014-03-02 23:31:41 +00:00
* @ param { HTMLIFrameElement } frame
2014-03-02 01:25:39 +00:00
* @ param { array } [ allowedOrigins = [ ] ] Array of origins from which incoming
2018-05-18 06:41:43 +00:00
* messages will be allowed when same origin is not used ; defaults to none .
* If supplied , it should probably be the same as svgEditor ' s allowedOrigins
2014-03-02 01:25:39 +00:00
* /
2018-05-29 16:43:02 +00:00
class EmbeddedSVGEdit {
2018-05-18 03:25:45 +00:00
constructor ( frame , allowedOrigins ) {
this . allowedOrigins = allowedOrigins || [ ] ;
// Initialize communication
this . frame = frame ;
this . callbacks = { } ;
// List of functions extracted with this:
// Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html
// for (const i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q
// const functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
// 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
// 'moveSelectedToLayer', 'clear'];
// Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
// const {svgCanvas} = frame.contentWindow;
// const l = [];
// for (const i in svgCanvas) { if (typeof svgCanvas[i] === 'function') { l.push(i);} };
// alert("['" + l.join("', '") + "']");
// Run in svgedit itself
const functions = [
2018-05-30 07:59:45 +00:00
'addExtension' ,
'addSvgElementFromJson' ,
'addToSelection' ,
'alignSelectedElements' ,
'assignAttributes' ,
'bind' ,
'call' ,
'changeSelectedAttribute' ,
'cleanupElement' ,
'clear' ,
'clearSelection' ,
'clearSvgContentElement' ,
'cloneLayer' ,
'cloneSelectedElements' ,
'convertGradients' ,
'convertToGroup' ,
'convertToNum' ,
'convertToPath' ,
'copySelectedElements' ,
'createLayer' ,
'cutSelectedElements' ,
'cycleElement' ,
'deleteCurrentLayer' ,
'deleteSelectedElements' ,
'embedImage' ,
'exportPDF' ,
'findDefs' ,
'getBBox' ,
'getBlur' ,
'getBold' ,
'getColor' ,
'getContentElem' ,
'getCurrentDrawing' ,
'getDocumentTitle' ,
'getEditorNS' ,
'getElem' ,
'getFillOpacity' ,
'getFontColor' ,
'getFontFamily' ,
'getFontSize' ,
'getHref' ,
'getId' ,
'getIntersectionList' ,
'getItalic' ,
'getMode' ,
'getMouseTarget' ,
'getNextId' ,
'getOffset' ,
'getOpacity' ,
'getPaintOpacity' ,
'getPrivateMethods' ,
'getRefElem' ,
'getResolution' ,
'getRootElem' ,
'getRotationAngle' ,
'getSelectedElems' ,
'getStrokeOpacity' ,
'getStrokeWidth' ,
'getStrokedBBox' ,
'getStyle' ,
'getSvgString' ,
'getText' ,
'getTitle' ,
'getTransformList' ,
'getUIStrings' ,
'getUrlFromAttr' ,
'getVersion' ,
'getVisibleElements' ,
'getVisibleElementsAndBBoxes' ,
'getZoom' ,
'groupSelectedElements' ,
'groupSvgElem' ,
'hasMatrixTransform' ,
'identifyLayers' ,
'importSvgString' ,
'leaveContext' ,
'linkControlPoints' ,
'makeHyperlink' ,
'matrixMultiply' ,
'mergeAllLayers' ,
'mergeLayer' ,
'moveSelectedElements' ,
'moveSelectedToLayer' ,
'moveToBottomSelectedElement' ,
'moveToTopSelectedElement' ,
'moveUpDownSelected' ,
'open' ,
'pasteElements' ,
'prepareSvg' ,
'pushGroupProperties' ,
'randomizeIds' ,
'rasterExport' ,
'ready' ,
'recalculateAllSelectedDimensions' ,
'recalculateDimensions' ,
'remapElement' ,
'removeFromSelection' ,
'removeHyperlink' ,
'removeUnusedDefElems' ,
'renameCurrentLayer' ,
'round' ,
'runExtensions' ,
'sanitizeSvg' ,
'save' ,
'selectAllInCurrentLayer' ,
'selectOnly' ,
'setBBoxZoom' ,
'setBackground' ,
'setBlur' ,
'setBlurNoUndo' ,
'setBlurOffsets' ,
'setBold' ,
'setColor' ,
'setConfig' ,
'setContext' ,
'setCurrentLayer' ,
'setCurrentLayerPosition' ,
'setDocumentTitle' ,
'setFillPaint' ,
'setFontColor' ,
'setFontFamily' ,
'setFontSize' ,
'setGoodImage' ,
'setGradient' ,
'setGroupTitle' ,
'setHref' ,
'setIdPrefix' ,
'setImageURL' ,
'setItalic' ,
'setLayerVisibility' ,
'setLinkURL' ,
'setMode' ,
'setOpacity' ,
'setPaint' ,
'setPaintOpacity' ,
'setRectRadius' ,
'setResolution' ,
'setRotationAngle' ,
'setSegType' ,
'setStrokeAttr' ,
'setStrokePaint' ,
'setStrokeWidth' ,
'setSvgString' ,
'setTextContent' ,
'setUiStrings' ,
'setUseData' ,
'setZoom' ,
'svgCanvasToString' ,
'svgToString' ,
'transformListToTransform' ,
'ungroupSelectedElement' ,
'uniquifyElems' ,
'updateCanvas' ,
'zoomChanged'
2018-05-18 04:02:30 +00:00
] ;
2018-05-18 03:25:45 +00:00
// TODO: rewrite the following, it's pretty scary.
for ( let i = 0 ; i < functions . length ; i ++ ) {
this [ functions [ i ] ] = getCallbackSetter ( functions [ i ] ) ;
}
2018-05-18 04:02:30 +00:00
2018-05-18 03:25:45 +00:00
// Older IE may need a polyfill for addEventListener, but so it would for SVG
window . addEventListener ( 'message' , getMessageListener ( this ) , false ) ;
}
2014-04-09 06:51:05 +00:00
2018-05-18 03:25:45 +00:00
send ( name , args , callback ) {
const t = this ;
cbid ++ ;
this . callbacks [ cbid ] = callback ;
setTimeout ( ( function ( cbid ) {
return function ( ) { // Delay for the callback to be set in case its synchronous
/ *
* Todo : Handle non - JSON arguments and return values ( undefined ,
* nonfinite numbers , functions , and built - in objects like Date ,
* RegExp ) , etc . ? Allow promises instead of callbacks ? Review
* SVG - Edit functions for whether JSON - able parameters can be
* made compatile with all API functionality
* /
// We accept and post strings for the sake of IE9 support
if ( window . location . origin === t . frame . contentWindow . location . origin ) {
// Although we do not really need this API if we are working same
// domain, it could allow us to write in a way that would work
// cross-domain as well, assuming we stick to the argument limitations
// of the current JSON-based communication API (e.g., not passing
// callbacks). We might be able to address these shortcomings; see
// the todo elsewhere in this file.
const message = { id : cbid } ,
{ svgCanvas } = t . frame . contentWindow ;
try {
message . result = svgCanvas [ name ] . apply ( svgCanvas , args ) ;
} catch ( err ) {
message . error = err . message ;
}
addCallback ( t , message ) ;
} else { // Requires the ext-xdomain-messaging.js extension
2018-05-30 07:59:45 +00:00
t . frame . contentWindow . postMessage ( JSON . stringify ( {
namespace : 'svgCanvas' , id : cbid , name , args
} ) , '*' ) ;
2018-05-18 04:02:30 +00:00
}
2018-05-18 03:25:45 +00:00
} ;
} ( cbid ) ) , 0 ) ;
2018-05-18 04:02:30 +00:00
2018-05-18 03:25:45 +00:00
return cbid ;
}
}
2018-05-29 16:43:02 +00:00
export default EmbeddedSVGEdit ;