// Scripting by Oleg Okhotnikov, contact - svoboda200786@gmail.com let poupup = document.querySelector(".poupup-menu"); let actionsHist = []; let actionsIndex = -1; let myUndo = document.getElementById('undo-btn'); let myRedo = document.getElementById('redo-btn'); let group = {groupName: undefined}; myUndo.onclick = function() { if(actionsIndex >= 0){ undo_redo('undo'); actionsIndex--; var ctx = canvas.contextTop; ctx.clearRect(0, 0, canvas.width, canvas.height); } }; myRedo.onclick = function () { if(actionsIndex < actionsHist.length - 1){ actionsIndex++; undo_redo('redo'); var ctx = canvas.contextTop; ctx.clearRect(0, 0, canvas.width, canvas.height); } }; function onObjectAdded(obj){ let item = obj.target; if(!file_loaded || item.clonned){ // в этом условии объекты создаются при загрузке сохраненного файла или клонировании let shape_type = /shape.*/.test(item.groupName); // если groupName содержит "shape", то это примитив if(shape_type){ // если примитив item.parent = item; item.parent.added = true; // для составных объектов параметр added задается в functions.js // также added задается в controls.js и editor.js в add_image index++; item.index = index; shape[index] = item; item.clonned = false; item.groupName = /[a-zA-Z\_]+/.exec(item.groupName) + index; // перезаписываем groupName, добавляя новый индекс } if(!item.parent){ item.parent = item; } oldParam(item); } else if(!/legend.*/.test(item.groupName)){ if(item.parent && !item.parent.zoom_size){ item.parent.zoom_size = zoom_size; } if(item.type == "textbox"){ canvas.setActiveObject(item); } } if(canvas.isDrawingMode == 1){ item.groupName = "shape_pencil" + index; item.parent = item; item.perPixelTargetFind = true; item.parent.zoom_size = zoom_size; } let shape_type = /shape.*/.test(item.groupName); // если groupName shape, то это примитив if(shape_type && !item.parent.added){ // если примитив, объект создается после загрузки сцены index++; item.index = index; shape[index] = item; } shape_type = /shape_img.*/.test(item.groupName); if((shape_type || canvas.isDrawingMode == 1) && !item.parent.added){ // если создали картинку или // карандашную линию, то есть объекты которые создаются без срабатывания обработчика canvas.on('mouse:down'), // записываем в истории тут - для остальных см. controls.js функцию endOfCreating item.parent.added = true; oldParam(item); changes(item, "4"); actionsHist[actionsIndex].flag = 'add'; actionsHist.index = item.index; } // возможно так будет быстрее, чем через groupName, потому что в последнем применяется регулярка (пока реализовано только в геометрических ф-ях) item.groupType = /[a-zA-Z\_]+/.exec(item.groupName)[0]; } function onObjectSelected(obj){ let item = obj.target; // // выделена или нажата группа if(item != undefined){ if(item.type == "activeSelection"){ // объект является выделением item.hasControls = false; item.lockScalingX = true; item.lockScalingY = true; item.lockRotation = true; item.parent.zoom_size = item._objects[0].parent.zoom_size; } if(canvas.isDrawingMode == 0 && item != null && (item.type != "activeSelection" || item.shapes == 1) ){ if(palette.active){ palette.active.classList.remove("color-cell-active"); } palette.active = document.querySelector("[color='" + item.parent.stroke + "']") || document.querySelector("[color='" + item.parent.fill + "']"); if(palette.active){ palette.active.classList.add("color-cell-active"); } if(item.parent && item.parent.zoom_size != undefined){ var size = item.parent.zoom_size + 1; setTimeout(function(){ scale_indicator.setAttribute("scale_value", size + "x"); }, 10); } } } } function calcObjects(item, obj){ let prop = "_objects"; let array = obj[prop]; for(var i = 0; i < array.length; i++){ item.objects_temp.push(array[i]); if(array[i][prop] != undefined){ calcObjects(item, array[i]); } } } function changes(item, num){ if(item.index != undefined || item.type == "activeSelection" ){ //console.log("num: " + num); actionsIndex++; actionsHist.splice(actionsIndex, actionsHist.length - actionsIndex); histAdd(item); } group.param_saved = false; } function onObjectModified(obj){ if(!/back/.test(obj.target.groupName) && !/legend/.test(obj.target.groupName)){ changes(obj.target, "2"); } } function onObjectRemoved(item){ if(item != undefined){ changes(item, "3"); actionsHist.index = item.index; } } function onSelectionCleared(item){ if(typeof selection_temp2 != "undefined" && selection_temp2 ){ var line = item.deselected[0].parent; line.x1 = line.arrow.left; line.y1 = line.arrow.top; line.x2 = line.arrow2.left; line.y2 = line.arrow2.top; selection_temp2 = null; } poupup.classList.add("mfp-hide"); } document.addEventListener("click", function(e){ // алгоритм для выделения нескольких объектов находится в fabric.js let item = canvas.getActiveObject(); if(item != undefined && item.type == "activeSelection"){ item.parent = item; // oldParam(item); } if(e.target.className == "upper-canvas " && !move_flag && item != undefined && item.parent.objects != undefined && item.type != "i-text"){ } else if(move_flag){ let obj = {}; obj.target = item; // onObjectSelected(obj); } move_flag = false; }); canvas.on('mouse:down:before', function(e){ var obj = e.target; if(obj && editor_command || text_editing){ drawing_object = true; } if(obj){ if(drawing_object){ let array = obj.parent.objects; for(var i in array){ obj.parent[array[i]].selectable = false; } } else{ let array = obj.parent.objects; for(var i in array){ var name = obj.parent[array[i]].name; if( (name == "rect" && obj.parent[array[i]].groupType == "socket") || name == "arrow" || name == "arrow2" || name == "arrow_part1" || name == "arrow_part2" || name == "line"){ obj.parent[array[i]].selectable = true; } } } } if(group.cloneOne){ // __onMouseDown в fabric.js, там установлен getActiveObject().setCoords() let item = canvas.getActiveObject(); editor_obj.setClone(item); // item.hasControls = true; // canvas.setActiveObject(item); canvas.renderAll(); element_cursor(); } }); canvas.on('selection:created', onObjectSelected); canvas.on('selection:updated', onObjectSelected); canvas.on('selection:cleared', onSelectionCleared); canvas.on('mouse:down', onObjectSelected); canvas.on('object:modified', onObjectModified); canvas.on('object:changed', onObjectModified); canvas.on('object:added', onObjectAdded); ///////////////////////////////// oldParam ///////////////////////////////// /* Записывает в свойства с посфиксом old текущие значения - при отмене в них можно будет вернуться */ // Параметр old нужен потому, что предыдущее действик могло быть с другим объектом, поэтому // нельзя просто взять старое свойство из actionIndex -1 function oldParam(item){ if (item != null){ let shape_type = /shape.*/.test(item.groupName); if(item.parent.objects || item.parent._objects){ if(item.type == "activeSelection"){ var group_deltaX = item.width / 2 + item.left; // у объектов в группе отсчет координат относительно центра выделения var group_deltaY = item.height / 2 + item.top; // у объектов в группе отсчет координат относительно центра выделения var prop = "_objects"; } else{ var group_deltaX = 0; var group_deltaY = 0; var prop = "objects"; } let array = item.parent[prop]; // записываем части составного объекта в массив for(var i in array){ if(item.type == "activeSelection"){ // у объектов в группе отсчет координат относительно центра выделения var obj = array[i]; } else{ var obj = item.parent[array[i]]; } obj.set({ "x1_old": obj.x1, "x2_old": obj.x2, "y1_old": obj.y1, "y2_old": obj.y2, "left_old": obj.left + group_deltaX, "top_old": obj.top + group_deltaY, "width_old": obj.width, "height_old": obj.height, "originX_old": obj.originX, "originY_old": obj.originY, "scaleX_old": obj.scaleX, "scaleY_old": obj.scaleY, "angle_old": obj.angle, "text_old": obj.text, "fill_old": obj.fill, "fontWeight_old": obj.fontWeight, "fontSize_old": obj.fontSize, "stroke_old": obj.stroke, "strokeWidth_old": obj.strokeWidth, "visible_old": obj.visible, "zoom_size_old": obj.zoom_size }) } } else{ if(item.x1){ item.set({ "x1_old": item.x1, "x2_old": item.x2, "y1_old": item.y1, "y2_old": item.y2 }) } item.set({ "left_old": item.left, "top_old": item.top, "width_old": item.width, "height_old": item.height, "originX_old": item.originX, "originY_old": item.originY, "scaleX_old": item.scaleX, "scaleY_old": item.scaleY, "angle_old": item.angle, "text_old": item.text, "fill_old": item.fill, "fontWeight_old": item.fontWeight, "fontSize_old": item.fontSize, "stroke_old": item.stroke, "strokeWidth_old": item.strokeWidth, "visible_old": item.visible, "zoom_size_old": item.zoom_size }) } } } ///////////////////////////////// histAdd ///////////////////////////////// /* Добавляет в историю объект, с текущими параметрами объекта fabric */ function histAdd(item){ if (item != undefined){ let shape_type = /shape.*/.test(item.groupName); // Пушим в историю все составные части if(!shape_type || item.type == "activeSelection"){ // если не примитив actionsHist.push([]); // создаем новый элемент в истории if(item.type == "activeSelection"){ var group_deltaX = item.width / 2 + item.left; // у объектов в группе отсчет координат относительно центра выделения var group_deltaY = item.height / 2 + item.top; // у объектов в группе отсчет координат относительно центра выделения var prop = "_objects"; } else{ var group_deltaX = 0; var group_deltaY = 0; var prop = "objects"; } let array = item.parent[prop]; // записываем части составного объекта в массив let actionsHistEl = actionsHist[actionsHist.length-1]; // даем новому эелементу истории название // actionsHistEl.objects = []; // в это свойсто будут записываться старые и текущие параметры части // составного объекта for(var i in array){ if(item.type == "activeSelection"){ // у объектов в группе отсчет координат относительно центра выделения var obj = array[i]; } else{ var obj = item.parent[array[i]]; } actionsHistEl.push({ "name": obj.name, "index": obj.parent.index, "left_old": obj.left_old, "top_old": obj.top_old, "width_old": obj.width_old, "height_old": obj.height_old, "originX_old": obj.originX_old, "originY_old": obj.originY_old, "scaleX_old": obj.scaleX_old, "scaleY_old": obj.scaleY_old, "angle_old": obj.angle_old, 'x1_old': obj.x1_old, 'y1_old': obj.y1_old, 'x2_old': obj.x2_old, 'y2_old': obj.y2_old, "text_old": obj.text_old, "fill_old": obj.fill_old, "fontWeight_old": obj.fontWeight_old, "fontSize_old": obj.fontSize_old, "stroke_old": obj.stroke_old, "strokeWidth_old": obj.strokeWidth_old, "visible_old": obj.visible_old, "zoom_size_old": obj.zoom_size_old, "left": obj.left + group_deltaX, "top": obj.top + group_deltaY, "width": obj.width, "height": obj.height, "originX": obj.originX, "originY": obj.originY, "scaleX": obj.scaleX, "scaleY": obj.scaleY, "angle": obj.angle, 'x1': obj.x1, 'y1': obj.y1, 'x2': obj.x2, 'y2': obj.y2, "text": obj.text, "fill": obj.fill, "fontWeight": obj.fontWeight, "fontSize": obj.fontSize, "stroke": obj.stroke, "strokeWidth": obj.strokeWidth, "visible": obj.visible, "zoom_size": obj.zoom_size }); } } else{ actionsHist.push([]); // создаем новый элемент в истории let actionsHistEl = actionsHist[actionsHist.length-1]; actionsHistEl.push({ "index": item.index, "left_old": item.left_old, "top_old": item.top_old, "width_old": item.width_old, "height_old": item.height_old, "scaleX_old": item.scaleX_old, "scaleY_old": item.scaleY_old, "originX_old": item.originX_old, "originY_old": item.originY_old, "angle_old": item.angle_old, "text_old": item.text_old, "fill_old": item.fill_old, "fontWeight_old": item.fontWeight_old, "fontSize_old": item.fontSize_old, "stroke_old": item.stroke_old, "strokeWidth_old": item.strokeWidth_old, "visible_old": item.visible_old, "zoom_size_old": item.zoom_size_old, "left": item.left, "top": item.top, "width": item.width, "height": item.height, "originX": item.originX, "originY": item.originY, "scaleX": item.scaleX, "scaleY": item.scaleY, "angle": item.angle, "text": item.text, "fill": item.fill, "fontWeight": item.fontWeight, "fontSize": item.fontSize, "stroke": item.stroke, "strokeWidth": item.strokeWidth, "visible": item.visible, "zoom_size": item.zoom_size }); } } } ///////////////////////////////// undo_redo ///////////////////////////////// /* Возврат - повтор */ function undo_redo(flag){ if(Boolean(actionsHist[actionsIndex])){ // let item = shape[actionsHist[actionsIndex].index]; for (var obj of actionsHist[actionsIndex]){ if(obj.name){ var item = shape[obj.index][obj.name]; } else{ var item = shape[obj.index]; } if(actionsHist[actionsIndex].flag == "add" && flag == 'undo'){ // если объект был создан в этот момент истории deleteObj(item, true); } else{ canvas.discardActiveObject(); canvas.requestRenderAll(); if(flag == 'undo'){ setHistoryParam(item, "_old", obj); } else{ setHistoryParam(item, "", obj); } } } // shape[actionsHist[actionsIndex].index].setCoords(); } canvas.discardActiveObject().renderAll(); } ///////////////////////////////// setHistoryParam ///////////////////////////////// /* Устанавливает значения , записанные при помощи oldParam */ function setHistoryParam(item, postfix, obj){ if(item.name){ item.set({ "x1": obj["x1" + postfix], "x2": obj["x2" + postfix], "y1": obj["y1" + postfix], "y2": obj["y2" + postfix], "left": obj["left" + postfix], "top": obj["top" + postfix], "width": obj["width" + postfix], "height": obj["height" + postfix], "originX": obj["originX" + postfix], "originY": obj["originY" + postfix], "scaleX": obj["scaleX" + postfix], "scaleY": obj["scaleY" + postfix], "angle": obj["angle" + postfix], "text": obj["text" + postfix], "fill": obj["fill" + postfix], "fontWeight": obj["fontWeight" + postfix], "fontSize": obj["fontSize" + postfix], "stroke": obj["stroke" + postfix], "strokeWidth": obj["strokeWidth" + postfix], "visible": obj["visible" + postfix], "zoom_size": obj["zoom_size" + postfix] }); item.setCoords(); } // Примитивы: // Если выделено несколько объектов, втом числе составных, // то все будут расчитываться как сумма примитиввов, потому что обсчет идет // для item, в который записывается результат canvas.getActiveObject() - это будет выделение // или (activeSelection) else{ item.set({ "left": obj["left" + postfix], "top": obj["top" + postfix], "width": obj["width" + postfix], "height": obj["height" + postfix], "originX": obj["originX" + postfix], "originY": obj["originY" + postfix], "scaleX": obj["scaleX" + postfix], "scaleY": obj["scaleY" + postfix], "angle": obj["angle" + postfix], "text": obj["text" + postfix], "fill": obj["fill" + postfix], "fontWeight": obj["fontWeight" + postfix], "fontSize": obj["fontSize" + postfix], "stroke": obj["stroke" + postfix], "strokeWidth": obj["strokeWidth" + postfix], "visible": obj["visible" + postfix], "zoom_size": obj["zoom_size" + postfix] }); shape_type = /shape_svg.*/.test(item.groupName); if(shape_type){ editor_obj.add_legend(); } canvas.renderAll(); } // if(item.parent._objects != undefined){ // выделение или группа // setHistoryParam_subfunction(item, postfix); // } } function setHistoryParam_subfunction(item, postfix, array){ /* подфункция для объектов, создаваемых с помощью выделения или группы */ // if(shape[actionsHist[actionsIndex].index].type == "activeSelection"){ // var obj = shape[actionsHist[actionsIndex].index].objects_temp; // } // else{ var obj = shape[actionsHist[actionsIndex].index]._objects; // } if(array == undefined){ var array = actionsHist[actionsIndex]["_objects"]; } for(var i in array){ obj[i].set({ "fill": array[i]["fill" + postfix], "fontWeight": array[i]["fontWeight" + postfix], "fontSize": array[i]["fontSize" + postfix], "stroke": array[i]["stroke" + postfix], "strokeWidth": array[i]["strokeWidth" + postfix], "scaleX": array[i]["scaleX" + postfix], "scaleY": array[i]["scaleY" + postfix], "visible": array[i]["visible" + postfix], "zoom_size": array[i]["zoom_size" + postfix] }); shape_type = /shape_svg.*/.test(obj[i].groupName); if(shape_type){ editor_obj.add_legend(); } } }