// 定义一个区域图类:
function GooFlow(bgDiv, property) {
if (
navigator.userAgent.indexOf('MSIE 8.0') > 0 ||
navigator.userAgent.indexOf('MSIE 7.0') > 0 ||
navigator.userAgent.indexOf('MSIE 6.0') > 0
)
GooFlow.prototype.useSVG = ''
else GooFlow.prototype.useSVG = '1'
// 初始化区域图的对象
this.$id = bgDiv.attr('id')
this.$bgDiv = bgDiv // 最父框架的DIV
this.$bgDiv.addClass('GooFlow')
if (GooFlow.prototype.color.font) {
this.$bgDiv.css('color', GooFlow.prototype.color.font)
}
if (GooFlow.prototype.color.main) {
this.$bgDiv.append(
''
)
}
var width = property.width || 800
var height = property.height || 500
this.$bgDiv.css({ width: width + 'px', height: height + 'px' })
this.$tool = null // 左侧工具栏对象
this.$head = null // 顶部标签及工具栏按钮
this.$title = 'newFlow_1' // 流程图的名称
this.$nodeRemark = {} // 每一种结点或按钮的说明文字,JSON格式,key为类名,value为用户自定义文字说明
this.$nowType = 'cursor' // 当前要绘制的对象类型
this.$lineData = {}
this.$lineCount = 0
this.$nodeData = {}
this.$nodeCount = 0
this.$areaData = {}
this.$areaCount = 0
this.$lineDom = {}
this.$nodeDom = {}
this.$areaDom = {}
this.$max = property.initNum || 1 // 计算默认ID值的起始SEQUENCE
this.$focus = '' // 当前被选定的结点/转换线ID,如果没选中或者工作区被清空,则为""
this.$cursor = 'default' // 鼠标指针在工作区内的样式
this.$editable = false // 工作区是否可编辑
this.$deletedItem = {} // 在流程图的编辑操作中被删除掉的元素ID集合,元素ID为KEY,元素类型(node,line.area)为VALUE
this.$workExtendStep = 200 // 在自动/手动扩展可编辑区时,一次扩展后宽/高增加多少像素
var headHeight = 0
var tmp = ''
if (property.haveHead) {
tmp =
"
'
if (property.headLabel) {
tmp +=
"
'
}
for (var x = 0; x < property.headBtns.length; ++x) {
tmp +=
"
'
}
tmp += '
'
this.$head = $(tmp)
this.$bgDiv.append(this.$head)
headHeight = 28
// 以下是当工具栏按钮被点击时触发的事件自定义(虚函数),格式为function(),因为可直接用THIS操作对象本身,不用传参;用户可自行重定义:
this.onBtnNewClick = null // 新建流程图按钮被点中
this.onBtnOpenClick = null // 打开流程图按钮定义
this.onBtnSaveClick = null // 保存流程图按钮定义
this.onFreshClick = null // 重载流程图按钮定义
this.onPrintClick = null // 打印流程图按钮定义
if (property.headBtns)
this.$head.on('click', { inthis: this }, function(e) {
if (!e) e = window.event
var tar = e.target
if (tar.tagName == 'DIV' || tar.tagName == 'SPAN') return
else if (tar.tagName == 'A') tar = tar.childNodes[0]
var This = e.data.inthis
// 定义顶部操作栏按钮的事件
switch ($(tar).attr('class')) {
case 'ico_new':
if (This.onBtnNewClick != null) This.onBtnNewClick()
break
case 'ico_open':
if (This.onBtnOpenClick != null) This.onBtnOpenClick()
break
case 'ico_save':
if (This.onBtnSaveClick != null) This.onBtnSaveClick()
break
case 'ico_undo':
This.undo()
break
case 'ico_redo':
This.redo()
break
case 'ico_reload':
if (This.onFreshClick != null) This.onFreshClick()
break
case 'ico_print':
if (This.onPrintClick != null) This.onPrintClick()
break
}
})
}
var toolWidth = 0
if (property.haveTool) {
this.$bgDiv.append(
""
)
this.$tool = this.$bgDiv.find('.GooFlow_tool div')
// 未加代码:加入绘图工具按钮
this.$tool.append(
"
" +
"" +
"'
)
if (property.toolBtns && property.toolBtns.length > 0) {
tmp = ''
for (var i = 0; i < property.toolBtns.length; ++i) {
tmp +=
"" // 加入自定义按钮
}
this.$tool.append(tmp)
}
// 加入区域划分框工具开关按钮
if (property.haveGroup)
this.$tool.append(
""
)
toolWidth = 31
this.$nowType = 'cursor'
// 绑定各个按钮的点击事件
this.$tool.on('click', { inthis: this }, function(e) {
if (!e) e = window.event
var tar
switch (e.target.tagName) {
case 'SPAN':
return false
case 'DIV':
return false
case 'I':
tar = e.target.parentNode
break
case 'A':
tar = e.target
}
var type = $(tar).attr('type')
e.data.inthis.switchToolBtn(type)
return false
})
this.$editable = true // 只有具有工具栏时可编辑
}
width = width - toolWidth - 9
height = height - headHeight - (property.haveHead ? 5 : 8)
this.$bgDiv.append(
""
)
this.$workArea = $(
""
).attr({
unselectable: 'on',
onselectstart: 'return false',
onselect: 'document.selection.empty()'
})
this.$bgDiv.children('.GooFlow_work').append(this.$workArea)
this.$draw = null // 画矢量线条的容器
this.initDraw('draw_' + this.$id, width, height)
this.$group = null
if (property.haveGroup) this.initGroup(width, height)
this.initExpendFunc()
if (this.$editable) {
// 绑定工作区事件
this.$workArea.on('click', { inthis: this }, function(e) {
if (!e) e = window.event
var This = e.data.inthis
if (!This.$editable) return
var type = This.$nowType
if (type == 'cursor') {
var t = $(e.target)
var n = t.prop('tagName')
// alert(n);
if (
n == 'svg' ||
(n == 'DIV' && t.prop('class').indexOf('GooFlow_work') > -1) ||
n == 'LABEL'
) {
if (This.$lineOper.data('tid')) {
This.focusItem(This.$lineOper.data('tid'), false)
// This.$mpFrom.removeData("p");
} else {
This.blurItem()
}
}
return
} else if (type == 'direct' || type == 'group') return
var X, Y
var ev = mousePosition(e)
var t = getElCoordinate(this)
X = ev.x - t.left + this.parentNode.scrollLeft
Y = ev.y - t.top + this.parentNode.scrollTop
This.addNode(new Date().getTime(), {
name: 'node_' + This.$max,
left: X,
top: Y,
type: This.$nowType
})
This.$max++
})
// 划线或改线时用的绑定
this.$workArea.mousemove({ inthis: this }, function(e) {
if (e.data.inthis.$nowType != 'direct' && !e.data.inthis.$mpTo.data('p'))
return
var lineStart = $(this).data('lineStart')
var lineEnd = $(this).data('lineEnd')
if (!lineStart && !lineEnd) return
var ev = mousePosition(e)
var t = getElCoordinate(this)
var X, Y
X = ev.x - t.left + this.parentNode.scrollLeft
Y = ev.y - t.top + this.parentNode.scrollTop
var line = document.getElementById('GooFlow_tmp_line')
if (lineStart) {
if (GooFlow.prototype.useSVG != '') {
line.childNodes[0].setAttribute(
'd',
'M ' + lineStart.x + ' ' + lineStart.y + ' L ' + X + ' ' + Y
)
line.childNodes[1].setAttribute(
'd',
'M ' + lineStart.x + ' ' + lineStart.y + ' L ' + X + ' ' + Y
)
if (line.childNodes[1].getAttribute('marker-end') == 'url("#arrow2")')
line.childNodes[1].setAttribute('marker-end', 'url(#arrow3)')
else line.childNodes[1].setAttribute('marker-end', 'url(#arrow2)')
} else
line.points.value =
lineStart.x + ',' + lineStart.y + ' ' + X + ',' + Y
} else if (lineEnd) {
if (GooFlow.prototype.useSVG != '') {
line.childNodes[0].setAttribute(
'd',
'M ' + X + ' ' + Y + ' L ' + lineEnd.x + ' ' + lineEnd.y
)
line.childNodes[1].setAttribute(
'd',
'M ' + X + ' ' + Y + ' L ' + lineEnd.x + ' ' + lineEnd.y
)
if (line.childNodes[1].getAttribute('marker-end') == 'url("#arrow2")')
line.childNodes[1].setAttribute('marker-end', 'url(#arrow3)')
else line.childNodes[1].setAttribute('marker-end', 'url(#arrow2)')
} else
line.points.value = X + ',' + Y + ' ' + lineEnd.x + ',' + lineEnd.y
}
})
this.$workArea.mouseup({ inthis: this }, function(e) {
var This = e.data.inthis
if (This.$nowType != 'direct' && !This.$mpTo.data('p')) return
var tmp = document.getElementById('GooFlow_tmp_line')
if (tmp) {
$(this)
.css('cursor', 'auto')
.removeData('lineStart')
.removeData('lineEnd')
This.$mpTo.hide().removeData('p')
This.$mpFrom.hide().removeData('p')
This.$draw.removeChild(tmp)
This.focusItem(This.$focus, false)
} else {
This.$lineOper.removeData('tid')
}
})
// 为了结点而增加的一些集体delegate绑定
this.initWorkForNode()
// 对结点进行移动或者RESIZE时用来显示的遮罩层
this.$ghost = $("").attr({
unselectable: 'on',
onselectstart: 'return false',
onselect: 'document.selection.empty()'
})
this.$bgDiv.append(this.$ghost)
this.$textArea = $('')
this.$bgDiv.append(this.$textArea)
this.$lineMove = $(
''
) // 操作折线时的移动框
this.$workArea.append(this.$lineMove)
this.$lineMove.on('mousedown', { inthis: this }, function(e) {
if (e.button == 2) return false
var lm = $(this)
lm.css({ 'background-color': '#333' })
var This = e.data.inthis
var ev = mousePosition(e)
var t = getElCoordinateOveride(This.$workArea[0])
var X, Y
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
var p = This.$lineMove.position()
var vX = X - p.left
var vY = Y - p.top
var isMove = false
document.onmousemove = function(e) {
if (!e) e = window.event
var ev = mousePosition(e)
var ps = This.$lineMove.position()
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
if (This.$lineMove.data('type') == 'lr') {
X = X - vX
if (X < 0) X = 0
else if (X > This.$workArea.width()) X = This.$workArea.width()
This.$lineMove.css({ left: X + 'px' })
} else if (This.$lineMove.data('type') == 'tb') {
Y = Y - vY
if (Y < 0) Y = 0
else if (Y > This.$workArea.height()) Y = This.$workArea.height()
This.$lineMove.css({ top: Y + 'px' })
}
isMove = true
}
document.onmouseup = function(e) {
if (isMove) {
var p = This.$lineMove.position()
if (This.$lineMove.data('type') == 'lr')
This.setLineM(This.$lineMove.data('tid'), p.left + 3)
else if (This.$lineMove.data('type') == 'tb')
This.setLineM(This.$lineMove.data('tid'), p.top + 3)
}
This.$lineMove.css({ 'background-color': 'transparent' })
if (This.$focus == This.$lineMove.data('tid')) {
This.focusItem(This.$lineMove.data('tid'))
}
document.onmousemove = null
document.onmouseup = null
}
})
// 选定一条转换线后出现的浮动操作栏,有改变线的样式和删除线等按钮。
this.$lineOper = $(
"
"
) // 选定线时显示的操作框
this.$workArea.parent().append(this.$lineOper)
this.$lineOper.on('click', { inthis: this }, function(e) {
if (!e) e = window.event
if (e.target.tagName != 'I') return
var This = e.data.inthis
var id = $(this).data('tid')
switch ($(e.target).attr('class')) {
case 'b_x':
This.delLine(id)
this.style.display = 'none'
break
case 'b_l1':
This.setLineType(id, 'lr')
break
case 'b_l2':
This.setLineType(id, 'tb')
break
case 'b_l3':
This.setLineType(id, 'sl')
break
}
})
// 新增移动线两个端点至新的结点功能移动功能,这里要提供移动用的DOM
this.$mpFrom = $("")
this.$mpTo = $("")
this.$workArea.append(this.$mpFrom).append(this.$mpTo)
this.initLinePointsChg()
// 下面绑定当结点/线/分组块的一些操作事件,这些事件可直接通过this访问对象本身
// 当操作某个单元(结点/线/分组块)被添加时,触发的方法,返回FALSE可阻止添加事件的发生
// 格式function(id,type,json):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,json即addNode,addLine或addArea方法的第二个传参json.
this.onItemAdd = null
// 当操作某个单元(结点/线/分组块)被删除时,触发的方法,返回FALSE可阻止删除事件的发生
// 格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值
this.onItemDel = null
// 当操作某个单元(结点/分组块)被移动时,触发的方法,返回FALSE可阻止移动事件的发生
// 格式function(id,type,left,top):id是单元的唯一标识ID,type是单元的种类,有"node","area"两种取值,线line不支持移动,left是新的左边距坐标,top是新的顶边距坐标
this.onItemMove = null
// 当操作某个单元(结点/线/分组块)被重命名时,触发的方法,返回FALSE可阻止重命名事件的发生
// 格式function(id,name,type):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值,name是新的名称
this.onItemRename = null
// 当操作某个单元(结点/线)被由不选中变成选中时,触发的方法,返回FALSE可阻止选中事件的发生
// 格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被选中
this.onItemFocus = null
// 当操作某个单元(结点/线)被由选中变成不选中时,触发的方法,返回FALSE可阻止取消选中事件的发生
// 格式function(id,type):id是单元的唯一标识ID,type是单元的种类,有"node","line"两种取值,"area"不支持被取消选中
this.onItemBlur = null
// 当操作某个单元(结点/分组块)被重定义大小或造型时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
// 格式function(id,type,width,height):id是单元的唯一标识ID,type是单元的种类,有"node","line","area"三种取值;width是新的宽度,height是新的高度
this.onItemResize = null
// 当移动某条折线中段的位置,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
// 格式function(id,M):id是单元的唯一标识ID,M是中段的新X(或Y)的坐标
this.onLineMove = null
// 当变换某条连接线的类型,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
// 格式function(id,type):id是单元的唯一标识ID,type是连接线的新类型,"sl":直线,"lr":中段可左右移动的折线,"tb":中段可上下移动的折线
this.onLineSetType = null
// 当变换某条连接线的端点变更连接的结点时,触发的方法,返回FALSE可阻止重定大小/造型事件的发生
// 格式function(id,newStart,newEnd):id是连线单元的唯一标识ID,newStart,newEnd分别是起始结点的ID和到达结点的ID
this.onLinePointMove = null
// 当用重色标注某个结点/转换线时触发的方法,返回FALSE可阻止重定大小/造型事件的发生
// 格式function(id,type,mark):id是单元的唯一标识ID,type是单元类型("node"结点,"line"转换线),mark为布尔值,表示是要标注TRUE还是取消标注FALSE
this.onItemMark = null
this.onItemDbClick = null
if (property.useOperStack && this.$editable) {
// 如果要使用堆栈记录操作并提供“撤销/重做”的功能,只在编辑状态下有效
this.$undoStack = []
this.$redoStack = []
this.$isUndo = 0
/// ////////////以下是构造撤销操作/重做操作的方法
// 为了节省浏览器内存空间,undo/redo中的操作缓存栈,最多只可放40步操作;超过40步时,将自动删掉最旧的一个缓存
this.pushOper = function(funcName, paras) {
var len = this.$undoStack.length
if (this.$isUndo == 1) {
this.$redoStack.push([funcName, paras])
this.$isUndo = false
if (this.$redoStack.length > 40) this.$redoStack.shift()
} else {
this.$undoStack.push([funcName, paras])
if (this.$undoStack.length > 40) this.$undoStack.shift()
if (this.$isUndo == 0) {
this.$redoStack.splice(0, this.$redoStack.length)
}
this.$isUndo = 0
}
}
// 将外部的方法加入到GooFlow对象的事务操作堆栈中,在过后的undo/redo操作中可以进行控制,一般用于对流程图以外的附加信息进行编辑的事务撤销/重做控制;
// 传参func为要执行方法对象,jsonPara为外部方法仅有的一个面向字面的JSON传参,由JSON对象带入所有要传的信息;
// 提示:为了让外部方法能够被UNDO/REDO,需要在编写这些外部方法实现时,加入对该方法执行后效果回退的另一个执行方法的pushExternalOper
this.pushExternalOper = function(func, jsonPara) {
this.pushOper('externalFunc', [func, jsonPara])
}
// 撤销上一步操作
this.undo = function() {
if (this.$undoStack.length == 0) return
this.blurItem()
var tmp = this.$undoStack.pop()
this.$isUndo = 1
if (tmp[0] == 'externalFunc') {
tmp[1][0](tmp[1][1])
} else {
// 传参的数量,最多支持6个.
switch (tmp[1].length) {
case 0:
this[tmp[0]]()
break
case 1:
this[tmp[0]](tmp[1][0])
break
case 2:
this[tmp[0]](tmp[1][0], tmp[1][1])
break
case 3:
this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2])
break
case 4:
this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3])
break
case 5:
this[tmp[0]](
tmp[1][0],
tmp[1][1],
tmp[1][2],
tmp[1][3],
tmp[1][4]
)
break
case 6:
this[tmp[0]](
tmp[1][0],
tmp[1][1],
tmp[1][2],
tmp[1][3],
tmp[1][4],
tmp[1][5]
)
break
}
}
}
// 重做最近一次被撤销的操作
this.redo = function() {
if (this.$redoStack.length == 0) return
this.blurItem()
var tmp = this.$redoStack.pop()
this.$isUndo = 2
if (tmp[0] == 'externalFunc') {
tmp[1][0](tmp[1][1])
} else {
// 传参的数量,最多支持6个.
switch (tmp[1].length) {
case 0:
this[tmp[0]]()
break
case 1:
this[tmp[0]](tmp[1][0])
break
case 2:
this[tmp[0]](tmp[1][0], tmp[1][1])
break
case 3:
this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2])
break
case 4:
this[tmp[0]](tmp[1][0], tmp[1][1], tmp[1][2], tmp[1][3])
break
case 5:
this[tmp[0]](
tmp[1][0],
tmp[1][1],
tmp[1][2],
tmp[1][3],
tmp[1][4]
)
break
case 6:
this[tmp[0]](
tmp[1][0],
tmp[1][1],
tmp[1][2],
tmp[1][3],
tmp[1][4],
tmp[1][5]
)
break
}
}
}
}
$(document).keydown({ inthis: this }, function(e) {
// 绑定键盘操作
var This = e.data.inthis
if (This.$focus == '') return
switch (e.keyCode) {
case 46: // 删除
This.delNode(This.$focus, true)
This.delLine(This.$focus)
break
}
})
}
}
GooFlow.prototype = {
useSVG: '',
getSvgMarker: function(id, color) {
var m = document.createElementNS('http://www.w3.org/2000/svg', 'marker')
m.setAttribute('id', id)
m.setAttribute('viewBox', '0 0 6 6')
m.setAttribute('refX', 5)
m.setAttribute('refY', 3)
m.setAttribute('markerUnits', 'strokeWidth')
m.setAttribute('markerWidth', 6)
m.setAttribute('markerHeight', 6)
m.setAttribute('orient', 'auto')
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
path.setAttribute('d', 'M 0 0 L 6 3 L 0 6 z')
path.setAttribute('fill', color)
path.setAttribute('stroke-width', 0)
m.appendChild(path)
return m
},
initDraw: function(id, width, height) {
var elem
if (GooFlow.prototype.useSVG != '') {
this.$draw = document.createElementNS('http://www.w3.org/2000/svg', 'svg') // 可创建带有指定命名空间的元素节点
this.$workArea.prepend(this.$draw)
var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs')
this.$draw.appendChild(defs)
defs.appendChild(
GooFlow.prototype.getSvgMarker(
'arrow1',
GooFlow.prototype.color.line || '#3892D3'
)
)
defs.appendChild(
GooFlow.prototype.getSvgMarker(
'arrow2',
GooFlow.prototype.color.mark || '#ff8800'
)
)
defs.appendChild(
GooFlow.prototype.getSvgMarker(
'arrow3',
GooFlow.prototype.color.mark || '#ff8800'
)
)
} else {
this.$draw = document.createElement('v:group')
this.$draw.coordsize = width + ',' + height
this.$workArea.prepend(
""
)
this.$workArea.children('div')[0].insertBefore(this.$draw, null)
}
this.$draw.id = id
this.$draw.style.width = width + 'px'
this.$draw.style.height = +height + 'px'
// 绑定连线的点击选中以及双击编辑事件
var tmpClk = null
if (GooFlow.prototype.useSVG != '') tmpClk = 'g'
else tmpClk = 'PolyLine'
if (!this.$editable) return
$(this.$draw).delegate(tmpClk, 'click', { inthis: this }, function(e) {
e.data.inthis.focusItem(this.id, true)
})
$(this.$draw).delegate(tmpClk, 'dblclick', { inthis: this }, function(e) {
var oldTxt, x, y, from, to
var This = e.data.inthis
if (GooFlow.prototype.useSVG != '') {
oldTxt = this.childNodes[2].textContent
from = this.getAttribute('from').split(',')
to = this.getAttribute('to').split(',')
} else {
oldTxt = this.childNodes[1].innerHTML
var n = this.getAttribute('fromTo').split(',')
from = [n[0], n[1]]
to = [n[2], n[3]]
}
if (This.$lineData[this.id].type == 'lr') {
from[0] = This.$lineData[this.id].M
to[0] = from[0]
} else if (This.$lineData[this.id].type == 'tb') {
from[1] = This.$lineData[this.id].M
to[1] = from[1]
}
x = (parseInt(from[0], 10) + parseInt(to[0], 10)) / 2 - 64
y = (parseInt(from[1], 10) + parseInt(to[1], 10)) / 2 - 18
var t = getElCoordinateOveride(This.$workArea[0])
This.$textArea
.val(oldTxt)
.css({
display: 'block',
width: 130,
height: 26,
left: t.left + x - This.$workArea[0].parentNode.scrollLeft,
top: t.top + y - This.$workArea[0].parentNode.scrollTop
})
.data('id', This.$focus)
.focus()
This.$workArea.parent().one('mousedown', function(e) {
if (e.button == 2) return false
This.setName(This.$textArea.data('id'), This.$textArea.val(), 'line')
This.$textArea
.val('')
.removeData('id')
.hide()
})
})
},
initGroup: function(width, height) {
this.$group = $(
""
) // 存放背景区域的容器
this.$workArea.prepend(this.$group)
if (!this.$editable) return
// 区域划分框操作区的事件绑定
this.$group.on('mousedown', { inthis: this }, function(e) {
// 绑定RESIZE功能以及移动功能
if (e.button == 2) return false
var This = e.data.inthis
if (This.$nowType != 'group') return
if (!e) e = window.event
var cursor = $(e.target).css('cursor')
var id = e.target.parentNode
switch (cursor) {
case 'nw-resize':
id = id.parentNode
break
case 'w-resize':
id = id.parentNode
break
case 'n-resize':
id = id.parentNode
break
case 'move':
break
default:
return
}
id = id.id
var ev = mousePosition(e)
var t = getElCoordinateOveride(This.$workArea[0])
var X, Y
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
if (cursor != 'move') {
This.$ghost.css({
display: 'block',
width: This.$areaData[id].width + 'px',
height: This.$areaData[id].height + 'px',
top:
This.$areaData[id].top +
t.top -
This.$workArea[0].parentNode.scrollTop +
'px',
left:
This.$areaData[id].left +
t.left -
This.$workArea[0].parentNode.scrollLeft +
'px',
cursor: cursor
})
var vX = This.$areaData[id].left + This.$areaData[id].width - X
var vY = This.$areaData[id].top + This.$areaData[id].height - Y
} else {
var vX = X - This.$areaData[id].left
var vY = Y - This.$areaData[id].top
}
var isMove = false
This.$ghost.css('cursor', cursor)
document.onmousemove = function(e) {
if (!e) e = window.event
var ev = mousePosition(e)
if (cursor != 'move') {
X =
ev.x -
t.left +
This.$workArea[0].parentNode.scrollLeft -
This.$areaData[id].left +
vX
Y =
ev.y -
t.top +
This.$workArea[0].parentNode.scrollTop -
This.$areaData[id].top +
vY
if (X < 200) X = 200
if (Y < 100) Y = 100
switch (cursor) {
case 'nw-resize':
This.$ghost.css({ width: X + 'px', height: Y + 'px' })
break
case 'w-resize':
This.$ghost.css({ width: X + 'px' })
break
case 'n-resize':
This.$ghost.css({ height: Y + 'px' })
break
}
} else {
if (This.$ghost.css('display') == 'none') {
This.$ghost.css({
display: 'block',
width: This.$areaData[id].width + 'px',
height: This.$areaData[id].height + 'px',
top:
This.$areaData[id].top +
t.top -
This.$workArea[0].parentNode.scrollTop +
'px',
left:
This.$areaData[id].left +
t.left -
This.$workArea[0].parentNode.scrollLeft +
'px',
cursor: cursor
})
}
X = ev.x - vX
Y = ev.y - vY
if (X < t.left - This.$workArea[0].parentNode.scrollLeft)
X = t.left - This.$workArea[0].parentNode.scrollLeft
else if (
X +
This.$workArea[0].parentNode.scrollLeft +
This.$areaData[id].width >
t.left + This.$workArea.width()
)
X =
t.left +
This.$workArea.width() -
This.$workArea[0].parentNode.scrollLeft -
This.$areaData[id].width
if (Y < t.top - This.$workArea[0].parentNode.scrollTop)
Y = t.top - This.$workArea[0].parentNode.scrollTop
else if (
Y +
This.$workArea[0].parentNode.scrollTop +
This.$areaData[id].height >
t.top + This.$workArea.height()
)
Y =
t.top +
This.$workArea.height() -
This.$workArea[0].parentNode.scrollTop -
This.$areaData[id].height
This.$ghost.css({ left: X + 'px', top: Y + 'px' })
}
isMove = true
}
document.onmouseup = function(e) {
This.$ghost.empty().hide()
document.onmousemove = null
document.onmouseup = null
if (!isMove) return
if (cursor != 'move')
This.resizeArea(
id,
This.$ghost.outerWidth(),
This.$ghost.outerHeight()
)
else
This.moveArea(
id,
X + This.$workArea[0].parentNode.scrollLeft - t.left,
Y + This.$workArea[0].parentNode.scrollTop - t.top
)
return false
}
})
// 绑定修改文字说明功能
this.$group.on('dblclick', { inthis: this }, function(e) {
var This = e.data.inthis
if (This.$nowType != 'group') return
if (!e) e = window.event
if (e.target.tagName != 'LABEL') return false
var oldTxt = e.target.innerHTML
var p = e.target.parentNode
var x = parseInt(p.style.left, 10) + 18
var y = parseInt(p.style.top, 10) + 1
var t = getElCoordinateOveride(This.$workArea[0])
This.$textArea
.val(oldTxt)
.css({
display: 'block',
width: 120,
height: 26,
left: t.left + x - This.$workArea[0].parentNode.scrollLeft,
top: t.top + y - This.$workArea[0].parentNode.scrollTop
})
.data('id', p.id)
.focus()
This.$workArea.parent().one('mouseup', function(e) {
if (e.button == 2) return false
if (This.$textArea.css('display') == 'block') {
This.setName(This.$textArea.data('id'), This.$textArea.val(), 'area')
This.$textArea
.val('')
.removeData('id')
.hide()
}
return false
})
return false
})
// 绑定点击事件
this.$group.mouseup({ inthis: this }, function(e) {
var This = e.data.inthis
if (This.$textArea.css('display') == 'block') {
This.setName(This.$textArea.data('id'), This.$textArea.val(), 'area')
This.$textArea
.val('')
.removeData('id')
.hide()
return false
}
if (This.$nowType != 'group') return
if (!e) e = window.event
switch ($(e.target).attr('class')) {
case 'rs_close':
This.delArea(e.target.parentNode.parentNode.id)
return false // 删除该分组区域
case 'bg':
return
}
switch (e.target.tagName) {
case 'LABEL':
return false
case 'I': // 绑定变色功能
var id = e.target.parentNode.id
switch (This.$areaData[id].color) {
case 'red':
This.setAreaColor(id, 'yellow')
break
case 'yellow':
This.setAreaColor(id, 'blue')
break
case 'blue':
This.setAreaColor(id, 'green')
break
case 'green':
This.setAreaColor(id, 'red')
break
}
return false
}
if (e.data.inthis.$ghost.css('display') == 'none') {
var X, Y
var ev = mousePosition(e)
var t = getElCoordinateOveride(this)
X = ev.x - t.left + this.parentNode.parentNode.scrollLeft
Y = ev.y - t.top + this.parentNode.parentNode.scrollTop
var color = ['red', 'yellow', 'blue', 'green']
e.data.inthis.addArea(new Date().getTime(), {
name: 'area_' + e.data.inthis.$max,
left: X,
top: Y,
color: color[e.data.inthis.$max % 4],
width: 200,
height: 100
})
e.data.inthis.$max++
return false
}
})
},
// 加入手动扩展编辑区功能,一次扩展200px
initExpendFunc: function() {
this.$workArea.append(
''
)
this.$workArea
.children('.Gooflow_extend_right')
.on('click', { inthis: this }, function(e) {
var This = e.data.inthis
var w = This.$workArea.width() + This.$workExtendStep
var h = This.$workArea.height()
This.$workArea.css({ width: w + 'px' })
if (GooFlow.prototype.useSVG == '') {
This.$draw.coordsize = w + ',' + h
}
This.$draw.style.width = w + 'px'
if (This.$group != null) {
This.$group.css({ width: w + 'px' })
}
var parentDiv = This.$workArea.parent()[0]
parentDiv.scrollLeft = parentDiv.scrollWidth
return false
})
this.$workArea
.children('.Gooflow_extend_bottom')
.on('click', { inthis: this }, function(e) {
var This = e.data.inthis
var w = This.$workArea.width()
var h = This.$workArea.height() + This.$workExtendStep
This.$workArea.css({ height: h + 'px' })
if (GooFlow.prototype.useSVG == '') {
This.$draw.coordsize = w + ',' + h
}
This.$draw.style.height = h + 'px'
if (This.$group != null) {
This.$group.css({ height: h + 'px' })
}
var parentDiv = This.$workArea.parent()[0]
parentDiv.scrollTop = parentDiv.scrollHeight
return false
})
},
// 初始化用来改变连线的连接端点的两个小方块的操作事件
initLinePointsChg: function() {
this.$mpFrom.on('mousedown', { inthis: this }, function(e) {
var This = e.data.inthis
This.switchToolBtn('cursor')
var ps = This.$mpFrom.data('p').split(',')
var pe = This.$mpTo.data('p').split(',')
$(this).hide()
This.$workArea
.data('lineEnd', {
x: pe[0],
y: pe[1],
id: This.$lineData[This.$lineOper.data('tid')].to
})
.css('cursor', 'crosshair')
var line = GooFlow.prototype.drawLine(
'GooFlow_tmp_line',
[ps[0], ps[1]],
[pe[0], pe[1]],
true,
true
)
This.$draw.appendChild(line)
return false
})
this.$mpTo.on('mousedown', { inthis: this }, function(e) {
var This = e.data.inthis
This.switchToolBtn('cursor')
var ps = This.$mpFrom.data('p').split(',')
var pe = This.$mpTo.data('p').split(',')
$(this).hide()
This.$workArea
.data('lineStart', {
x: ps[0],
y: ps[1],
id: This.$lineData[This.$lineOper.data('tid')].from
})
.css('cursor', 'crosshair')
var line = GooFlow.prototype.drawLine(
'GooFlow_tmp_line',
[ps[0], ps[1]],
[pe[0], pe[1]],
true,
true
)
This.$draw.appendChild(line)
return false
})
},
// 每一种类型结点及其按钮的说明文字
setNodeRemarks: function(remark) {
if (this.$tool == null) return
this.$tool.children('a').each(function() {
this.title =
remark[
$(this)
.attr('id')
.split('btn_')[1]
]
})
this.$nodeRemark = remark
},
// 切换左边工具栏按钮,传参TYPE表示切换成哪种类型的按钮
switchToolBtn: function(type) {
this.$tool
.children('#' + this.$id + '_btn_' + this.$nowType.split(' ')[0])
.attr('class', 'GooFlow_tool_btn')
if (this.$nowType == 'group') {
this.$workArea.prepend(this.$group)
for (var key in this.$areaDom)
this.$areaDom[key]
.addClass('lock')
.children('div:eq(1)')
.css('display', 'none')
}
this.$nowType = type
this.$tool
.children('#' + this.$id + '_btn_' + type.split(' ')[0])
.attr('class', 'GooFlow_tool_btndown')
if (this.$nowType == 'group') {
this.blurItem()
this.$workArea.append(this.$group)
for (var key in this.$areaDom)
this.$areaDom[key]
.removeClass('lock')
.children('div:eq(1)')
.css('display', '')
} else if (this.$nowType == 'direct') {
this.blurItem()
}
if (this.$textArea.css('display') == 'none')
this.$textArea
.removeData('id')
.val('')
.hide()
},
// 增加一个流程结点,传参为一个JSON,有id,name,top,left,width,height,type(结点类型)等属性
addNode: function(id, json) {
if (this.onItemAdd != null && !this.onItemAdd(id, 'node', json)) return
if (this.$undoStack && this.$editable) {
this.pushOper('delNode', [id])
}
var mark = json.marked ? ' item_mark' : ''
if (json.type.indexOf(' round') < 0) {
if (!json.width || json.width < 104) json.width = 104
if (!json.height || json.height < 26) json.height = 26
if (!json.top || json.top < 0) json.top = 0
if (!json.left || json.left < 0) json.left = 0
this.$nodeDom[id] = $(
""
)
} else {
json.width = 26
json.height = 26
this.$nodeDom[id] = $(
"'
)
}
if (GooFlow.prototype.color.node) {
if (json.type.indexOf(' mix') > -1) {
this.$nodeDom[id].css({
'background-color': GooFlow.prototype.color.mix,
'border-color': GooFlow.prototype.color.mix
})
if (GooFlow.prototype.color.mixFont) {
this.$nodeDom[id]
.find('td:eq(1)')
.css('color', GooFlow.prototype.color.mixFont)
this.$nodeDom[id]
.find('.span')
.css('color', GooFlow.prototype.color.mixFont)
}
} else {
this.$nodeDom[id].css({
'background-color': GooFlow.prototype.color.node,
'border-color': GooFlow.prototype.color.node
})
}
if (mark && GooFlow.prototype.color.mark) {
this.$nodeDom[id].css({ 'border-color': GooFlow.prototype.color.mark })
}
}
if (json.type.indexOf(' mix') > -1) {
this.$nodeDom[id].addClass('item_mix')
}
var ua = navigator.userAgent.toLowerCase()
if (ua.indexOf('msie') != -1 && ua.indexOf('8.0') != -1)
this.$nodeDom[id].css(
'filter',
'progid:DXImageTransform.Microsoft.Shadow(color=#94AAC2,direction=135,strength=2)'
)
this.$workArea.append(this.$nodeDom[id])
this.$nodeData[id] = json
++this.$nodeCount
if (this.$editable) {
this.$nodeData[id].alt = true
if (this.$deletedItem[id]) delete this.$deletedItem[id] // 在回退删除操作时,去掉该元素的删除记录
}
},
initWorkForNode: function() {
// 绑定点击事件
this.$workArea.delegate(
'.GooFlow_item',
'click',
{ inthis: this },
function(e) {
e.data.inthis.focusItem(this.id, true)
$(this).removeClass('item_mark')
}
)
// 绑定用鼠标移动事件
this.$workArea.delegate('.ico', 'mousedown', { inthis: this }, function(e) {
if (!e) e = window.event
if (e.button == 2) return false
var This = e.data.inthis
if (This.$nowType == 'direct') return
var Dom = $(this).parents('.GooFlow_item')
var id = Dom.attr('id')
This.focusItem(id, true)
var ev = mousePosition(e)
var t = getElCoordinateOveride(This.$workArea[0])
Dom.children('table')
.clone()
.prependTo(This.$ghost)
var X, Y
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
var vX = X - This.$nodeData[id].left
var vY = Y - This.$nodeData[id].top
var isMove = false
document.onmousemove = function(e) {
if (!e) e = window.event
var ev = mousePosition(e)
if (X == ev.x - vX && Y == ev.y - vY) return false
X = ev.x - vX
Y = ev.y - vY
if (isMove && This.$ghost.css('display') == 'none') {
This.$ghost.css({
display: 'block',
width: This.$nodeData[id].width + 'px',
height: This.$nodeData[id].height + 'px',
top:
This.$nodeData[id].top +
t.top -
This.$workArea[0].parentNode.scrollTop +
'px',
left:
This.$nodeData[id].left +
t.left -
This.$workArea[0].parentNode.scrollLeft +
'px',
cursor: 'move'
})
}
if (X < t.left - This.$workArea[0].parentNode.scrollLeft)
X = t.left - This.$workArea[0].parentNode.scrollLeft
else if (
X +
This.$workArea[0].parentNode.scrollLeft +
This.$nodeData[id].width >
t.left + This.$workArea.width()
)
X =
t.left +
This.$workArea.width() -
This.$workArea[0].parentNode.scrollLeft -
This.$nodeData[id].width
if (Y < t.top - This.$workArea[0].parentNode.scrollTop)
Y = t.top - This.$workArea[0].parentNode.scrollTop
else if (
Y +
This.$workArea[0].parentNode.scrollTop +
This.$nodeData[id].height >
t.top + This.$workArea.height()
)
Y =
t.top +
This.$workArea.height() -
This.$workArea[0].parentNode.scrollTop -
This.$nodeData[id].height
This.$ghost.css({ left: X + 'px', top: Y + 'px' })
isMove = true
}
document.onmouseup = function(e) {
if (isMove)
This.moveNode(
id,
X + This.$workArea[0].parentNode.scrollLeft - t.left,
Y + This.$workArea[0].parentNode.scrollTop - t.top
)
This.$ghost.empty().hide()
document.onmousemove = null
document.onmouseup = null
}
})
if (!this.$editable) return
// 绑定鼠标覆盖/移出事件
this.$workArea.delegate(
'.GooFlow_item',
'mouseenter',
{ inthis: this },
function(e) {
if (
e.data.inthis.$nowType != 'direct' &&
!document.getElementById('GooFlow_tmp_line')
)
return
$(this)
.addClass('item_mark')
.addClass('crosshair')
.css('border-color', GooFlow.prototype.color.mark || '#ff8800')
}
)
this.$workArea.delegate(
'.GooFlow_item',
'mouseleave',
{ inthis: this },
function(e) {
if (
e.data.inthis.$nowType != 'direct' &&
!document.getElementById('GooFlow_tmp_line')
)
return
$(this)
.removeClass('item_mark')
.removeClass('crosshair')
if (this.id == e.data.inthis.$focus) {
$(this).css('border-color', GooFlow.prototype.color.line || '#3892D3')
} else {
$(this).css('border-color', GooFlow.prototype.color.node || '#A1DCEB')
}
}
)
// 绑定连线时确定初始点
this.$workArea.delegate(
'.GooFlow_item',
'mousedown',
{ inthis: this },
function(e) {
if (e.button == 2) return false
var This = e.data.inthis
if (This.$nowType != 'direct') return
var ev = mousePosition(e)
var t = getElCoordinate(This.$workArea[0])
var X, Y
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
This.$workArea
.data('lineStart', { x: X, y: Y, id: this.id })
.css('cursor', 'crosshair')
var line = GooFlow.prototype.drawLine(
'GooFlow_tmp_line',
[X, Y],
[X, Y],
true,
true
)
This.$draw.appendChild(line)
}
)
// 绑定连线时确定结束点
this.$workArea.delegate(
'.GooFlow_item',
'mouseup',
{ inthis: this },
function(e) {
var This = e.data.inthis
if (This.$nowType != 'direct' && !This.$mpTo.data('p')) return
var lineStart = This.$workArea.data('lineStart')
var lineEnd = This.$workArea.data('lineEnd')
if (lineStart && !This.$mpTo.data('p')) {
This.addLine(new Date().getTime(), {
from: lineStart.id,
to: this.id,
name: ''
})
This.$max++
} else {
if (lineStart) {
This.moveLinePoints(This.$focus, lineStart.id, this.id)
} else if (lineEnd) {
This.moveLinePoints(This.$focus, this.id, lineEnd.id)
}
if (!This.$nodeData[this.id].marked) {
$(this).removeClass('item_mark')
if (this.id != This.$focus) {
$(this).css('border-color', GooFlow.prototype.color.node)
} else {
$(this).css('border-color', GooFlow.prototype.color.line)
}
}
}
}
)
// 绑定双击编辑事件
this.$workArea.delegate(
'.GooFlow_item > .span',
'dblclick',
{ inthis: this },
function(e) {
var oldTxt = this.innerHTML
var This = e.data.inthis
var id = this.parentNode.id
var t = getElCoordinateOveride(This.$workArea[0])
This.$textArea
.val(oldTxt)
.css({
display: 'block',
height: $(this).height() + 6,
width: 100,
left:
t.left +
This.$nodeData[id].left -
This.$workArea[0].parentNode.scrollLeft -
26,
top:
t.top +
This.$nodeData[id].top -
This.$workArea[0].parentNode.scrollTop +
26
})
.data('id', This.$focus)
.focus()
This.$workArea.parent().one('mousedown', function(e) {
if (e.button == 2) return false
This.setName(This.$textArea.data('id'), This.$textArea.val(), 'node')
This.$textArea
.val('')
.removeData('id')
.hide()
})
}
)
this.$workArea.delegate('.ico + td', 'dblclick', { inthis: this }, function(
e
) {
var oldTxt = this.innerHTML
var This = e.data.inthis
var id = $(this)
.parents('.GooFlow_item')
.attr('id')
winDbClick(oldTxt, id)
/* var t = getElCoordinateOveride(This.$workArea[0])
This.$textArea
.val(oldTxt)
.css({
display: 'block',
width: $(this).width() + 26,
height: $(this).height() + 6,
left:
t.left +
26 +
This.$nodeData[id].left -
This.$workArea[0].parentNode.scrollLeft,
top:
t.top +
2 +
This.$nodeData[id].top -
This.$workArea[0].parentNode.scrollTop
})
.data('id', This.$focus)
.focus()
This.$workArea.parent().one('mousedown', function(e) {
if (e.button == 2) return false
This.setName(This.$textArea.data('id'), This.$textArea.val(), 'node')
This.$textArea
.val('')
.removeData('id')
.hide()
}) */
})
// 绑定结点的删除功能
this.$workArea.delegate('.rs_close', 'click', { inthis: this }, function(
e
) {
if (!e) e = window.event
e.data.inthis.delNode(e.data.inthis.$focus)
return false
})
// 绑定结点的RESIZE功能
this.$workArea.delegate(
'.GooFlow_item > div > div[class!=rs_close]',
'mousedown',
{ inthis: this },
function(e) {
if (!e) e = window.event
if (e.button == 2) return false
var cursor = $(this).css('cursor')
if (cursor == 'pointer') {
return
}
var This = e.data.inthis
var id = This.$focus
This.switchToolBtn('cursor')
e.cancelBubble = true
e.stopPropagation()
var ev = mousePosition(e)
var t = getElCoordinateOveride(This.$workArea[0])
This.$ghost.css({
display: 'block',
width: This.$nodeData[id].width + 'px',
height: This.$nodeData[id].height + 'px',
top:
This.$nodeData[id].top +
t.top -
This.$workArea[0].parentNode.scrollTop +
'px',
left:
This.$nodeData[id].left +
t.left -
This.$workArea[0].parentNode.scrollLeft +
'px',
cursor: cursor
})
var X, Y
X = ev.x - t.left + This.$workArea[0].parentNode.scrollLeft
Y = ev.y - t.top + This.$workArea[0].parentNode.scrollTop
var vX = This.$nodeData[id].left + This.$nodeData[id].width - X
var vY = This.$nodeData[id].top + This.$nodeData[id].height - Y
var isMove = false
This.$ghost.css('cursor', cursor)
document.onmousemove = function(e) {
if (!e) e = window.event
var ev = mousePosition(e)
X =
ev.x -
t.left +
This.$workArea[0].parentNode.scrollLeft -
This.$nodeData[id].left +
vX
Y =
ev.y -
t.top +
This.$workArea[0].parentNode.scrollTop -
This.$nodeData[id].top +
vY
if (X < 104) X = 104
if (Y < 26) Y = 26
isMove = true
switch (cursor) {
case 'nw-resize':
This.$ghost.css({ width: X + 'px', height: Y + 'px' })
break
case 'w-resize':
This.$ghost.css({ width: X + 'px' })
break
case 'n-resize':
This.$ghost.css({ height: Y + 'px' })
break
}
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
This.$ghost.hide()
if (!isMove) return
if (!e) e = window.event
This.resizeNode(
id,
This.$ghost.outerWidth(),
This.$ghost.outerHeight()
)
}
}
)
},
// 获取结点/连线/分组区域的详细信息
getItemInfo: function(id, type) {
switch (type) {
case 'node':
return this.$nodeData[id] || null
case 'line':
return this.$lineData[id] || null
case 'area':
return this.$areaData[id] || null
}
},
// 取消所有结点/连线被选定的状态
blurItem: function() {
if (this.$focus != '') {
var jq = $('#' + this.$focus)
if (jq.prop('tagName') == 'DIV') {
if (this.onItemBlur != null && !this.onItemBlur(this.$focus, 'node'))
return false
jq.removeClass('item_focus')
.children('div:eq(0)')
.css('display', 'none')
if (this.$nodeData[this.$focus].marked) {
jq.addClass('item_mark').css(
'border-color',
GooFlow.prototype.color.mark || '#ff8800'
)
}
} else {
if (this.onItemBlur != null && !this.onItemBlur(this.$focus, 'line'))
return false
if (GooFlow.prototype.useSVG != '') {
if (!this.$lineData[this.$focus].marked) {
jq[0].childNodes[1].setAttribute(
'stroke',
GooFlow.prototype.color.line || '#3892D3'
)
jq[0].childNodes[1].setAttribute('marker-end', 'url(#arrow1)')
}
} else {
if (!this.$lineData[this.$focus].marked) {
jq[0].strokeColor = GooFlow.prototype.color.line || '#3892D3'
}
}
this.$lineMove
.hide()
.removeData('type')
.removeData('tid')
if (this.$editable) {
this.$lineOper.hide().removeData('tid')
this.$mpFrom.hide().removeData('p')
this.$mpTo.hide().removeData('p')
}
}
}
this.$focus = ''
return true
},
// 选定某个结点/转换线 bool:TRUE决定了要触发选中事件,FALSE则不触发选中事件,多用在程序内部调用。
focusItem: function(id, bool) {
var jq = $('#' + id)
if (jq.length == 0) return
if (!this.blurItem()) return // 先执行"取消选中",如果返回FLASE,则也会阻止选定事件继续进行.
if (jq.prop('tagName') == 'DIV') {
if (bool && this.onItemFocus != null && !this.onItemFocus(id, 'node'))
return
jq.addClass('item_focus')
if (GooFlow.prototype.color.line) {
jq.css('border-color', GooFlow.prototype.color.line)
}
if (this.$editable) jq.children('div:eq(0)').css('display', 'block')
this.$workArea.append(jq)
} else {
// 如果是连接线
if (this.onItemFocus != null && !this.onItemFocus(id, 'line')) return
if (GooFlow.prototype.useSVG != '') {
jq[0].childNodes[1].setAttribute(
'stroke',
GooFlow.prototype.color.mark || '#ff8800'
)
jq[0].childNodes[1].setAttribute('marker-end', 'url(#arrow2)')
} else {
jq[0].strokeColor = GooFlow.prototype.color.mark || '#ff8800'
}
if (!this.$editable) return
var x, y, from, to, n
if (GooFlow.prototype.useSVG != '') {
from = jq.attr('from').split(',')
to = jq.attr('to').split(',')
n = [from[0], from[1], to[0], to[1]]
} else {
n = jq[0].getAttribute('fromTo').split(',')
from = [n[0], n[1]]
to = [n[2], n[3]]
}
from[0] = parseInt(from[0], 10)
from[1] = parseInt(from[1], 10)
to[0] = parseInt(to[0], 10)
to[1] = parseInt(to[1], 10)
// var t=getElCoordinateOveride(this.$workArea[0]);
if (this.$lineData[id].type == 'lr') {
from[0] = this.$lineData[id].M
to[0] = from[0]
this.$lineMove
.css({
width: '5px',
height: (to[1] - from[1]) * (to[1] > from[1] ? 1 : -1) + 'px',
left: from[0] - 3 + 'px',
top: (to[1] > from[1] ? from[1] : to[1]) + 1 + 'px',
cursor: 'e-resize',
display: 'block'
})
.data({ type: 'lr', tid: id })
} else if (this.$lineData[id].type == 'tb') {
from[1] = this.$lineData[id].M
to[1] = from[1]
this.$lineMove
.css({
width: (to[0] - from[0]) * (to[0] > from[0] ? 1 : -1) + 'px',
height: '5px',
left: (to[0] > from[0] ? from[0] : to[0]) + 1 + 'px',
top: from[1] - 3 + 'px',
cursor: 's-resize',
display: 'block'
})
.data({ type: 'tb', tid: id })
}
x = (from[0] + to[0]) / 2 - 40
y = (from[1] + to[1]) / 2 + 4
this.$lineOper
.css({ display: 'block', left: x + 'px', top: y + 'px' })
.data('tid', id)
if (this.$editable) {
this.$mpFrom
.css({
display: 'block',
left: n[0] - 4 + 'px',
top: n[1] - 4 + 'px'
})
.data('p', n[0] + ',' + n[1])
this.$mpTo
.css({
display: 'block',
left: n[2] - 4 + 'px',
top: n[3] - 4 + 'px'
})
.data('p', n[2] + ',' + n[3])
}
this.$draw.appendChild(jq[0])
}
this.$focus = id
this.switchToolBtn('cursor')
},
// 移动结点到一个新的位置
moveNode: function(id, left, top) {
if (!this.$nodeData[id]) return
if (this.onItemMove != null && !this.onItemMove(id, 'node', left, top))
return
if (this.$undoStack) {
var paras = [id, this.$nodeData[id].left, this.$nodeData[id].top]
this.pushOper('moveNode', paras)
}
if (left < 0) left = 0
if (top < 0) top = 0
$('#' + id).css({ left: left + 'px', top: top + 'px' })
this.$nodeData[id].left = left
this.$nodeData[id].top = top
// 重画转换线
this.resetLines(id, this.$nodeData[id])
if (this.$editable) {
this.$nodeData[id].alt = true
}
},
// 设置结点/连线/分组区域的文字信息
setName: function(id, name, type) {
var oldName
if (type == 'node') {
// 如果是结点
if (!this.$nodeData[id]) return
if (this.$nodeData[id].name == name) return
if (this.onItemRename != null && !this.onItemRename(id, name, 'node'))
return
oldName = this.$nodeData[id].name
this.$nodeData[id].name = name
if (this.$nodeData[id].type.indexOf('round') > 1) {
this.$nodeDom[id].children('.span').text(name)
} else {
this.$nodeDom[id].find('td:eq(1)').text(name)
var width = this.$nodeDom[id].width()
var height = this.$nodeDom[id].height()
this.$nodeDom[id]
.children('table')
.css({ width: width + 'px', height: height + 'px' })
this.$nodeData[id].width = width
this.$nodeData[id].height = height
}
if (this.$editable) {
this.$nodeData[id].alt = true
}
// 重画转换线
this.resetLines(id, this.$nodeData[id])
} else if (type == 'line') {
// 如果是线
if (!this.$lineData[id]) return
if (this.$lineData[id].name == name) return
if (this.onItemRename != null && !this.onItemRename(id, name, 'line'))
return
oldName = this.$lineData[id].name
this.$lineData[id].name = name
if (GooFlow.prototype.useSVG != '') {
this.$lineDom[id].childNodes[2].textContent = name
} else {
this.$lineDom[id].childNodes[1].innerHTML = name
var n = this.$lineDom[id].getAttribute('fromTo').split(',')
var x
if (this.$lineData[id].type != 'lr') {
x = (n[2] - n[0]) / 2
} else {
var Min = n[2] > n[0] ? n[0] : n[2]
if (Min > this.$lineData[id].M) Min = this.$lineData[id].M
x = this.$lineData[id].M - Min
}
if (x < 0) x = x * -1
this.$lineDom[id].childNodes[1].style.left =
x - this.$lineDom[id].childNodes[1].offsetWidth / 2 + 4 + 'px'
}
if (this.$editable) {
this.$lineData[id].alt = true
}
} else if (type == 'area') {
// 如果是分组区域
if (!this.$areaData[id]) return
if (this.$areaData[id].name == name) return
if (this.onItemRename != null && !this.onItemRename(id, name, 'area'))
return
oldName = this.$areaData[id].name
this.$areaData[id].name = name
this.$areaDom[id].children('label').text(name)
if (this.$editable) {
this.$areaData[id].alt = true
}
}
if (this.$undoStack) {
var paras = [id, oldName, type]
this.pushOper('setName', paras)
}
},
// 设置结点的尺寸,仅支持非开始/结束结点
resizeNode: function(id, width, height) {
if (!this.$nodeData[id]) return
if (
this.onItemResize != null &&
!this.onItemResize(id, 'node', width, height)
)
return
if (this.$nodeData[id].type == 'start' || this.$nodeData[id].type == 'end')
return
if (this.$undoStack) {
var paras = [id, this.$nodeData[id].width, this.$nodeData[id].height]
this.pushOper('resizeNode', paras)
}
this.$nodeDom[id]
.children('table')
.css({ width: width - 2 + 'px', height: height - 2 + 'px' })
// width=this.$nodeDom[id].outerWidth();
// height=this.$nodeDom[id].outerHeight();
// this.$nodeDom[id].children("table").css({width:width-2+"px",height:height-2+"px"});
this.$nodeData[id].width = width
this.$nodeData[id].height = height
if (this.$editable) {
this.$nodeData[id].alt = true
}
// 重画转换线
this.resetLines(id, this.$nodeData[id])
},
// 删除结点
delNode: function(id, trigger) {
if (!this.$nodeData[id]) return
if (
trigger != false &&
this.onItemDel != null &&
!this.onItemDel(id, 'node')
)
return
// 先删除可能的连线
for (var k in this.$lineData) {
if (this.$lineData[k].from == id || this.$lineData[k].to == id) {
// this.$draw.removeChild(this.$lineDom[k]);
// delete this.$lineData[k];
// delete this.$lineDom[k];
this.delLine(k, false)
}
}
// 再删除结点本身
if (this.$undoStack) {
var paras = [id, this.$nodeData[id]]
this.pushOper('addNode', paras)
}
delete this.$nodeData[id]
this.$nodeDom[id].remove()
delete this.$nodeDom[id]
--this.$nodeCount
if (this.$focus == id) this.$focus = ''
if (this.$editable) {
// 在回退新增操作时,如果节点ID以this.$id+"_node_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
if (id.indexOf(this.$id + '_node_') < 0) this.$deletedItem[id] = 'node'
}
},
// 设置流程图的名称
setTitle: function(text) {
this.$title = text
if (this.$head)
this.$head
.children('label')
.attr('title', text)
.text(text)
},
// 载入一组数据
loadData: function(data) {
var t = this.$editable
this.$editable = false
if (data.title) this.setTitle(data.title)
if (data.initNum) this.$max = data.initNum
for (var i in data.nodes) this.addNode(i, data.nodes[i])
for (var j in data.lines) this.addLine(j, data.lines[j])
for (var k in data.areas) this.addArea(k, data.areas[k])
this.$editable = t
this.$deletedItem = {}
// 自行重构工作区,使之大小自适应
var width = this.$workArea.width()
var height = this.$workArea.height()
var maxW = 0
var maxH = 0
for (var key in this.$nodeData) {
var item = this.$nodeData[key]
if (maxW < item.width + item.left) {
maxW = item.width + item.left
}
if (maxH < item.height + item.top) {
maxH = item.height + item.top
}
}
for (var key in this.$areaData) {
var item = this.$areaData[key]
if (maxW < item.width + item.left) {
maxW = item.width + item.left
}
if (maxH < item.height + item.top) {
maxH = item.height + item.top
}
}
for (var key in this.$lineData) {
var item = this.$lineData[key]
if (item.M && item.type == 'lt' && maxW < item.M) {
maxW = M + 4
}
if (item.M && item.type == 'tb' && maxH < item.M) {
maxH = M + 4
}
}
while (maxW > width) {
width += this.$workExtendStep
}
while (maxH > height) {
height += this.$workExtendStep
}
this.$workArea.css({ height: height + 'px', width: width + 'px' })
if (GooFlow.prototype.useSVG == '') {
this.$draw.coordsize = width + ',' + height
}
this.$draw.style.width = width + 'px'
this.$draw.style.height = +height + 'px'
if (this.$group != null) {
this.$group.css({ height: height + 'px', width: width + 'px' })
}
},
// 用AJAX方式,远程读取一组数据
// 参数para为JSON结构,与JQUERY中$.ajax()方法的传参一样
loadDataAjax: function(para) {
var This = this
$.ajax({
type: para.type,
url: para.url,
dataType: 'json',
data: para.data,
success: function(msg) {
if (para.dataFilter) para.dataFilter(msg, 'json')
This.loadData(msg)
if (para.success) para.success(msg)
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
if (para.error) para.error(textStatus, errorThrown)
}
})
},
// 把画好的整个流程图导出到一个变量中(其实也可以直接访问GooFlow对象的$nodeData,$lineData,$areaData这三个JSON属性)
exportData: function() {
var ret = {
title: this.$title,
nodes: this.$nodeData,
lines: this.$lineData,
areas: this.$areaData,
initNum: this.$max
}
for (var k1 in ret.nodes) {
if (!ret.nodes[k1].marked) {
delete ret.nodes[k1].marked
}
}
for (var k2 in ret.lines) {
if (!ret.lines[k2].marked) {
delete ret.lines[k2].marked
}
}
return ret
},
// 只把本次编辑流程图中作了变更(包括增删改)的元素导出到一个变量中,以方便用户每次编辑载入的流程图后只获取变更过的数据
exportAlter: function() {
var ret = { nodes: {}, lines: {}, areas: {} }
for (var k1 in this.$nodeData) {
if (this.$nodeData[k1].alt) {
ret.nodes[k1] = this.$nodeData[k1]
}
}
for (var k2 in this.$lineData) {
if (this.$lineData[k2].alt) {
ret.lines[k2] = this.$lineData[k2]
}
}
for (var k3 in this.$areaData) {
if (this.$areaData[k3].alt) {
ret.areas[k3] = this.$areaData[k3]
}
}
ret.deletedItem = this.$deletedItem
return ret
},
// 变更元素的ID,一般用于快速保存后,将后台返回新元素的ID更新到页面中;type为元素类型(节点,连线,区块)
transNewId: function(oldId, newId, type) {
var tmp
switch (type) {
case 'node':
if (this.$nodeData[oldId]) {
tmp = this.$nodeData[oldId]
delete this.$nodeData[oldId]
this.$nodeData[newId] = tmp
tmp = this.$nodeDom[oldId].attr('id', newId)
delete this.$nodeDom[oldId]
this.$nodeDom[newId] = tmp
}
break
case 'line':
if (this.$lineData[oldId]) {
tmp = this.$lineData[oldId]
delete this.$lineData[oldId]
this.$lineData[newId] = tmp
tmp = this.$lineDom[oldId].attr('id', newId)
delete this.$lineDom[oldId]
this.$lineDom[newId] = tmp
}
break
case 'area':
if (this.$areaData[oldId]) {
tmp = this.$areaData[oldId]
delete this.$areaData[oldId]
this.$areaData[newId] = tmp
tmp = this.$areaDom[oldId].attr('id', newId)
delete this.$areaDom[oldId]
this.$areaDom[newId] = tmp
}
break
}
},
// 清空工作区及已载入的数据
clearData: function() {
for (var key in this.$nodeData) {
this.delNode(key)
}
for (var key in this.$lineData) {
this.delLine(key)
}
for (var key in this.$areaData) {
this.delArea(key)
}
this.$deletedItem = {}
},
// 销毁自己
destrory: function() {
this.$bgDiv.empty()
this.$lineData = null
this.$nodeData = null
this.$lineDom = null
this.$nodeDom = null
this.$areaDom = null
this.$areaData = null
this.$nodeCount = 0
this.$areaCount = 0
this.$areaCount = 0
this.$deletedItem = {}
},
/// ////////以下为有关画线的方法
// 绘制一条箭头线,并返回线的DOM
drawLine: function(id, sp, ep, mark, dash) {
var line
if (GooFlow.prototype.useSVG != '') {
line = document.createElementNS('http://www.w3.org/2000/svg', 'g')
var hi = document.createElementNS('http://www.w3.org/2000/svg', 'path')
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
if (id != '') line.setAttribute('id', id)
line.setAttribute('from', sp[0] + ',' + sp[1])
line.setAttribute('to', ep[0] + ',' + ep[1])
hi.setAttribute('visibility', 'hidden')
hi.setAttribute('stroke-width', 9)
hi.setAttribute('fill', 'none')
hi.setAttribute('stroke', 'white')
hi.setAttribute(
'd',
'M ' + sp[0] + ' ' + sp[1] + ' L ' + ep[0] + ' ' + ep[1]
)
hi.setAttribute('pointer-events', 'stroke')
path.setAttribute(
'd',
'M ' + sp[0] + ' ' + sp[1] + ' L ' + ep[0] + ' ' + ep[1]
)
path.setAttribute('stroke-width', mark ? 2.4 : 1.4)
path.setAttribute('stroke-linecap', 'round')
path.setAttribute('fill', 'none')
if (dash) path.setAttribute('style', 'stroke-dasharray:6,5')
if (mark) {
path.setAttribute('stroke', GooFlow.prototype.color.mark || '#ff8800')
path.setAttribute('marker-end', 'url(#arrow2)')
} else {
path.setAttribute('stroke', GooFlow.prototype.color.line || '#3892D3')
path.setAttribute('marker-end', 'url(#arrow1)')
}
line.appendChild(hi)
line.appendChild(path)
line.style.cursor = 'crosshair'
if (id != '' && id != 'GooFlow_tmp_line') {
var text = document.createElementNS(
'http://www.w3.org/2000/svg',
'text'
)
text.setAttribute('fill', GooFlow.prototype.color.lineFont || '#333')
line.appendChild(text)
var x = (ep[0] + sp[0]) / 2
var y = (ep[1] + sp[1]) / 2
text.setAttribute('text-anchor', 'middle')
text.setAttribute('x', x)
text.setAttribute('y', y)
line.style.cursor = 'pointer'
text.style.cursor = 'text'
}
} else {
line = document.createElement('v:polyline')
if (id != '') line.id = id
// line.style.position="absolute";
line.points.value = sp[0] + ',' + sp[1] + ' ' + ep[0] + ',' + ep[1]
line.setAttribute(
'fromTo',
sp[0] + ',' + sp[1] + ',' + ep[0] + ',' + ep[1]
)
line.strokeWeight = '1.2'
line.stroke.EndArrow = 'Block'
line.style.cursor = 'crosshair'
if (id != '' && id != 'GooFlow_tmp_line') {
var text = document.createElement('div')
// text.innerHTML=id;
line.appendChild(text)
var x = (ep[0] - sp[0]) / 2
var y = (ep[1] - sp[1]) / 2
if (x < 0) x = x * -1
if (y < 0) y = y * -1
text.style.left = x + 'px'
text.style.top = y - 6 + 'px'
line.style.cursor = 'pointer'
}
if (dash) line.stroke.dashstyle = 'Dash'
if (mark) line.strokeColor = GooFlow.prototype.color.mark || '#ff8800'
else line.strokeColor = GooFlow.prototype.color.line || '#3892D3'
line.fillColor = GooFlow.prototype.color.line || '#3892D3'
}
return line
},
// 画一条只有两个中点的折线
drawPoly: function(id, sp, m1, m2, ep, mark) {
var poly, strPath
if (GooFlow.prototype.useSVG != '') {
poly = document.createElementNS('http://www.w3.org/2000/svg', 'g')
var hi = document.createElementNS('http://www.w3.org/2000/svg', 'path')
var path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
if (id != '') poly.setAttribute('id', id)
poly.setAttribute('from', sp[0] + ',' + sp[1])
poly.setAttribute('to', ep[0] + ',' + ep[1])
hi.setAttribute('visibility', 'hidden')
hi.setAttribute('stroke-width', 9)
hi.setAttribute('fill', 'none')
hi.setAttribute('stroke', 'white')
strPath = 'M ' + sp[0] + ' ' + sp[1]
if (m1[0] != sp[0] || m1[1] != sp[1])
strPath += ' L ' + m1[0] + ' ' + m1[1]
if (m2[0] != ep[0] || m2[1] != ep[1])
strPath += ' L ' + m2[0] + ' ' + m2[1]
strPath += ' L ' + ep[0] + ' ' + ep[1]
hi.setAttribute('d', strPath)
hi.setAttribute('pointer-events', 'stroke')
path.setAttribute('d', strPath)
path.setAttribute('stroke-width', mark ? 2.4 : 1.4)
path.setAttribute('stroke-linecap', 'round')
path.setAttribute('fill', 'none')
if (mark) {
path.setAttribute('stroke', GooFlow.prototype.color.mark || '#ff8800')
path.setAttribute('marker-end', 'url(#arrow2)')
} else {
path.setAttribute('stroke', GooFlow.prototype.color.line || '#3892D3')
path.setAttribute('marker-end', 'url(#arrow1)')
}
poly.appendChild(hi)
poly.appendChild(path)
var text = document.createElementNS('http://www.w3.org/2000/svg', 'text')
text.setAttribute('fill', GooFlow.prototype.color.lineFont || '#333')
poly.appendChild(text)
var x = (m2[0] + m1[0]) / 2
var y = (m2[1] + m1[1]) / 2
text.setAttribute('text-anchor', 'middle')
text.setAttribute('x', x)
text.setAttribute('y', y)
text.style.cursor = 'text'
poly.style.cursor = 'pointer'
} else {
poly = document.createElement('v:Polyline')
if (id != '') poly.id = id
poly.filled = 'false'
strPath = sp[0] + ',' + sp[1]
if (m1[0] != sp[0] || m1[1] != sp[1]) strPath += ' ' + m1[0] + ',' + m1[1]
if (m2[0] != ep[0] || m2[1] != ep[1]) strPath += ' ' + m2[0] + ',' + m2[1]
strPath += ' ' + ep[0] + ',' + ep[1]
poly.points.value = strPath
poly.setAttribute(
'fromTo',
sp[0] + ',' + sp[1] + ',' + ep[0] + ',' + ep[1]
)
poly.strokeWeight = mark ? '2.4' : '1.2'
poly.stroke.EndArrow = 'Block'
var text = document.createElement('div')
// text.innerHTML=id;
poly.appendChild(text)
var x = (m2[0] - m1[0]) / 2
var y = (m2[1] - m1[1]) / 2
if (x < 0) x = x * -1
if (y < 0) y = y * -1
text.style.left = x + 'px'
text.style.top = y - 4 + 'px'
poly.style.cursor = 'pointer'
if (mark) poly.strokeColor = GooFlow.prototype.color.mark || '#ff8800'
else poly.strokeColor = GooFlow.prototype.color.line || '#3892D3'
}
return poly
},
// 计算两个结点间要连直线的话,连线的开始坐标和结束坐标
calcStartEnd: function(n1, n2) {
var X_1, Y_1, X_2, Y_2
// X判断:
var x11 = n1.left
var x12 = n1.left + n1.width
var x21 = n2.left
var x22 = n2.left + n2.width
// 结点2在结点1左边
if (x11 >= x22) {
X_1 = x11
X_2 = x22
}
// 结点2在结点1右边
else if (x12 <= x21) {
X_1 = x12
X_2 = x21
}
// 结点2在结点1水平部分重合
else if (x11 <= x21 && x12 >= x21 && x12 <= x22) {
X_1 = (x12 + x21) / 2
X_2 = X_1
} else if (x11 >= x21 && x12 <= x22) {
X_1 = (x11 + x12) / 2
X_2 = X_1
} else if (x21 >= x11 && x22 <= x12) {
X_1 = (x21 + x22) / 2
X_2 = X_1
} else if (x11 <= x22 && x12 >= x22) {
X_1 = (x11 + x22) / 2
X_2 = X_1
}
// Y判断:
var y11 = n1.top
var y12 = n1.top + n1.height
var y21 = n2.top
var y22 = n2.top + n2.height
// 结点2在结点1上边
if (y11 >= y22) {
Y_1 = y11
Y_2 = y22
}
// 结点2在结点1下边
else if (y12 <= y21) {
Y_1 = y12
Y_2 = y21
}
// 结点2在结点1垂直部分重合
else if (y11 <= y21 && y12 >= y21 && y12 <= y22) {
Y_1 = (y12 + y21) / 2
Y_2 = Y_1
} else if (y11 >= y21 && y12 <= y22) {
Y_1 = (y11 + y12) / 2
Y_2 = Y_1
} else if (y21 >= y11 && y22 <= y12) {
Y_1 = (y21 + y22) / 2
Y_2 = Y_1
} else if (y11 <= y22 && y12 >= y22) {
Y_1 = (y11 + y22) / 2
Y_2 = Y_1
}
return { start: [X_1, Y_1], end: [X_2, Y_2] }
},
// 计算两个结点间要连折线的话,连线的所有坐标
calcPolyPoints: function(n1, n2, type, M) {
// 开始/结束两个结点的中心
var SP = { x: n1.left + n1.width / 2, y: n1.top + n1.height / 2 }
var EP = { x: n2.left + n2.width / 2, y: n2.top + n2.height / 2 }
var sp = []
var m1 = []
var m2 = []
var ep = []
// 如果是允许中段可左右移动的折线,则参数M为可移动中段线的X坐标
// 粗略计算起始点
sp = [SP.x, SP.y]
ep = [EP.x, EP.y]
if (type == 'lr') {
// 粗略计算2个中点
m1 = [M, SP.y]
m2 = [M, EP.y]
// 再具体分析修改开始点和中点1
if (m1[0] > n1.left && m1[0] < n1.left + n1.width) {
m1[1] = SP.y > EP.y ? n1.top : n1.top + n1.height
sp[0] = m1[0]
sp[1] = m1[1]
} else {
sp[0] = m1[0] < n1.left ? n1.left : n1.left + n1.width
}
// 再具体分析中点2和结束点
if (m2[0] > n2.left && m2[0] < n2.left + n2.width) {
m2[1] = SP.y > EP.y ? n2.top + n2.height : n2.top
ep[0] = m2[0]
ep[1] = m2[1]
} else {
ep[0] = m2[0] < n2.left ? n2.left : n2.left + n2.width
}
}
// 如果是允许中段可上下移动的折线,则参数M为可移动中段线的Y坐标
else if (type == 'tb') {
// 粗略计算2个中点
m1 = [SP.x, M]
m2 = [EP.x, M]
// 再具体分析修改开始点和中点1
if (m1[1] > n1.top && m1[1] < n1.top + n1.height) {
m1[0] = SP.x > EP.x ? n1.left : n1.left + n1.width
sp[0] = m1[0]
sp[1] = m1[1]
} else {
sp[1] = m1[1] < n1.top ? n1.top : n1.top + n1.height
}
// 再具体分析中点2和结束点
if (m2[1] > n2.top && m2[1] < n2.top + n2.height) {
m2[0] = SP.x > EP.x ? n2.left + n2.width : n2.left
ep[0] = m2[0]
ep[1] = m2[1]
} else {
ep[1] = m2[1] < n2.top ? n2.top : n2.top + n2.height
}
}
return { start: sp, m1: m1, m2: m2, end: ep }
},
// 初始化折线中段的X/Y坐标,mType='rb'时为X坐标,mType='tb'时为Y坐标
getMValue: function(n1, n2, mType) {
if (mType == 'lr') {
return (n1.left + n1.width / 2 + n2.left + n2.width / 2) / 2
} else if (mType == 'tb') {
return (n1.top + n1.height / 2 + n2.top + n2.height / 2) / 2
}
},
// 原lineData已经设定好的情况下,只在绘图工作区画一条线的页面元素
addLineDom: function(id, lineData) {
var n1 = this.$nodeData[lineData.from]
var n2 = this.$nodeData[lineData.to] // 获取开始/结束结点的数据
if (!n1 || !n2) return
// 开始计算线端点坐标
var res
if (lineData.type && lineData.type != 'sl')
res = GooFlow.prototype.calcPolyPoints(n1, n2, lineData.type, lineData.M)
else res = GooFlow.prototype.calcStartEnd(n1, n2)
if (!res) return
if (lineData.type == 'sl')
this.$lineDom[id] = GooFlow.prototype.drawLine(
id,
res.start,
res.end,
lineData.marked
)
else
this.$lineDom[id] = GooFlow.prototype.drawPoly(
id,
res.start,
res.m1,
res.m2,
res.end,
lineData.marked
)
this.$draw.appendChild(this.$lineDom[id])
if (GooFlow.prototype.useSVG == '') {
this.$lineDom[id].childNodes[1].innerHTML = lineData.name
if (lineData.type != 'sl') {
var Min = res.start[0] > res.end[0] ? res.end[0] : res.start[0]
if (Min > res.m2[0]) Min = res.m2[0]
if (Min > res.m1[0]) Min = res.m1[0]
this.$lineDom[id].childNodes[1].style.left =
(res.m2[0] + res.m1[0]) / 2 -
Min -
this.$lineDom[id].childNodes[1].offsetWidth / 2 +
4
Min = res.start[1] > res.end[1] ? res.end[1] : res.start[1]
if (Min > res.m2[1]) Min = res.m2[1]
if (Min > res.m1[1]) Min = res.m1[1]
this.$lineDom[id].childNodes[1].style.top =
(res.m2[1] + res.m1[1]) / 2 -
Min -
this.$lineDom[id].childNodes[1].offsetHeight / 2
} else
this.$lineDom[id].childNodes[1].style.left =
((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) -
this.$lineDom[id].childNodes[1].offsetWidth) /
2 +
4
} else this.$lineDom[id].childNodes[2].textContent = lineData.name
},
// 增加一条线
addLine: function(id, json) {
if (this.onItemAdd != null && !this.onItemAdd(id, 'line', json)) return
if (this.$undoStack && this.$editable) {
this.pushOper('delLine', [id])
}
if (json.from == json.to) return
var n1 = this.$nodeData[json.from]
var n2 = this.$nodeData[json.to] // 获取开始/结束结点的数据
if (!n1 || !n2) return
// 避免两个节点间不能有一条以上同向接连线
for (var k in this.$lineData) {
if (
json.from == this.$lineData[k].from &&
json.to == this.$lineData[k].to
)
return
}
// 设置$lineData[id]
this.$lineData[id] = {}
if (json.type) {
this.$lineData[id].type = json.type
this.$lineData[id].M = json.M
} else this.$lineData[id].type = 'sl' // 默认为直线
this.$lineData[id].from = json.from
this.$lineData[id].to = json.to
this.$lineData[id].name = json.name
if (json.marked) this.$lineData[id].marked = json.marked
else this.$lineData[id].marked = false
// 设置$lineData[id]完毕
this.addLineDom(id, this.$lineData[id])
++this.$lineCount
if (this.$editable) {
this.$lineData[id].alt = true
if (this.$deletedItem[id]) delete this.$deletedItem[id] // 在回退删除操作时,去掉该元素的删除记录
}
},
// 重构所有连向某个结点的线的显示,传参结构为$nodeData数组的一个单元结构
resetLines: function(id, node) {
for (var i in this.$lineData) {
var other = null // 获取结束/开始结点的数据
var res
if (this.$lineData[i].from == id) {
// 找结束点
other = this.$nodeData[this.$lineData[i].to] || null
if (other == null) continue
if (this.$lineData[i].type == 'sl')
res = GooFlow.prototype.calcStartEnd(node, other)
else
res = GooFlow.prototype.calcPolyPoints(
node,
other,
this.$lineData[i].type,
this.$lineData[i].M
)
if (!res) break
} else if (this.$lineData[i].to == id) {
// 找开始点
other = this.$nodeData[this.$lineData[i].from] || null
if (other == null) continue
if (this.$lineData[i].type == 'sl')
res = GooFlow.prototype.calcStartEnd(other, node)
else
res = GooFlow.prototype.calcPolyPoints(
other,
node,
this.$lineData[i].type,
this.$lineData[i].M
)
if (!res) break
}
if (other == null) continue
this.$draw.removeChild(this.$lineDom[i])
if (this.$lineData[i].type == 'sl') {
this.$lineDom[i] = GooFlow.prototype.drawLine(
i,
res.start,
res.end,
this.$lineData[i].marked
)
} else {
this.$lineDom[i] = GooFlow.prototype.drawPoly(
i,
res.start,
res.m1,
res.m2,
res.end,
this.$lineData[i].marked
)
}
this.$draw.appendChild(this.$lineDom[i])
if (GooFlow.prototype.useSVG == '') {
this.$lineDom[i].childNodes[1].innerHTML = this.$lineData[i].name
if (this.$lineData[i].type != 'sl') {
var Min = res.start[0] > res.end[0] ? res.end[0] : res.start[0]
if (Min > res.m2[0]) Min = res.m2[0]
if (Min > res.m1[0]) Min = res.m1[0]
this.$lineDom[i].childNodes[1].style.left =
(res.m2[0] + res.m1[0]) / 2 -
Min -
this.$lineDom[i].childNodes[1].offsetWidth / 2 +
4
Min = res.start[1] > res.end[1] ? res.end[1] : res.start[1]
if (Min > res.m2[1]) Min = res.m2[1]
if (Min > res.m1[1]) Min = res.m1[1]
this.$lineDom[i].childNodes[1].style.top =
(res.m2[1] + res.m1[1]) / 2 -
Min -
this.$lineDom[i].childNodes[1].offsetHeight / 2 -
4
} else
this.$lineDom[i].childNodes[1].style.left =
((res.end[0] - res.start[0]) *
(res.end[0] > res.start[0] ? 1 : -1) -
this.$lineDom[i].childNodes[1].offsetWidth) /
2 +
4
} else this.$lineDom[i].childNodes[2].textContent = this.$lineData[i].name
}
},
// 重新设置连线的样式 newType= "sl":直线, "lr":中段可左右移动型折线, "tb":中段可上下移动型折线
setLineType: function(id, newType, M) {
if (
!newType ||
newType == null ||
newType == '' ||
newType == this.$lineData[id].type
)
return false
if (this.onLineSetType != null && !this.onLineSetType(id, newType)) return
if (this.$undoStack) {
var paras = [id, this.$lineData[id].type, this.$lineData[id].M]
this.pushOper('setLineType', paras)
}
var from = this.$lineData[id].from
var to = this.$lineData[id].to
this.$lineData[id].type = newType
var res
// 如果是变成折线
if (newType != 'sl') {
var res = GooFlow.prototype.calcPolyPoints(
this.$nodeData[from],
this.$nodeData[to],
this.$lineData[id].type,
this.$lineData[id].M
)
if (M) {
this.setLineM(id, M, true)
} else {
this.setLineM(
id,
this.getMValue(this.$nodeData[from], this.$nodeData[to], newType),
true
)
}
}
// 如果是变回直线
else {
delete this.$lineData[id].M
this.$lineMove
.hide()
.removeData('type')
.removeData('tid')
res = GooFlow.prototype.calcStartEnd(
this.$nodeData[from],
this.$nodeData[to]
)
if (!res) return
this.$draw.removeChild(this.$lineDom[id])
this.$lineDom[id] = GooFlow.prototype.drawLine(
id,
res.start,
res.end,
this.$lineData[id].marked
)
this.$draw.appendChild(this.$lineDom[id])
if (GooFlow.prototype.useSVG == '') {
this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name
this.$lineDom[id].childNodes[1].style.left =
((res.end[0] - res.start[0]) * (res.end[0] > res.start[0] ? 1 : -1) -
this.$lineDom[id].childNodes[1].offsetWidth) /
2 +
4
} else
this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name
}
if (this.$focus == id) {
this.focusItem(id)
}
if (this.$editable) {
this.$lineData[id].alt = true
}
},
// 设置折线中段的X坐标值(可左右移动时)或Y坐标值(可上下移动时)
setLineM: function(id, M, noStack) {
if (
!this.$lineData[id] ||
M < 0 ||
!this.$lineData[id].type ||
this.$lineData[id].type == 'sl'
)
return false
if (this.onLineMove != null && !this.onLineMove(id, M)) return false
if (this.$undoStack && !noStack) {
var paras = [id, this.$lineData[id].M]
this.pushOper('setLineM', paras)
}
var from = this.$lineData[id].from
var to = this.$lineData[id].to
this.$lineData[id].M = M
var ps = GooFlow.prototype.calcPolyPoints(
this.$nodeData[from],
this.$nodeData[to],
this.$lineData[id].type,
this.$lineData[id].M
)
this.$draw.removeChild(this.$lineDom[id])
this.$lineDom[id] = GooFlow.prototype.drawPoly(
id,
ps.start,
ps.m1,
ps.m2,
ps.end,
this.$lineData[id].marked
)
this.$draw.appendChild(this.$lineDom[id])
if (GooFlow.prototype.useSVG == '') {
this.$lineDom[id].childNodes[1].innerHTML = this.$lineData[id].name
var Min = ps.start[0] > ps.end[0] ? ps.end[0] : ps.start[0]
if (Min > ps.m2[0]) Min = ps.m2[0]
if (Min > ps.m1[0]) Min = ps.m1[0]
this.$lineDom[id].childNodes[1].style.left =
(ps.m2[0] + ps.m1[0]) / 2 -
Min -
this.$lineDom[id].childNodes[1].offsetWidth / 2 +
4
Min = ps.start[1] > ps.end[1] ? ps.end[1] : ps.start[1]
if (Min > ps.m2[1]) Min = ps.m2[1]
if (Min > ps.m1[1]) Min = ps.m1[1]
this.$lineDom[id].childNodes[1].style.top =
(ps.m2[1] + ps.m1[1]) / 2 -
Min -
this.$lineDom[id].childNodes[1].offsetHeight / 2 -
4
} else this.$lineDom[id].childNodes[2].textContent = this.$lineData[id].name
if (this.$editable) {
this.$lineData[id].alt = true
}
},
// 删除转换线
delLine: function(id, trigger) {
if (!this.$lineData[id]) return
if (
trigger != false &&
this.onItemDel != null &&
!this.onItemDel(id, 'node')
)
return
if (this.$undoStack) {
var paras = [id, this.$lineData[id]]
this.pushOper('addLine', paras)
}
this.$draw.removeChild(this.$lineDom[id])
delete this.$lineData[id]
delete this.$lineDom[id]
if (this.$focus == id) this.$focus = ''
--this.$lineCount
if (this.$editable) {
// 在回退新增操作时,如果节点ID以this.$id+"_line_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
if (id.indexOf(this.$id + '_line_') < 0) this.$deletedItem[id] = 'line'
this.$mpFrom.hide().removeData('p')
this.$mpTo.hide().removeData('p')
}
if (this.$lineOper) {
this.$lineOper.hide().removeData('tid')
}
},
// 变更连线两个端点所连的结点
// 参数:要变更端点的连线ID,新的开始结点ID、新的结束结点ID;如果开始/结束结点ID是传入null或者"",则表示原端点不变
moveLinePoints: function(lineId, newStart, newEnd, noStack) {
if (newStart == newEnd) return
if (!lineId || !this.$lineData[lineId]) return
if (newStart == null || newStart == '')
newStart = this.$lineData[lineId].from
if (newEnd == null || newEnd == '') newEnd = this.$lineData[lineId].to
// 避免两个节点间不能有一条以上同向接连线
for (var k in this.$lineData) {
if (newStart == this.$lineData[k].from && newEnd == this.$lineData[k].to)
return
}
if (
this.onLinePointMove != null &&
!this.onLinePointMove(id, newStart, newEnd)
)
return
if (this.$undoStack && !noStack) {
var paras = [
lineId,
this.$lineData[lineId].from,
this.$lineData[lineId].to
]
this.pushOper('moveLinePoints', paras)
}
if (newStart != null && newStart != '') {
this.$lineData[lineId].from = newStart
}
if (newEnd != null && newEnd != '') {
this.$lineData[lineId].to = newEnd
}
// 重建转换线
this.$draw.removeChild(this.$lineDom[lineId])
this.addLineDom(lineId, this.$lineData[lineId])
if (this.$editable) {
this.$lineData[lineId].alt = true
}
},
// 用颜色标注/取消标注一个结点或转换线,常用于显示重点或流程的进度。
// 这是一个在编辑模式中无用,但是在纯浏览模式中非常有用的方法,实际运用中可用于跟踪流程的进度。
markItem: function(id, type, mark) {
if (type == 'node') {
if (!this.$nodeData[id]) return
if (this.onItemMark != null && !this.onItemMark(id, 'node', mark)) return
this.$nodeData[id].marked = mark || false
if (mark) {
this.$nodeDom[id]
.addClass('item_mark')
.css('border-color', GooFlow.prototype.color.mark)
} else {
this.$nodeDom[id].removeClass('item_mark')
if (id != this.$focus)
this.$nodeDom[id].css('border-color', 'transparent')
}
} else if (type == 'line') {
if (!this.$lineData[id]) return
if (this.onItemMark != null && !this.onItemMark(id, 'line', mark)) return
this.$lineData[id].marked = mark || false
if (GooFlow.prototype.useSVG != '') {
if (mark) {
this.$lineDom[id].childNodes[1].setAttribute(
'stroke',
GooFlow.prototype.color.mark || '#ff8800'
)
this.$lineDom[id].childNodes[1].setAttribute(
'marker-end',
'url(#arrow2)'
)
this.$lineDom[id].childNodes[1].setAttribute('stroke-width', 2.4)
} else {
this.$lineDom[id].childNodes[1].setAttribute(
'stroke',
GooFlow.prototype.color.line || '#3892D3'
)
this.$lineDom[id].childNodes[1].setAttribute(
'marker-end',
'url(#arrow1)'
)
this.$lineDom[id].childNodes[1].setAttribute('stroke-width', 1.4)
}
} else {
if (mark) {
this.$lineDom[id].strokeColor =
GooFlow.prototype.color.mark || '#ff8800'
this.$lineDom[id].strokeWeight = '2.4'
} else {
this.$lineDom[id].strokeColor =
GooFlow.prototype.color.line || '#3892D3'
this.$lineDom[id].strokeWeight = '1.2'
}
}
}
if (this.$undoStatck) {
var paras = [id, type, !mark]
this.pushOper('markItem', paras)
}
},
/// /////////////////////以下为区域分组块操作
moveArea: function(id, left, top) {
if (!this.$areaData[id]) return
if (this.onItemMove != null && !this.onItemMove(id, 'area', left, top))
return
if (this.$undoStack) {
var paras = [id, this.$areaData[id].left, this.$areaData[id].top]
this.pushOper('moveNode', paras)
}
if (left < 0) left = 0
if (top < 0) top = 0
$('#' + id).css({ left: left + 'px', top: top + 'px' })
this.$areaData[id].left = left
this.$areaData[id].top = top
if (this.$editable) {
this.$areaData[id].alt = true
}
},
// 删除区域分组
delArea: function(id, trigger) {
if (!this.$areaData[id]) return
if (this.$undoStack) {
var paras = [id, this.$areaData[id]]
this.pushOper('addArea', paras)
}
if (
trigger != false &&
this.onItemDel != null &&
!this.onItemDel(id, 'node')
)
return
delete this.$areaData[id]
this.$areaDom[id].remove()
delete this.$areaDom[id]
--this.$areaCount
if (this.$editable) {
// 在回退新增操作时,如果节点ID以this.$id+"_area_"开头,则表示为本次编辑时新加入的节点,这些节点的删除不用加入到$deletedItem中
if (id.indexOf(this.$id + '_area_') < 0) this.$deletedItem[id] = 'area'
}
},
// 设置区域分组的颜色
setAreaColor: function(id, color) {
if (!this.$areaData[id]) return
if (this.$undoStack) {
var paras = [id, this.$areaData[id].color]
this.pushOper('setAreaColor', paras)
}
if (
color == 'red' ||
color == 'yellow' ||
color == 'blue' ||
color == 'green'
) {
this.$areaDom[id]
.removeClass('area_' + this.$areaData[id].color)
.addClass('area_' + color)
this.$areaData[id].color = color
}
if (this.$editable) {
this.$areaData[id].alt = true
}
},
// 设置区域分块的尺寸
resizeArea: function(id, width, height) {
if (!this.$areaData[id]) return
if (
this.onItemResize != null &&
!this.onItemResize(id, 'area', width, height)
)
return
if (this.$undoStack) {
var paras = [id, this.$areaData[id].width, this.$areaData[id].height]
this.pushOper('resizeArea', paras)
}
this.$areaDom[id]
.children('.bg')
.css({ width: width + 'px', height: height + 'px' })
// width=this.$areaDom[id].outerWidth();
// height=this.$areaDom[id].outerHeight();
// this.$areaDom[id].children("bg").css({width:width+"px",height:height+"px"});
this.$areaData[id].width = width
this.$areaData[id].height = height
if (this.$editable) {
this.$areaData[id].alt = true
}
},
addArea: function(id, json) {
if (this.onItemAdd != null && !this.onItemAdd(id, 'area', json)) return
if (this.$undoStack && this.$editable) {
this.pushOper('delArea', [id])
}
this.$areaDom[id] = $(
""
)
this.$areaData[id] = json
this.$group.append(this.$areaDom[id])
if (this.$nowType != 'group')
this.$areaDom[id].children('div:eq(1)').css('display', 'none')
++this.$areaCount
if (this.$editable) {
this.$areaData[id].alt = true
if (this.$deletedItem[id]) delete this.$deletedItem[id] // 在回退删除操作时,去掉该元素的删除记录
}
},
// 重构整个流程图设计器的宽高
reinitSize: function(width, height) {
var w = width || 800
var h = height || 500
this.$bgDiv.css({ height: h + 'px', width: w + 'px' })
var headHeight = 0
var hack = 8
if (this.$head != null) {
headHeight = 26
hack = 5
}
if (this.$tool != null) {
this.$tool.css({ height: h - headHeight - hack + 'px' })
w -= 31
}
w -= 9
h = h - headHeight - (this.$head != null ? 5 : 8)
this.$workArea.parent().css({ height: h + 'px', width: w + 'px' })
if (this.$workArea.width() > w) {
w = this.$workArea.width()
}
if (this.$workArea.height() > h) {
h = this.$workArea.height()
}
this.$workArea.css({ height: h + 'px', width: w + 'px' })
if (GooFlow.prototype.useSVG == '') {
this.$draw.coordsize = w + ',' + h
}
this.$draw.style.width = w + 'px'
this.$draw.style.height = +h + 'px'
if (this.$group != null) {
this.$group.css({ height: h + 'px', width: w + 'px' })
}
}
}
GooFlow.prototype.color = {}
// 将此类的构造函数加入至JQUERY对象中
jQuery.extend({
createGooFlow: function(bgDiv, property) {
return new GooFlow(bgDiv, property)
}
})