jsyg-fulleditor
Version:
complete and very simple API to create svg editor
1,784 lines (1,194 loc) • 61.1 kB
JavaScript
/*jshint forin:false, eqnull:true*/
/* globals JSYG*/
(function(factory) {
if (typeof module == "object" && typeof module.exports == "object") {
module.exports = factory(
require("jsyg"),
require("jsyg-editor"),
require("jsyg-texteditor"),
require("jsyg-zoomandpan"),
require("jsyg-pathdrawer"),
require("jsyg-polylinedrawer"),
require("jsyg-shapedrawer"),
require("jsyg-undoredo"),
require("jquery.hotkeys")
);
}
else if (typeof define != "undefined" && define.amd) {
define("jsyg-fulleditor",[
"jsyg",
"jsyg-editor",
"jsyg-texteditor",
"jsyg-zoomandpan",
"jsyg-pathdrawer",
"jsyg-polylinedrawer",
"jsyg-shapedrawer",
"jsyg-undoredo",
"jquery.hotkeys"
],factory);
}
else if (typeof JSYG != "undefined") {
var deps = ["Editor","TextEditor","ZoomAndPan","PathDrawer","PolylineDrawer","ShapeDrawer","UndoRedo"];
deps = deps.map(function(dep) {
if (!JSYG[dep]) throw new Error("JSYG."+dep+" is missing");
return JSYG[dep];
});
deps.unshift(JSYG);
factory.apply(null,deps);
}
else throw new Error("JSYG is needed");
})(function(JSYG,Editor,TextEditor,ZoomAndPan,PathDrawer,PolylineDrawer,ShapeDrawer,UndoRedo) {
"use strict";
var slice = Array.prototype.slice;
function FullEditor(node,opt) {
this._bindFunctions();
this._init();
this._keyShortCuts = {};
if (node) this.setNode(node);
if (opt) this.enable(opt);
}
FullEditor._plugins = [];
FullEditor.prototype = Object.create(JSYG.StdConstruct.prototype);
FullEditor.prototype.constructor = FullEditor;
//events
[
'onload',
'ondrag',
'ondraw',
'oninsert',
'onremove',
'onchange',
'onzoom',
'onchangetarget',
'ondisableedition'
].forEach(function(event) { FullEditor.prototype[event] = null; });
FullEditor.prototype.idContainer = "containerDoc";
FullEditor.prototype._initPlugins = function() {
FullEditor._plugins.forEach(this._createPlugin.bind(this));
this._applyMethodPlugins("init");
return this;
};
FullEditor.prototype._init = function() {
this._initUndoRedo();
this._initShapeEditor();
this._initZoomAndPan();
this._initTextEditor();
this._initShapeDrawer();
this._initPlugins();
return this;
};
FullEditor.prototype._bindFunctions = function() {
for (var n in this) {
if (typeof(this[n]) == "function" && n.charAt(0) != '_') this[n] = this[n].bind(this);
}
return this;
};
/**
* Register a key shortcut
* @param {string} key jquery hotkeys syntax (example : "ctrl+i")
* @param {function} fct callback called when key or combination keys are pressed
* @returns {JSYG.FullEditor}
* @description You can also pass an object with several key shortcuts as keys/values
*/
FullEditor.prototype.registerKeyShortCut = function(key,fct) {
if (JSYG.isPlainObject(key)) {
for (var n in key) this.registerKeyShortCut(n,key[n]);
return this;
}
if (this._keyShortCuts[key]) this._disableKeyShortCut(key);
this._keyShortCuts[key] = fct;
if (this.enabled) this._enableKeyShortCut(key,fct);
return this;
};
/**
* Unregister a key shortcut
* @param {string} key jquery hotkeys syntax (example : "ctrl+i")
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.unregisterKeyShortCut = function(key) {
var that = this;
if (Array.isArray(key) || arguments.length > 1 && (key = [].slice.call(arguments))) {
key.forEach(that.unregisterKeyShortCut);
return this;
}
this._disableKeyShortCut(key,this._keyShortCuts[key]);
delete this._keyShortCuts[key];
return this;
};
/**
* Select all editable elements in document
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.selectAll = function() {
this.disableEdition();
this.enableSelection();
this.shapeEditor.selection.selectAll();
return this;
};
/**
* Deselect all editable elements
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.deselectAll = function() {
var isEnabled = this.shapeEditor.enabled;
if (!isEnabled) this.shapeEditor.enable();
this.shapeEditor.selection.deselectAll();
if (!isEnabled) this.shapeEditor.disable();
return this;
};
FullEditor.prototype._enableKeyShortCut = function(key,fct) {
if (typeof fct != 'function') throw new Error(typeof fct + " instead of function expected");
new JSYG(document).on('keydown',null,key,fct);
return this;
};
FullEditor.prototype._disableKeyShortCut = function(key,fct) {
if (typeof fct != 'function') throw new Error(typeof fct + " instead of function expected");
new JSYG(document).off('keydown',fct);
return this;
};
/**
* Enable all key shorcuts registered by registerKeyShortCut method
* @returns {JSYG.FullEditor}
* @see JSYG.prototype.registerKeyShortCut
*/
FullEditor.prototype.enableKeyShortCuts = function() {
var keys = this._keyShortCuts;
for (var n in keys) this._enableKeyShortCut(n,keys[n]);
return this;
};
/**
* Disable all key shorcuts registered by registerKeyShortCut method
* @returns {JSYG.FullEditor}
* @see JSYG.prototype.registerKeyShortCut
*/
FullEditor.prototype.disableKeyShortCuts = function() {
var keys = this._keyShortCuts;
for (var n in keys) this._disableKeyShortCut(n,keys[n]);
return this;
};
FullEditor.prototype._editText = true;
/**
* @property {boolean} editText set if text elements can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editText',{
get : function() {
return this._editText;
},
set:function(value) {
this._editText = !!value;
if (!value) this.textEditor.hide();
}
});
/**
* @property {boolean} editPosition set if elements position can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editPosition',{
get : function() {
return this.shapeEditor.ctrlsDrag.enabled;
},
set:function(value) {
this.shapeEditor.ctrlsDrag[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} editSize set if elements size can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editSize',{
get : function() {
return this.shapeEditor.ctrlsResize.enabled;
},
set:function(value) {
this.shapeEditor.ctrlsResize[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} editRotation set if elements rotation can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editRotation',{
get : function() {
return this.shapeEditor.ctrlsRotate.enabled;
},
set:function(value) {
this.shapeEditor.ctrlsRotate[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} editPathMainPoints set if main points of paths can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editPathMainPoints',{
get : function() {
return this.shapeEditor.ctrlsMainPoints.enabled;
},
set:function(value) {
this.shapeEditor.ctrlsMainPoints[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} editPathCtrlPoints set if control points of paths can be edited or not
*/
Object.defineProperty(FullEditor.prototype,'editPathCtrlPoints',{
get : function() {
return this.shapeEditor.ctrlsCtrlPoints.enabled;
},
set:function(value) {
this.shapeEditor.ctrlsCtrlPoints[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} canvasResizable set if the editor can be resized or not
*/
Object.defineProperty(FullEditor.prototype,'canvasResizable',{
get:function() {
return this.zoomAndPan.resizable.enabled;
},
set:function(value) {
this.zoomAndPan.resizable[ (value ? 'en' : 'dis') + 'able']();
}
});
/**
* @property {boolean} keepShapesRatio set if ratio must be kept when resizing
*/
Object.defineProperty(FullEditor.prototype,'keepShapesRatio',{
get:function() {
return this.shapeEditor.ctrlsResize.keepRatio;
},
set:function(value) {
value = !!value;
this.shapeEditor.ctrlsResize.keepRatio = value;
this._keepShapesRatio = value;
if (this.shapeEditor.display) this.shapeEditor.update();
}
});
/**
* @property {string} drawingPathMethod "freehand" or "point2point". Set method of drawing paths
*/
Object.defineProperty(FullEditor.prototype,'drawingPathMethod',{
get:function() {
return this.pathDrawer.type;
},
set:function(value) {
if (value != 'freehand' && value != 'point2point')
throw new Error("Only 'freehand' and 'point2point' are allowed");
this.pathDrawer.type = value;
}
});
/**
* @property {boolean} autoSmoothPaths set if paths must be smoothed automatically when drawing
*/
Object.defineProperty(FullEditor.prototype,'autoSmoothPaths',{
get:function() {
return this.shapeEditor.ctrlsMainPoints.autoSmooth;
},
set:function(value) {
this.shapeEditor.ctrlsMainPoints.autoSmooth = value;
}
});
/**
* @property {boolean} useTransformAttr set if transform attribute must be affected when editing size and position, instead
* of position and size attributes
*/
Object.defineProperty(FullEditor.prototype,'useTransformAttr',{
get:function() {
var dragType = this.shapeEditor.ctrlsDrag.type;
var resizeType = this.shapeEditor.ctrlsResize.type;
if (dragType!=resizeType) throw new Error("dragType and resizeType are not the same");
return dragType;
},
set:function(value) {
var oldValue = this.useTransformAttr;
if (value != oldValue) {
this.shapeEditor.ctrlsDrag.type = value ? 'transform' : 'attributes';
if (this.shapeEditor.ctrlsDrag.enabled) this.shapeEditor.ctrlsDrag.disable().enable();
this.shapeEditor.ctrlsResize.type = value ? 'transform' : 'attributes';
if (this.shapeEditor.ctrlsResize.enabled) this.shapeEditor.ctrlsResize.disable().enable();
}
}
});
FullEditor.prototype._nbLayers = 0;
FullEditor.prototype._currentLayer = null;
/**
* @property {number} currentLayer set current layer of edition
*/
Object.defineProperty(FullEditor.prototype,'currentLayer',{
get : function() {
return this._currentLayer;
},
set : function(value) {
var $node;
if (value != null) {
$node = new JSYG( this._getDocumentSelector() + ' #layer' + value );
if (!$node.length) throw new Error("Invalid value for currentLayer. No node found.");
}
this._currentLayer = value;
this.hideEditors();
this.editableShapes = this.editableShapes; //on force la valeur pour l'actualiser
}
});
/**
* Get all layers defined
* @returns {JSYG}
*/
FullEditor.prototype.getLayers = function() {
return new JSYG(this._getDocumentSelector()).find(".layer");
};
/**
* Add and use a new layer
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.addLayer = function() {
var nb = ++this._nbLayers,
id = "layer"+nb,
g = new JSYG('<g>').addClass("layer").attr("id",id).appendTo( this._getDocumentSelector() );
this.currentLayer = nb;
this.triggerChange();
return this;
};
/**
* Remove a layer
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.removeLayer = function() {
if (!this.currentLayer) throw new Error("No layer selected");
new JSYG(this._getLayerSelector()).remove();
this._actuLayers();
this.currentLayer = null;
this.triggerChange();
return this;
};
FullEditor.prototype._getLayerSelector = function() {
return this._getDocumentSelector() + (this.currentLayer ? ' #layer'+this.currentLayer : '')+' ';
};
/**
* Get document as a DOM node
* @returns {Element}
*/
FullEditor.prototype.getDocument = function() {
return document.querySelector( this._getDocumentSelector() );
};
FullEditor.prototype._initUndoRedo = function() {
var that = this;
this.undoRedo = new UndoRedo();
this.undoRedo.on("change",function() {
//that.hideEditors();
that.trigger("change", that, that.getDocument() );
});
};
/**
* Hide shape and text editors
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.hideEditors = function() {
this.shapeEditor.hide();
this.textEditor.hide();
return this;
};
/**
* Enable mouse pointer selection
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.enableSelection = function() {
var target = this.shapeEditor.display && this.shapeEditor.target();
this.disableEdition();
this.shapeEditor.enable();
if (target) this.shapeEditor.target(target).show();
return this;
};
/**
* Disable mouse pointer selection
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.disableSelection = function() {
this.hideEditors();
if (this.shapeEditor.enabled) this.shapeEditor.disable();
return this;
};
/**
* Disable mouse pointer insertion
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.disableInsertion = function() {
this.disableInsertElement();
this.disableShapeDrawer();
return this;
};
/**
* Register a plugin
* @param {object} plugin
* @returns {JSYG.FullEditor}
*/
FullEditor.registerPlugin = function(plugin) {
if (!plugin.name) throw new Error("Plugin must have a name property");
if (this._plugins.some(function(otherPlugin) { return otherPlugin.name == plugin.name }))
throw new Error(plugin.name+" plugin already exists");
this._plugins.push(plugin);
return this;
};
function isPrivate(name) {
return name.charAt(0) == '_';
}
FullEditor.prototype._createPlugin = function(plugin) {
plugin = Object.create(plugin);
plugin.set = JSYG.StdConstruct.prototype.set;
plugin.editor = this;
this[plugin.name] = function(method) {
var args = slice.call(arguments,1);
var returnValue;
var prop;
if (!method || JSYG.isPlainObject(method)) {
args = [method];
method = "enable";
}
if (method == "get") {
prop = args[0];
if (isPrivate(prop)) throw new Error("property "+prop+" is private");
return plugin[args[0]];
}
if (!plugin[method]) throw new Error("method "+method+" does not exist");
if (isPrivate(method)) throw new Error("method "+method+" is private");
returnValue = plugin[method].apply(plugin,args);
return returnValue || this;
};
};
FullEditor.prototype._applyMethodPlugins = function(method) {
var that = this;
FullEditor._plugins.forEach(function(plugin) {
try { that[plugin.name](method); }
catch(e) {}
});
};
/**
* Enable edition functionalities
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.disableEdition = function() {
this.disableInsertion();
this.disableMousePan();
this.disableSelection();
this.trigger("disableedition",this);
return this;
};
["copy","cut","paste"].forEach(function(action) {
FullEditor.prototype[action] = function() {
if (action!=="paste" && !this.shapeEditor.display) return;
this.shapeEditor.clipBoard[action]();
return this;
};
});
/**
* Duplicate selected element
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.duplicate = function() {
var cb = this.shapeEditor.clipBoard,
buffer = cb.buffer;
cb.copy();
cb.paste();
cb.buffer = buffer;
return this;
};
["undo","redo"].forEach(function(action) {
FullEditor.prototype[action] = function() {
this.hideEditors();
this.undoRedo[action]();
return this;
};
});
["Front","Backwards","Forwards","Back"].forEach(function(type) {
var methode = "move"+type;
FullEditor.prototype[methode] = function() {
var target = this.shapeEditor._target;
if (target) {
new JSYG(target)[methode]();
this.triggerChange();
}
return this;
};
});
["Horiz","Verti"].forEach(function(type) {
var methode = "move"+type;
FullEditor.prototype[methode] = function(value) {
var target = this.shapeEditor.target();
var dim;
if (target && target.length) {
dim = target.getDim();
target.setDim( type == "Horiz" ? {x:dim.x+value} : {y:dim.y+value} );
this.shapeEditor.update();
this.triggerChange();
}
return this;
};
});
var regOperator = /^\s*(\+|-|\*|\/)=(\d+)\s*$/;
function parseValue(newValue,oldValue) {
var matches = regOperator.exec(newValue);
if (!matches) return newValue;
switch (matches[1]) {
case '+' : return oldValue + Number(matches[2]);
case '-' : return oldValue - Number(matches[2]);
case '*' : return oldValue * Number(matches[2]);
case '/' : return oldValue / Number(matches[2]);
}
}
/**
* Get or set dimensions of element selected
* @param {string} prop x, y , width or height
* @param {number} value
* @returns {number,JSYG.FullEditor}
* @description You can also pass an object
*/
FullEditor.prototype.dim = function(prop,value) {
if (JSYG.isPlainObject(prop) || value != null) return this._setDim(prop,value);
else return this._getDim(prop,value);
};
FullEditor.prototype._getDim = function(prop) {
var target = this.shapeEditor.target();
var doc = this.getDocument();
var dim;
if (!target || !target.length) return null;
dim = target.getDim(doc);
return (prop == null) ? dim : dim[prop];
};
FullEditor.prototype._setDim = function(prop,value) {
var target = this.shapeEditor.target();
var change = false;
var doc = this.getDocument();
var n,newDim,oldDim;
if (!target || !target.length) return this;
if (JSYG.isPlainObject(prop)) newDim = JSYG.extend({},prop);
else {
newDim = {};
newDim[prop] = value;
}
oldDim = target.getDim(doc);
for (n in newDim) {
newDim[n] = parseValue(newDim[n],oldDim[n]);
if (newDim[n] != oldDim[n]) change = true;
}
if (change) {
newDim.from = doc;
newDim.keepRatio = this.keepShapesRatio;
target.setDim(newDim);
this.shapeEditor.update();
this.triggerChange();
}
return this;
};
/**
* Rotate selected element
* @param {number} value angle in degrees
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.rotate = function(value) {
var target = this.target(),
oldValue = target && target.rotate();
if (!target) return (value == null) ? null : this;
if (value == null) return oldValue;
value = parseValue(value,oldValue) - oldValue;
if (oldValue != value) {
target.rotate(value);
this.shapeEditor.update();
this.triggerChange();
}
return this;
};
/**
* Get or set css property
* @param {string} prop name of css property
* @param {string,number} value
* @returns {number,string,JSYG.FullEditor}
*/
FullEditor.prototype.css = function(prop,value) {
if (JSYG.isPlainObject(prop)) {
for (var n in prop) this.css(n,prop[n]);
return this;
}
var target = this.target(),
oldValue = target && target.css(prop);
if (!target) return (value == null) ? null : this;
if (value == null) return oldValue;
value = parseValue(value,oldValue);
if (oldValue != value) {
target.css(prop,value);
this.shapeEditor.update();
this.triggerChange();
}
return this;
};
/**
* Trigger change event
* @returns {JSYG.FullEditor}
*/
FullEditor.prototype.triggerChange = function() {
this.undoRedo.saveState();
this.trigger("change", this, this.getDocument() );
return this;
};
FullEditor.prototype._insertFrame = function() {
var mainFrame = this.zoomAndPan.innerFrame,
content = new JSYG(mainFrame).children().detach();
this._frameShadow = new JSYG("<rect>")
.attr({x:2,y:2})
.addClass("jsyg-doc-shadow")
.appendTo(mainFrame)[0];
this._frame = new JSYG("<rect>")
.attr({x:0,y:0})
.addClass("jsyg-doc-frame")
.appendTo(mainFrame)[0];
this.containerDoc = new JSYG("<g>")
.attr("id",this.idContainer)
.append(content)
.appendTo(mainFrame)
[0];
this._adjustSize();
return this;
};
/**
* @property {string} cursorDrawing name of css cursor when drawing is active
*/
FullEditor.prototype.cursorDrawing = "copy";
FullEditor.prototype._removeFrame = function() {
var container = new JSYG(this.containerDoc),
content = container.children();
new JSYG(this._frame).remove();
new JSYG(this._frameShadow).remove();
container.remove();
content.appendTo(this.zoomAndPan.innerFrame);
return this;
};
FullEditor.prototype._initShapeDrawer = function() {
this.pathDrawer = this._initDrawer(new PathDrawer);
this.polylineDrawer = this._initDrawer(new PolylineDrawer);
this.shapeDrawer = this._initDrawer(new ShapeDrawer);
return this;
};
FullEditor.prototype._initDrawer = function(drawer) {
var that = this;
drawer.on({
draw : function(e) { that.trigger("draw",that,e,this); },
end : function(e) {
if (!this.parentNode) return;
that.trigger("insert",that,e,this);
that.triggerChange();
if (that.autoEnableSelection) that.shapeEditor.target(this).show();
}
});
return drawer;
};
FullEditor.prototype._setCursorDrawing = function() {
if (this.cursorDrawing) this.zoomAndPan.node.style.cursor = this.cursorDrawing;
};
FullEditor.prototype._removeCursorDrawing = function() {
if (this.cursorDrawing) this.zoomAndPan.node.style.cursor = null;
};
/**
* @property {object} shapeDrawerModel dom node to clone when starting drawing
*/
Object.defineProperty(FullEditor.prototype,"shapeDrawerModel",{
get:function() {
return this._shapeDrawerModel;
},
set:function(value) {
var jNode = new JSYG(value);
if (jNode.length != 1) throw new Error("Shape model incorrect");
if (JSYG.svgShapes.indexOf(jNode.getTag()) == -1)
throw new Error(jNode.getTag()+" is not a svg shape");
this._shapeDrawerModel = jNode[0];
}
});
/**
* Draw one shape
* @param {type} modele
* @returns {Promise}
*/
FullEditor.prototype.drawShape = function(modele) {
var that = this;
return new Promise(function(resolve,reject) {
that.enableShapeDrawer(modele,function() {
var target = that.target();
that.disableShapeDrawer();
if (target) resolve(target[0]);
else reject(new Error("No shape was drawn"));
});
});
}
FullEditor.prototype.enableShapeDrawer = function(modele,_callback) {
var frame = new JSYG(this.zoomAndPan.innerFrame),
that = this;
this.disableEdition();
if (modele) this.shapeDrawerModel = modele;
function onmousedown(e) {
if (that.pathDrawer.inProgress || that.polylineDrawer.inProgress || that.shapeDrawer.inProgress || e.which != 1) return;
e.preventDefault();
var modele = that.shapeDrawerModel;
if (!modele) throw new Error("You must define a model");
var shape = new JSYG(modele).clone().appendTo( that._getLayerSelector() );
var tag = shape.getTag();
var drawer;
switch(tag) {
case "polyline": case "polygon" : drawer = that.polylineDrawer; break;
case "path" : drawer = that.pathDrawer; break;
default : drawer = that.shapeDrawer; break;
}
drawer.area = frame;
drawer.draw(shape,e);
if (_callback) drawer.one("end",_callback);
}
frame.on("mousedown",onmousedown).data("enableDrawingShape",onmousedown);
this._setCursorDrawing();
return this;
};
FullEditor.prototype.disableShapeDrawer = function() {
var frame = new JSYG(this.zoomAndPan.innerFrame),
fct = frame.data("enableDrawingShape");
if (!fct) return this;
frame.off("mousedown",fct);
this._removeCursorDrawing();
return this;
};
FullEditor.prototype.autoEnableSelection = true;
Object.defineProperty(FullEditor.prototype,"insertElementModel",{
get:function() {
return this._insertElementModel;
},
set:function(value) {
var jNode = new JSYG(value);
if (jNode.length != 1) throw new Error("element model incorrect");
if (JSYG.svgGraphics.indexOf(jNode.getTag()) == -1)
throw new Error(jNode.getTag()+" is not a svg graphic element");
this._insertElementModel = jNode[0];
}
});
FullEditor.prototype.is = function(type,_elmt) {
_elmt = _elmt || this.target();
var list = "svg"+JSYG.ucfirst(type)+"s";
var types = ["container","graphic","shape","text"];
if (types.indexOf(type) == -1) throw new Error(type+" : type incorrect ("+types+" required)");
return JSYG[list].indexOf( JSYG(_elmt).getTag() ) != -1;
};
FullEditor.prototype.mouseInsertElement = function(modele) {
var that = this;
return new Promise(function(resolve) {
that.enableInsertElement(modele,function() {
var target = that.target();
that.disableInsertElement();
if (target) resolve(target[0]);
else reject(new Error("No element inserted"));
});
});
}
FullEditor.prototype.enableInsertElement = function(modele,_callback) {
var frame = new JSYG(this.zoomAndPan.innerFrame),
that = this;
this.disableEdition();
if (modele) this.insertElementModel = modele;
function onmousedown(e) {
if (e.which != 1) return;
e.preventDefault();
var modele = that.insertElementModel;
if (!modele) throw new Error("You must define a model");
var shape = new JSYG(modele).clone(),
isText = JSYG.svgTexts.indexOf(shape.getTag()) !== -1;
that.insertElement(shape,e,isText);
if (that.autoEnableSelection) {
new JSYG(that.node).one('mouseup',function() {
that.shapeEditor.target(shape);
if (that.editText && isText) {
that.textEditor.target(shape).show();
that.textEditor.one("validate",_callback);
}
else {
that.shapeEditor.show();
if (_callback) _callback();
}
});
}
}
frame.on("mousedown",onmousedown).data("enableInsertElement",onmousedown);
this._setCursorDrawing();
return this;
};
FullEditor.prototype.disableInsertElement = function() {
var frame = new JSYG(this.zoomAndPan.innerFrame),
fct = frame.data("enableInsertElement");
if (!fct) return this;
frame.off("mousedown",fct);
this._removeCursorDrawing();
return this;
};
FullEditor.prototype.marqueeZoom = function(opt) {
var that = this;
return new Promise(function(resolve) {
that.enableMarqueeZoom(opt,function() {
that.disableMarqueeZoom();
resolve();
});
});
};
FullEditor.prototype.disableMarqueeZoom = function() {
this.zoomAndPan.marqueeZoom.disable();
return this;
};
FullEditor.prototype.enableMarqueeZoom = function(opt,_callback) {
if (this.zoomAndPan.marqueeZoom.enabled && !opt) return this;
this.disableEdition();
this.zoomAndPan.marqueeZoom.enable(opt);
if (_callback) this.zoomAndPan.marqueeZoom.one("end",_callback);
return this;
};
FullEditor.prototype.zoom = function(percent) {
this.zoomAndPan.scale( 1 + (percent/100) );
this.trigger("zoom",this,this.getDocument());
return this;
};
FullEditor.prototype.zoomTo = function(percentage) {
if (percentage == "canvas") this.zoomAndPan.fitToCanvas().scale(0.95);
else if (JSYG.isNumeric(percentage)) this.zoomAndPan.scaleTo( percentage/100 );
else throw new Error("argument must be numeric or 'canvas' string");
this.trigger("zoom",this,this.getDocument());
return this;
};
FullEditor.prototype.fitToDoc = function() {
var dim = new JSYG(this.getDocument()).getDim("screen"),
overflow = this.zoomAndPan.overflow;
this.zoomAndPan.size({
width : dim.width + (overflow!="hidden" ? 10 : 0),
height : dim.height + (overflow!="hidden" ? 10 : 0)
});
return this;
};
FullEditor.prototype._initZoomAndPan = function() {
var that = this;
this.zoomAndPan = new ZoomAndPan();
this.zoomAndPan.overflow = "auto";
this.zoomAndPan.scaleMin = 0;
this.zoomAndPan.resizable.keepViewBox = false;
this.zoomAndPan.resizable.keepRatio = false;
this.zoomAndPan.mouseWheelZoom.key = "ctrl";
this.zoomAndPan.on("change",function() {
that._updateBoundingBoxes();
that.shapeEditor.update();
that.textEditor.update();
});
return this;
};
FullEditor.prototype._initShapeEditor = function() {
var editor = new Editor();
var that = this;
editor.selection.multiple = true;
new JSYG(editor.container).on("dblclick",function(e) {
var target = editor.target();
if (!that.editText || JSYG.svgTexts.indexOf( target.getTag() ) == -1) return;
that.textEditor.target(target).show();
that.textEditor.cursor.setFromPos(e);
});
editor.selection.on("beforedeselect beforedrag",function(e) {
if (e.target == that.textEditor.container || new JSYG(e.target).isChildOf(that.textEditor.container)) return false;
});
editor.on({
show : function() {
that.textEditor.hide();
},
change : this.triggerChange,
drag : function(e) {
that.trigger("drag", that, e, editor._target);
},
changetarget : function() {
that.trigger("changetarget",that,editor._target);
}
});
//editor.ctrlsDrag.bounds = 0;
//editor.ctrlsResize.bounds = 0;
this.shapeEditor = editor;
return this;
};
FullEditor.prototype._initTextEditor = function() {
var that = this;
this.textEditor = new TextEditor();
this.textEditor.on({
show : function() {
that.shapeEditor.hide();
},
hide : function() {
var target = that.textEditor.target();
if (!target.text()) target.remove();
else that.shapeEditor.target(target).show();
},
validate : function() {
that.triggerChange();
}
});
return this;
};
FullEditor.prototype.setNode = function() {
JSYG.StdConstruct.prototype.setNode.apply(this,arguments);
this.zoomAndPan.setNode(this.node);
this.shapeEditor.setNode(this.node);
this.textEditor.setNode(this.node);
return this;
};
FullEditor.prototype._getDocumentSelector = function() {
return "#" + this.idContainer + " > svg ";
};
FullEditor.prototype._editableShapes = "*";
Object.defineProperty(FullEditor.prototype,'editableShapes',{
get : function() {
return this._editableShapes;
},
set : function(value) {
this._editableShapes = value;
this.shapeEditor.list = this._getLayerSelector()+value;
}
});
FullEditor.prototype.enableMousePan = function(opt) {
if (!this.zoomAndPan.mousePan.enabled) {
this.disableEdition();
this.zoomAndPan.mousePan.enable(opt);
}
return this;
};
FullEditor.prototype.disableMousePan = function() {
if (this.zoomAndPan.mousePan.enabled) {
this.zoomAndPan.mousePan.disable();
}
return this;
};
FullEditor.prototype.enableMouseWheelZoom = function(opt) {
if (!this.zoomAndPan.mouseWheelZoom.enabled) {
this.zoomAndPan.mouseWheelZoom.enable(opt);
}
return this;
};
FullEditor.prototype.disableMouseWheelZoom = function() {
if (this.zoomAndPan.mouseWheelZoom.enabled) {
this.zoomAndPan.mouseWheelZoom.disable();
}
return this;
};
FullEditor.prototype.canMoveBackwards = function() {
var shapes = new JSYG(this.shapeEditor.list),
target = this.shapeEditor.target();
return shapes.index(target) > 0 && shapes.length > 1;
};
FullEditor.prototype.canMoveForwards = function() {
var shapes = new JSYG(this.shapeEditor.list),
target = this.shapeEditor.target();
return shapes.index(target) < shapes.length-1 && shapes.length > 1;
};
FullEditor.prototype.isGroup = function() {
return this.shapeEditor.isGroup();
};
Object.defineProperty(FullEditor.prototype,'overflow',{
get : function() { return this.zoomAndPan.overflow; },
set : function(value) {
var displayShapeEditor = this.shapeEditor.display,
displaytextEditor = this.textEditor.display;
if (displayShapeEditor) this.shapeEditor.hide();
if (displaytextEditor) this.textEditor.hide();
this.zoomAndPan.overflow = value;
if (displayShapeEditor) this.shapeEditor.show();
if (displaytextEditor) this.textEditor.show();
}
});
FullEditor.prototype.enable = function(opt) {
this.disable();
if (opt) this.set(opt);
if (!this.node) throw new Error("node is not defined");
this.zoomAndPan.enable();
this._insertFrame();
//on force les valeurs pour exécuter les fonctions définies dans Object.defineProperty
if (this._editPathCtrlPoints) this._editPathCtrlPoints = true;
if (this._resizable) this._resizable = true;
this.editableShapes = this.editableShapes;
this.shapeEditor.enableCtrls('drag','resize','rotate','mainPoints');
if (this.autoEnableSelection) this.shapeEditor.enable();
this.enableKeyShortCuts();
this.enabled = true;
return this;
};
FullEditor.prototype.disable = function() {
this.disableEdition();
this._removeFrame();
this.zoomAndPan.disable();
this.undoRedo.disable();
this.disableKeyShortCuts();
this.enabled = false;
return this;
};
/**
* Aligne les éléments sélectionnés
* @param {String} type (top,middle,bottom,left,center,right)
* @returns {undefined}
*/
FullEditor.prototype.align = function(type) {
this.shapeEditor.align(type);
return this;
};
FullEditor.prototype.target = function(value) {
if (value == null) {
if (this.textEditor.display) return this.textEditor.target();
else return this.shapeEditor.target();
}
else {
this.shapeEditor.target( new JSYG(this.getDocument()).find(value) ).show();
}
return this;
};
FullEditor.prototype.editTextElmt = function(elmt) {
if (elmt == null) elmt = this.target();
this.textEditor.target(elmt).show();
return this;
};
FullEditor.prototype.dimDocument = function(dim) {
var doc = new JSYG( this.getDocument() );
var oldDim = doc.getDim();
if (dim == null) return oldDim;
if (dim.width && dim.width != oldDim.width || dim.height && dim.height != oldDim.height) {
doc.setDim(dim);
this.triggerChange();
this._adjustSize();
}
return this;
};
FullEditor.prototype.isMultiSelection = function() {
return this.shapeEditor.isMultiSelection();
};
FullEditor.prototype._adjustSize = function() {
var contenu = new JSYG( this.getDocument() ),
dim = contenu.length && contenu.getDim() || {width:0, height:0};
new JSYG(this._frameShadow).add(this._frame).attr({
width:dim.width,
height:dim.height
});
if (dim.width && dim.height) this.zoomTo('canvas');
if (!this.shapeEditor.ctrlsDrag.options) this.shapeEditor.ctrlsDrag.options = {};
if (!this.shapeEditor.ctrlsResize.options) this.shapeEditor.ctrlsResize.options = {};
this.shapeEditor.ctrlsDrag.options.guides = {
list : [{x:0},{x:dim.width},{y:0},{y:dim.height}]
};
this.shapeEditor.ctrlsResize.options.stepsX = {
list : [0,dim.width]
};
this.shapeEditor.ctrlsResize.options.stepsY = {
list : [0,dim.height]
};
return this;
};
FullEditor.prototype.createImage = function(src) {
var image = new JSYG('<image>').attr('href',src),
that = this;
return new Promise(function(resolve,reject) {
var img = new Image();
img.onload = function() {
var dimDoc = JSYG(that.getDocument()).viewBox(),
height = this.height,
width = this.width;
if (width > dimDoc.width) {
height = height * dimDoc.width / width;
width = dimDoc.width;
}
if (height > dimDoc.height) {
width = width * dimDoc.height / height;
height = dimDoc.height;
}
image.attr({width:width,height:height});
resolve(image[0]);
};
img.onerror = reject;
img.src = src;
});
};
FullEditor.prototype.insertElement = function(elmt,pos,_preventEvent) {
var textNode;
elmt = new JSYG(elmt);
elmt.appendTo( this._getLayerSelector() );
if (JSYG.svgTexts.indexOf(elmt.getTag())!=-1 && !elmt.text()) {
textNode = document.createTextNode("I");
elmt.append(textNode);
}
if (pos instanceof JSYG.Event) elmt.setCenter( elmt.getCursorPos(pos) );
else {
elmt.setDim({
x : pos && pos.x || 0,
y : pos && pos.y || 0
});
}
if (textNode) new JSYG(textNode).remove();
if (!_preventEvent) {
this.trigger("insert", this, this.getDocument(), elmt );
this.triggerChange();
}
return this;
};
function stopEvents(e) {
e.stopPropagation();
e.preventDefault();
}
FullEditor.prototype.lang = "en";
FullEditor.prototype.importSVGAs = "image";
FullEditor.prototype.enableDropFiles = function() {
var that = this;
var fcts = {
dragenter : stopEvents,
dragover : stopEvents,
drop : function(e) {
stopEvents(e);
var dt = e.originalEvent.dataTransfer;
var file, str;
if (!dt) return;
if (dt.files && dt.files.length) {
file = dt.files[0];
if (/svg/i.test(file.type) && that.importSVGAs.toLowerCase() == "svg") that.insertSVGFile(file,e);
else that.insertImageFile(file,e);
} else {
str = dt.getData("text")
if (str) {
that.importImage(str)
.then(function(img) { that.insertElement(img,e); that.target(img); })
.catch(function() {})
}
}
}
}
JSYG(this.zoomAndPan.innerFrame).on(fcts);
this.disableDropFiles = function() {
JSYG(this.zoomAndPan.innerFrame).off(fcts);
};
return this;
};
FullEditor.prototype.disableDropFiles = function() { return this; };
FullEditor.prototype.insertImageFile = function(file,e) {
var that = this;
return this.importImage(file)
.then(function(img) {
that.insertElement(img,e);
that.shapeEditor.target(img).show();
});
};
FullEditor.prototype.insertSVGFile = function(file,e) {
var that = this;
return this.readFile(file,"text")
.then(JSYG.parseSVG)
.then(function(svg) {
that.insertElement(svg,e);
that.shapeEditor.target(svg).show();
});
};
FullEditor.prototype.importImage = function(arg) {
var promise;
if (arg instanceof File) {
if (!arg.type.match(/image/)) return Promise.reject(new TypeError(arg.name + " is not an image file"));
promise = this.readFile(arg,'dataURL');
}
else {
if (arg.src) arg = arg.src; //DOMElement
else if (arg instanceof jQuery) {
arg = JSYG(arg);
arg = arg.attr( arg.isSVG() ? 'href' : 'src' );
if (!arg) throw new TypeError("no src/href attribute found");
}
else if (typeof arg != "string") throw new TypeError("argument incorrect"); //URL or dataURL
promise = Promise.resolve(arg);
}
return promise.then(this.createImage);
};
FullEditor.prototype.chooseFile = function() {