import StartNode from "./StartNode";
import AutoNode from "./AutoNode";
import ManualNode from "./ManualNode";
import Relation from "./Relation";
import CompleteNode from "./CompleteNode";
import Node from "./Node";
import PaintElement from "./PaintElement";
import SubFlow from "./SubFlow";
import GatewayNode from "./GatewayNode";
import Point from "../utility/Point";
import Resources from "../utility/Resources";
import Graphics from "../utility/Graphics";
import Sequence from "../utility/Sequence";
import FlowType from "../utility/FlowType";
import AuxiliaryLine from "../utility/AuxiliaryLine";

class FlowDiagram {
  constructor(canvas) {
    this.canvas = canvas;
    this.g = new Graphics(canvas.getContext("2d"));
    this._elems = [];
    this._statues = FlowDiagram.ACTION_NORMAL;
    this._currToEdit = null;
    this._changed = false;
    this._drag = false;
    this._selected = null; // **
    this.flowstatus = FlowType.FLOWSTATUS_OPEN_NOSTART;
    this.flowpath = "";
    this.deleteMSG = null;
    this.width = 10000;
    this.height = 1536;
    this._applicationid = null;
    this._sessionid = null;
    this._changed = false;
    this._zoomrate = 1;
    this.win = null;
    this.id = null;
    this.scaleNum = 1;
    this.scalePreNum;
    this.scaleStep = 0.2;
    this.ctrlDown = false; // 有无按下ctrl键
    this.selectArr = []; // 按ctrl键选下的节点值
    this.ctrlEle = null; // 先按下鼠标然后再按下ctrl要记录的元素
  }

  getImageResource(filename) {
    let obj = IMG_RESOURCE.get(filename);
    let img = null;
    if (obj == null) {
      try {
        let icon = new ImageIcon(BFApplet.class.getResource(filename));
        obj = icon.getImage();
      } catch (e) {
        obj = Toolkit.getDefaultToolkit().createImage(new byte[0]());
      }
      IMG_RESOURCE.put(filename, obj);
    }
    img = obj;
    return img;
  }

  setJSObject(win) {
    this.win = win;
  }

  get_statues() {
    return this._statues;
  }

  getLocationWithCanvas(canvas, x, y) {
    var bbox = canvas.getBoundingClientRect();
    return {
      x: (x - bbox.left) * (canvas.width / bbox.width),
      y: (y - bbox.top) * (canvas.height / bbox.height),
    };
  }

  // 编辑时用到的接口
  getCurrToEdit() {
    return this._currToEdit;
  }

  setCurrToEdit(_currToEdit) {
    this._currToEdit = _currToEdit;
  }

  getChanged() {
    return this._changed;
  }

  getScaleNum(option) {
    if (option == "big") {
      this.scaleNum += this.scaleStep;
    } else {
      if ((this.scaleNum).toFixed(1) == 0.2) {
        alert('已缩放到最小值')
      } else {
        this.scaleNum -= this.scaleStep;
      }
    }
    return (this.scaleNum).toFixed(1);
  }

  editManualNode(grp, opts) {
    console.log(grp, opts);

    grp.id = opts.id || Sequence.getSequence() + "";
    grp.name = opts.name;
    grp.scale = opts.scale;
    grp.note = opts.note;
    grp.x = opts.x;
    grp.y = opts.y;
    grp.width = opts.width;
    grp.height = opts.height;
    grp.m_width = opts.m_width;
    grp.m_height = opts.m_height;
    grp.prenodeid = opts.prenodeid;
    grp.statelabel = opts.statelabel;
    grp.orderNum = opts.orderNum;
    grp.backnodeid = opts.backnodeid;
    grp.formname = opts.formname;
    grp.fieldpermlist = opts.fieldpermlist;
    grp.isstartandnext = opts.isstartandnext;
    grp._iscurrent = opts._iscurrent;
    grp.actorListScript = opts.actorListScript;

    grp.jumpNameScript = opts.jumpNameScript;
    grp.jump = opts.jump;
    grp.jumpTo = opts.jumpTo;

    grp.actorEditMode = opts.actorEditMode;
    grp.deptlist = opts.deptlist;
    grp.namelist = opts.namelist;
    grp.realnamelist = opts.realnamelist;
    grp.passcondition = opts.passcondition;
    grp.approverNumType = opts.approverNumType;

    grp.isApproverEdit = opts.isApproverEdit;
    grp.approverEditScript = opts.approverEditScript;
		grp.isCoApproverEdit = opts.isCoApproverEdit;//彩生活
		grp.coApproverEditScript = opts.coApproverEditScript;//彩生活
    grp.isSupplementComments = opts.isSupplementComments;
    grp.exceedaction = opts.exceedaction;
    grp.issetcurruser = opts.issetcurruser;
    grp.inputform = opts.inputform;
    grp.isgather = opts.isgather;
    grp.issplit = opts.issplit;
    grp.cBack = opts.cBack;
    grp.splitStartNode = opts.splitStartNode;
    grp.isFrontEdit = opts.isFrontEdit;
    grp.backType = opts.backType;
    grp.isToPerson = opts.isToPerson;
    grp.checkedOnSinglePerson = opts.checkedOnSinglePerson;
		grp.checkedOnMultiplePerson = opts.checkedOnMultiplePerson;


    grp.bnodelist = opts.bnodelist;
    grp.retracementEditMode = opts.retracementEditMode;
    grp.cRetracement = opts.cRetracement;
    grp.retracementScript = opts.retracementScript;
    grp.notificationStrategyJSON = opts.notificationStrategyJSON;
    grp.isCarbonCopy = opts.isCarbonCopy;
    grp.isSelectCirculator = opts.isSelectCirculator;
    grp.circulatorEditMode = opts.circulatorEditMode;
    grp.circulatorListScript = opts.circulatorListScript;
    grp.circulatorNamelist = opts.circulatorNamelist;
    grp.userList = opts.userList;
    grp.circulatorNamelistByUser = opts.circulatorNamelistByUser;
    grp.orgField = opts.orgField;
    grp.orgScope = opts.orgScope;
    grp.orgRoleCondition = opts.orgRoleCondition;
    grp.isLimited = opts.isLimited;
    grp.timeLimitEditMode = opts.timeLimitEditMode;
    grp.timeLimitDay = opts.timeLimitDay;
    grp.timeLimitHour = opts.timeLimitHour;
    grp.timeLimitMinute = opts.timeLimitMinute;
    grp.timeLimitScript = opts.timeLimitScript;
    grp.isUsbKeyVerify = opts.isUsbKeyVerify;
    grp.roleCondition = opts.roleCondition;
    grp.handupEditMode = opts.handupEditMode;
    grp.isHandup = opts.isHandup;
    grp.handupScript = opts.handupScript;
    grp.isAllowEditAuditor = opts.isAllowEditAuditor;
    grp.allowEditAuditorScript = opts.allowEditAuditorScript;
    grp.isAllowTermination = opts.isAllowTermination;
    grp.activityPermList = opts.activityPermList;
    grp.urge2ApprovalEditMode = opts.urge2ApprovalEditMode;
    grp.allowUrge2Approval = opts.allowUrge2Approval;
    grp.allowUrge2ApprovalScript = opts.allowUrge2ApprovalScript;
    grp.isAllowSkip = opts.isAllowSkip;
    grp.nextNodeCheckedStatus = opts.nextNodeCheckedStatus;

    //彩生活
    grp.isAssist = opts.isAssist; //是否开启协办
    grp.isSelectAssistUser = opts.isSelectAssistUser; //允许上一步流程处理人指定本节点的协办人
    grp.assistEditMode = opts.assistEditMode; //指定方式
    grp.assistListScript = opts.assistListScript; //按脚本指定范围
    grp.assistNamelist = opts.assistNamelist; //按角色指定范围
    grp.assistNamelistByUser = opts.assistNamelistByUser;

    this._selected = grp;
  }

  // the StartNode edit Process method

  editStartNode(sn, id, name, statelabel, x, y) {
    sn.id = id;
    sn.name = name;
    sn.statelabel = statelabel;
    sn.x = x;
    sn.y = y;

    this._selected = sn;
  }

  editCompleteNode(sn, id, name, statelabel, orderNum, x, y, isgather, splitStartNode, isAutoArchive) {
    sn.id = id;
    sn.name = name;
    sn.statelabel = statelabel;
    sn.orderNum = orderNum;
    sn.x = x;
    sn.y = y;
    sn.isgather = isgather;
    sn.splitStartNode = splitStartNode;
    sn.isAutoArchive = isAutoArchive;

    this._selected = sn;
  }

  // yx 编辑网关节点
  editGatewayNode(sn, id, name, statelabel, x, y, issplit, isgather, splitStartNode) {
    sn.id = id;
    sn.name = name;
    sn.statelabel = statelabel;
    sn.x = x;
    sn.y = y;
    sn.isgather = isgather;
    sn.issplit = issplit;
    sn.splitStartNode = splitStartNode;
    this._selected = sn;
  }

  editSuspendNode(sn, id, name, statelabel, x, y) {
    sn.id = id;
    sn.name = name;
    sn.statelabel = statelabel;
    sn.x = x;
    sn.y = y;

    this._selected = sn;
  }

  // the AutoNode class process method

  editAutoNode(an, id, name, statelabel, orderNum, issplit, isgather, autoAuditType, delayDay, delayHour, delayMinute, auditDateTime, x, y, splitStartNode, autoAuditTimeEditMode, auditDateTimeScript) {
    an.id = id;
    an.name = name;
    an.statelabel = statelabel;
    an.orderNum = orderNum;
    an.auditDateTime = auditDateTime;
    an.isgather = isgather;
    an.issplit = issplit;
    an.autoAuditType = autoAuditType;
    an.delayDay = delayDay;
    an.delayHour = delayHour;
    an.delayMinute = delayMinute;
    an.x = x;
    an.y = y;
    an.splitStartNode = splitStartNode;
    an.autoAuditTimeEditMode = autoAuditTimeEditMode;
    an.auditDateTimeScript = auditDateTimeScript;

    this._selected = an;
  }

  editSubFlow(an, opts) {
    an.subFlowDefiType = opts.subFlowDefiType;
    an.subflowid = opts.subflowid;
    an.subflowname = opts.subflowname;
    an.subflowScript = opts.subflowScript;
    an.paramPassingType = opts.paramPassingType;
    an.parentFlowFormId = opts.parentFlowFormId;
    an.parentFlowFormName = opts.parentFlowFormName;
    an.subFlowFormId = opts.subFlowFormId;
    an.subFlowFormName = opts.subFlowFormName;
    an.fieldMappingXML = opts.fieldMappingXML;
    an.shareDocument = opts.shareDocument;
    an.paramPassingScript = opts.paramPassingScript;
    an.numberSetingType = opts.numberSetingType;
    an.numberSetingContent = opts.numberSetingContent;
    an.callback = opts.callback;
    an.callbackScript = opts.callbackScript;
    an.iscurrent = opts.iscurrent;
    an.ispassed = opts.ispassed;
    an.crossform = opts.crossform;
    an.startupScript = opts.startupScript;
    an.isgather = opts.isgather;
    an.splitStartNode = opts.splitStartNode;
    an.issplit = opts.issplit;
    an.isToPerson = opts.isToPerson;
    an.checkedOnSinglePerson = opts.checkedOnSinglePerson;
		an.checkedOnMultiplePerson = opts.checkedOnMultiplePerson;


    an.id = opts.id;
    an.name = opts.name;
    an.scale = opts.scale;
    an.note = opts.note;
    an.x = opts.x;
    an.y = opts.y;
    an.width = opts.width;
    an.height = opts.height;
    an.m_width = opts.m_width;
    an.m_height = opts.m_height;
    an.prenodeid = opts.prenodeid;
    an.statelabel = opts.statelabel;
    an.orderNum = opts.orderNum;
    an.backnodeid = opts.backnodeid;
    an.formname = opts.formname;
    an.fieldpermlist = opts.fieldpermlist;
    an.isstartandnext = opts.isstartandnext;
    an._iscurrent = opts._iscurrent;

    this._selected = an;
  }

  /**
   * @param id
   * @param name
   * @param note
   * @roseuid 3E0406A90239
   */
  editRelation(rlt, id, name, condition, note, action, validateScript, filtercondition, editMode, processDescription, formlist) {
    rlt.id = id;
    rlt.name = name;
    rlt.condition = condition;
    rlt.note = note;
    rlt.action = action;
    rlt.validateScript = validateScript;
    rlt.filtercondition = filtercondition;
    rlt.editMode = editMode;
    rlt.processDescription = processDescription;
    rlt.formlist = formlist;
    this._selected = rlt;
  }

  isCurrentSelected(em) {
    if (em != null && this._selected != null && Object.is(em, this._selected)) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * yx 选中的数组中有无当前的元素
   */
  isInZoom(em) {
    if (em) {
      // 在
      for (let i = 0; i < this.selectArr.length; i++) {
        if (Object.is(em.id, this.selectArr[i].id))
          return true;
      }
      return false
    } else {
      return false;
    }
  }

  /**
   * yx 看selectArr中有无数据
   */
  checkSelectArr(){
    if(this.selectArr.length > 0)
      return true
    else
      return false
  }

  isCurrentToEdit(em) {
    if (em != null && this._currToEdit != null && Object.is(em, this._currToEdit)) {
      return true;
    } else {
      return false;
    }
  }

  //全部分散节点
  getAllSplitNode (currentNode) {
    let arrayColleaction = [];
    let relation = [], node = [], nodeRelation = {};
    let obja = {
      name: "---select---",
      id: ""
    };
    arrayColleaction.push(obja);

    for (let i = 0; i < this._elems.length; i++) {
      let element = this._elems[i];
      //  console.log(element.hasSomeRelation)
      if (!(element instanceof Relation)) {
        if (element instanceof ManualNode) {
          let manualNode = element;
          if (manualNode.issplit) {
            // if (id != manualNode.id) {
            let obj = {
              name: element.name,
              id: element.id
            };
            arrayColleaction.push(obj);
            // }
          }
        } else if (element instanceof AutoNode) {
          let autoNode = element;
          if (autoNode.issplit) {
            // if (id != autoNode.id) {
            var obj1 = {
              name: element.name,
              id: element.id
            };
            arrayColleaction.push(obj1);
            // }
          }
        } else if (element instanceof SubFlow) {
          var subFlow = element;
          if (subFlow.issplit) {
            // if (id != subFlow.id) {
            var obj2 = {
              name: element.name,
              id: element.id
            };
            arrayColleaction.push(obj2);
            // }
          }
        }
      } else {
        relation.push(element)
      }

    }

    if(!(currentNode instanceof Relation)){
      nodeRelation = currentNode.hasSomeRelation(relation)
    }
    return { arrayColleaction: arrayColleaction, ...nodeRelation };
  }

  /*
   * 获取指定结点前的所有结点（踢除指定结点）
   */
  getAllBeforeNode(node, ispassed) {
    let all = this.getAllNodeBeforeNode(null, node, ispassed);
    if (this.isContain(all, node)) {
      all = this._removeElementFromArray(all, node);
    }
    return all;
  }

  /*
   * 获取指定结点前的所有结点
   */
  getAllNodeBeforeNode(allnode, node, ispassed) {
    if (allnode == null) {
      allnode = [];
    }
    if (node == null || node instanceof StartNode) {
      return allnode;
    }

    let allrelation = this.getNodeBeforeRelation(node, ispassed);

    for (let i = 0; i < allrelation.length; i++) {
      let item = allrelation[i];
      if (item instanceof Relation) {
        let r = item;
        let beforeNode = this.getStartNode(r);

        if (!this.isContain(allnode, beforeNode)) {
          if (beforeNode instanceof ManualNode) {
            let tmp = beforeNode;
            if (tmp.namelist.indexOf("*") == -1) {
              allnode.push(beforeNode);
            }
          }
          allnode = this.getAllNodeBeforeNode(allnode, beforeNode, ispassed);
        }
      }
    }

    return allnode;
  }

  isContain(all, beforeNode) {
    if (all != null) {
      for (let i = 0; i < all.length; i++) {
        let item = all[i];
        if (item instanceof Node) {
          let n = item;
          if (n.id == beforeNode.id) {
            return true;
          }
        }
      }
    }
    return false;
  }

  addManualNode(name, statelabel, orderNum, x, y) {
    const grp = new ManualNode(this);
    const opts = {
      id: Sequence.getSequence() + "",
      name: "",
      scale: 0,
      note: "",
      x,
      y,
      width: 75,
      height: 80,
      m_width: 50,
      m_height: 50,
      prenodeid: "",
      statelabel,
      orderNum: orderNum,
      backnodeid: "",
      formname: "",
      fieldpermlist: "",
      isstartandnext: false,
      _iscurrent: false,
      actorListScript: "",
      actorEditMode: 0,
      namelist: "",
      deptlist: "",
      realnamelist: "",
      approverNumType: 0,
      passcondition: "",
      isApproverEdit: false,
      approverEditScript: '',
			isCoApproverEdit: false,//彩生活
			coApproverEditScript: '',
      isSupplementComments: false,
      exceedaction: "",
      issetcurruser: false,
      inputform: "",
      isgather: false,
      issplit: false,
      cBack: true,
      splitStartNode: "",
      isFrontEdit: false,
      backType: 0,
      isToPerson: false,
      checkedOnSinglePerson: false,
			checkedOnMultiplePerson:false,


      bnodelist: "",
      retracementEditMode: 0,
      cRetracement: false,
      retracementScript: "",
      notificationStrategyJSON: "",
      isCarbonCopy: false,
      isSelectCirculator: false,
      circulatorEditMode: 0,
      circulatorListScript: "",
      circulatorNamelist: "",
      userList: "",
      circulatorNamelistByUser: "",
      orgField: "",
      orgScope: "self",
      orgRoleCondition: "",
      isLimited: false,
      timeLimitEditMode: 0,
      timeLimitDay: "",
      timeLimitHour: "",
      timeLimitMinute: "",
      timeLimitScript: "",
      isUsbKeyVerify: false,
      roleCondition: "",
      handupEditMode: 0,
      isHandup: false,
      handupScript: "",
      isAllowEditAuditor: false,
      allowEditAuditorScript: '',
      isAllowTermination: false,
      activityPermList: "",
      urge2ApprovalEditMode: 0,
      allowUrge2Approval: false,
      allowUrge2ApprovalScript: "",
      isAllowSkip: false,
      nextNodeCheckedStatus: 0,
      //彩生活
      isAssist: false, //是否开启协办
      isSelectAssistUser: false, //允许上一步流程处理人指定本节点的协办人
      assistEditMode: 0, //指定方式
      assistListScript: "", //按脚本指定范围
      assistNamelist: "", //按角色指定范围
      assistNamelistByUser: "",
    };

    this.editManualNode(grp, opts);

    this._elems.push(grp);
    return grp;
  }

  createManualNode() {
    let mn = new ManualNode(this);
    return mn;
  }

  // the StartNode add process method

  addStartNode(name, statelabel, x, y) {
    let cn = new StartNode(this);
    this.editStartNode(cn, Sequence.getSequence(), name, statelabel, x, y);
    this._elems.push(cn);
    return cn;
  }

  addCompleteNode(name, statelabel, orderNum, x, y) {
    let cn = new CompleteNode(this);
    this.editCompleteNode(cn, Sequence.getSequence(), name, statelabel, orderNum, x, y, false, "", false);
    this._elems.push(cn);
    return cn;
  }
  // yx
  addGatewayNode(name, statelabel, x, y) {
    let cn = new GatewayNode(this);
    this.editGatewayNode(cn, Sequence.getSequence(), name, statelabel, x, y, false, false, "");
    this._elems.push(cn);
    return cn;
  }

  addSuspendNode(name, statelabel, x, y) {
    let cn = new SuspendNode(this);
    this.editSuspendNode(cn, Sequence.getSequence(), name, statelabel, x, y);
    this._elems.push(cn);
    return cn;
  }

  // the AutoNode process method
  addAutoNode(name, statelabel, orderNum, x, y) {
    let an = new AutoNode(this);
    this.editAutoNode(an, Sequence.getSequence(), name, statelabel, orderNum, false, false, 1, "", "", "", "", x, y, "", 1, "");
    this._elems.push(an);
    return an;
  }

  /**
   * 添加子流程节点
   *
   * @param name
   * @param statelabel
   * @param x
   * @param y
   * @return
   */
  addSubFlow(name, statelabel, orderNum, x, y) {
    const an = new SubFlow(this);
    const opts = {
      subFlowDefiType: "01",
      subflowid: "",
      subflowname: "",
      subflowScript: "",
      paramPassingType: "01",
      parentFlowFormId: "",
      parentFlowFormName: "",
      subFlowFormId: "",
      subFlowFormName: "",
      fieldMappingXML: "",
      shareDocument: false,
      paramPassingScript: "",
      numberSetingType: "01",
      numberSetingContent: "1",
      callback: false,
      callbackScript: "",
      iscurrent: false,
      ispassed: false,
      crossform: false,
      startupScript: "",
      isgather: false,
      splitStartNode: "",
      issplit: true,
      isToPerson: false,
      checkedOnSinglePerson: false,
			checkedOnMultiplePerson:false,

      id: Sequence.getSequence() + "",
      name: name,
      scale: 0,
      note: "",
      x: x,
      y: y,
      width: 75,
      height: 80,
      m_width: 20,
      m_height: 20,
      prenodeid: "",
      statelabel: statelabel,
      orderNum: orderNum,
      backnodeid: "",
      formname: "",
      fieldpermlist: "",
      isstartandnext: false,
      _iscurrent: false,
    };

    this.editSubFlow(an, opts);
    this._elems.push(an);
    return an;
  }

  addRelation(name, condition, note, action, validateScript, filtercondition, editMode, processDescription) {
    let rlt = new Relation(this);
    this.editRelation(rlt, Sequence.getSequence(), name, condition, note, action, validateScript, filtercondition, editMode, processDescription);
    this._elems.push(rlt);
  }

  /**
   * @param id
   * @roseuid 3E0406950172
   */
  delActor(id) {
    delElement(id);
  }

  /**
   * @param id
   * @roseuid 3E0406B003D4
   */
  delRelation(id) {
    delElement(id);
  }

  /**
   * 改变鼠标状态
   *
   * @param statues
   * @roseuid 3E0A6E1A0258
   */
  changeStatues(statues) {
    this._statues = statues;
    switch (this._statues) {
      case FlowDiagram.ACTION_NORMAL:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_REMOVE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_ABORTNODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_AUTONODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_COMPLETENODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_MANUALNODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_STARTNODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_GATEWAYNODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_SUSPENDNODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_TERMINATENODE:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_ADD_SUBFLOW:
        this.canvas.style.cursor = "pointer";
        break;
      case FlowDiagram.ACTION_BREAK_LINE: // add by gusd
        this.canvas.style.cursor = "pointer";
        break; // end

      default:
        this.canvas.style.cursor = "initial";
    }
  }

  zoomIn() {
    if (_zoomrate * 0.9 < 0.5) {
      return;
    }

    _zoomrate *= 0.9;

    this.getGraphics().clearRect(0, 0, width, height);

    this.setSize(int(width / _zoomrate), int(height / _zoomrate));
  }

  zoomOut() {
    if (_zoomrate / 0.9 > 2) {
      return;
    }
    _zoomrate /= 0.9;

    this.getGraphics().clearRect(0, 0, width, height);

    this.setSize(int(width / _zoomrate), int(height / _zoomrate));
  }

  /**
   * @param id
   * @roseuid 3E0A6E1A03DF
   */
  delElement(id) {
    for (let e = _elems.elements(); e.hasMoreElements(); ) {
      let em = e.nextElement();
      if (em.id != null && em.id.equals(id)) {
        _elems.removeElement(em);
      }
    }
  }

  repaint() {
    this.paint();
  }

  paintTo(og, fillBackground) {
    // 清除背景
    if (fillBackground) {
      og.setColor(Resources.COLOR.white);
      og.setFont(PaintElement.DEF_FONT);

      og.fillRect(0, 0, this.width/ this.scaleNum, this.height / this.scaleNum);

      // 画背景网格
      og.setColor(Resources.COLOR.lightGray);

      for (let i = 0; i < this.width / this.scaleNum / 50; i++) {
        og.drawLine(i * 50, 0, i * 50, this.height/ this.scaleNum);
      }

      for (let i = 0; i < this.width / this.scaleNum / 5; i++) {
        og.drawLine(i * 5, 0, i * 5, 5);
      }

      for (let i = 0; i < this.width / this.scaleNum / 25; i++) {
        og.drawLine(i * 25, 0, i * 25, 10);
      }

      for (let i = 0; i < this.width / this.scaleNum / 50; i++) {
        og.drawLine(0, i * 50, this.width/ this.scaleNum, i * 50);
      }

      for (let i = 0; i < this.width / this.scaleNum / 5; i++) {
        og.drawLine(0, i * 5, 5, i * 5);
      }

      for (let i = 0; i < this.width / this.scaleNum / 25; i++) {
        og.drawLine(0, i * 25, 10, i * 25);
      }
    } else {
      og.setColor(Resources.COLOR.lightGray);
      og.fillRect(0, 0, this.width / this.scaleNum, this.height / this.scaleNum);
    }

    // 画元素

    this._elems.forEach((elem) => {
      if (elem instanceof PaintElement) {
        //console.log(elem)
        elem.paint(og);
      }
    });

    //绘制辅助线
		if(AuxiliaryLine.getInstance().getProp('points')) {
			AuxiliaryLine.getInstance().paint(og);
		}
  }

  /**
     * yx获取绘画辅助线的点
     * @target 当前画布中被选中的元素，以此元素的顶点作为基准寻找画辅助线的点
     * @return 绘画辅助线的点，找不到时为空
     */
	getDrawAuxiliaryLinePoint(target) {
		let point = {};
		
		if(!target) {
			return false;
		}
		
      for(let i = 0, len = this._elems.length; i < len; i++) {
				if(!(this._elems[i] instanceof Relation)){
          const result = this._elems[i].getElementConnectionPoint(this._elems, target);
          
          if(point.abscissa && point.ordinate) {
            return point;
          }

          if(result && result.middle) {
            point.middle = result.middle;
          }
				
          if(result && result.abscissa && !point.abscissa) {
            point.abscissa = result.abscissa;
          }
              
          if(result && result.ordinate && !point.ordinate) {
            point.ordinate = result.ordinate;
          }
        }			
      }
		
		if(!point.abscissa && !point.ordinate && !point.middle) {
			return null;
		} else {
			return point;
		}
	}

  /**
	 * yx 靠近其他其他元素時自動修改x,y(辅助线)
	 */
	closeEleChange(target){
    console.log(target, 'targettttttttt')
    let tempArr = []
    for(let a = 0; a < this._elems.length; a++){
      if(!(this._elems[a] instanceof Relation)){
        // 剔除原来的元素
        if(target.id == this._elems[a].id){
          continue;
        }else{
          tempArr.push(this._elems[a])
        }
      }
    }
    // 用新的数组判断是否相距5
    for(let b = 0; b < tempArr.length; b++){
      if(Math.abs(target.x - tempArr[b].x) <= 5 && Math.abs(target.y - tempArr[b].y) <= 5){
        target.x = tempArr[b].x;
        target.y = tempArr[b].y
        break;
      }
    }
	}

  getMaxRect() {
    // OGraphics og = new OGraphics();
    // og.setCompressRate(0.5);
    // paintMobile(og);

    // ///////////////
    // 画元素

    let maxRect = new Rectangle(0, 0, 1, 1);
    for (let e = _elems.elements(); e.hasMoreElements(); ) {
      let te = e.nextElement();
      if (te instanceof PaintElement) {
        let em = te;
        maxRect.add(em.getRepaintRect());
      }
    }

    maxRect.setSize(maxRect.width + 50, maxRect.height + 50);

    return maxRect;
  }

  /**
   * @param g
   * @roseuid 3E0A6E1B0065
   */
  paint() {
    this.paintTo(this.g, true);
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0079
   */

  appendElement(e) {
    _elems.push(e);
  }

  toXML() {
    let rslt = "";
    try {
      let tagNames = ["flowstatus", "flowpath", "deleteMSG", "width", "height", "_applicationid", "_sessionid"];
      let cls = this;
      rslt = "<cn.myapps.runtime.workflow.element.FlowDiagram>\n";
      for (let key in cls) {
        if (tagNames.indexOf(key) >= 0) {
          let clsValue = cls[key] == null ? "" : cls[key];
          rslt += "<" + key + ">" + clsValue + "</" + key + ">\n";
        }
      }
      let flds = this._elems;
      for (let i = 0; i < flds.length; i++) {
        rslt += flds[i].toXML();
      }
      rslt += "</cn.myapps.runtime.workflow.element.FlowDiagram>\n";
    } catch (e) {}

    return rslt;
  }

  //xh
  //验证器
  check() {
    let elems = this._elems;
    let result = "";
    let startNodeCount = 0,
      endNodeCount = 0,
      relationCount = 0;
    let relation = [],
      node = [];

    if (elems.length > 0) {
      for (let i = 0; i < elems.length; i++) {
        if (elems[i] instanceof StartNode) {
          startNodeCount++;
          node.push(elems[i]);
        } else if (elems[i] instanceof CompleteNode) {
          endNodeCount++;
          node.push(elems[i]);
        } else if (elems[i] instanceof Relation) {
          relationCount++;
          relation.push(elems[i]);
        } else {
          node.push(elems[i]);
        }
      }

      for (let j = 0; j < node.length; j++) {
        result = node[j].check();
        if (!result) {
          return false;
        }
      }

      if (startNodeCount == 0 || endNodeCount == 0) {
        alert("请创建开始和完成节点！");
        return false;
      } else if (node.length < 3) {
        alert("请为流程至少创建一个发起者和审批人！");
        return false;
      }

      if (relationCount == 0) {
        alert("请创建节点之间的关联线！");
        return false;
      } else {
        for (let nl = 0; nl < node.length; nl++) {
          if (!node[nl].hasRelation(relation)) {
            return false;
          }
        }
      }

      return true;
    }

    alert("请创建节点元素！");
    return false;
  }

  /**
   * yx 元素垂直居中
   */
  alignVerticalCenter(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最大x值和最小x值，y值不变，x取两者的中间值
      const maxX =  Math.max.apply(Math,this.selectArr.map(item => { return item.x }))
      const minX =  Math.min.apply(Math,this.selectArr.map(item => { return item.x }))
      const maxW = Math.max.apply(Math,this.selectArr.map(item => { return item.width }))
      const minW = Math.min.apply(Math,this.selectArr.map(item => { return item.width }))
      const averageValue = (maxX + minX) / 2;
      const averageWidth = (maxW - minW) / 2
      this.abscissaChange(averageValue, maxW, averageWidth)
    }
  }
  /**
   * yx 左对齐
   */
  alignLeft(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最小x值，y值不变
      const minX =  Math.min.apply(Math,this.selectArr.map(item => { return item.x }))
      this.abscissaChange(minX)
    }
  }
  /**
   * yx 右对齐
   */
  alignRight(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最小x值，y值不变
      const maxX =  Math.max.apply(Math,this.selectArr.map(item => { return item.x }))
      const maxW = Math.max.apply(Math,this.selectArr.map(item => { return item.width }))
      const minW= Math.min.apply(Math,this.selectArr.map(item => { return item.width }))
      const averageWidth = maxW - minW
      this.abscissaChange(maxX, maxW, averageWidth)
    }
  }
  /**
   * yx 元素水平居中
   */
  alignHorizontalCenter(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最大x值和最小x值，y值不变，x取两者的中间值
      const maxX =  Math.max.apply(Math,this.selectArr.map(item => { return item.y }))
      const minX =  Math.min.apply(Math,this.selectArr.map(item => { return item.y }))
      const maxH = Math.max.apply(Math,this.selectArr.map(item => { return item.height }))
      const minH = Math.min.apply(Math,this.selectArr.map(item => { return item.height }))
      const averageValue = (maxX + minX) / 2;
      const averageHeight = (maxH - minH) / 2
      this.ordinateChange(averageValue, maxH, averageHeight)
    }
  }
  /**
   * yx 元素顶部对齐
   */
  alignTop(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最大x值和最小x值，y值不变，x取两者的中间值
      const minX =  Math.min.apply(Math,this.selectArr.map(item => { return item.y }))
      this.ordinateChange(minX)
    }
  }
  /**
   * yx 元素底部对齐
   */
  alignBottom(){
    if(this.selectArr.length < 2){
      alert('请按下Ctrl并至少选择两个节点(节点不包括关连线)')
    }else{ // 获取被选中元素的最大x值和最小x值，y值不变，x取两者的中间值
      const maxX =  Math.max.apply(Math,this.selectArr.map(item => { return item.y }))
      const maxH = Math.max.apply(Math,this.selectArr.map(item => { return item.height }))
      const minH = Math.min.apply(Math,this.selectArr.map(item => { return item.height }))
      const averageHeight = maxH - minH
      this.ordinateChange(maxX, maxH, averageHeight)
    }
  }

  /**
   * yx 原元素的横坐标修改(在selectArr中找到elems的所有节点)
   * @param {横坐标} cx 
   */
  abscissaChange(cx, maxX, width=0){
    for(let i = 0; i < this.selectArr.length; i++){
      for(let j = 0; j < this._elems.length; j++){
        if(Object.is(this.selectArr[i], this._elems[j])){
          if(maxX && maxX > this.selectArr[i].width){
            this._elems[j].x = cx+width;
            break;
          }else{
            this._elems[j].x = cx
            break;
          }
        }
      }
    }
    this.repaint()
  }

  /**
   * yx 原元素纵坐标的修改
   * @param {纵坐标} cy 
   */
  ordinateChange(cy, maxH, height=0){
    for(let i = 0; i < this.selectArr.length; i++){
      for(let j = 0; j < this._elems.length; j++){
        if(Object.is(this.selectArr[i], this._elems[j])){
          if(maxH && maxH > this.selectArr[i].height){
            this._elems[j].y = cy+height;
            break;
          }else{
            this._elems[j].y = cy;
            break;
          }
        }
      }
    }
    this.repaint()
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0097
   */
  removeElement(emn) {
    if (emn != null) {
      if (emn instanceof Node) {
        let v = this.getAllElements();
        for (let i = 0; i < v.length; i++) {
          let elem = v[i];
          if (elem instanceof Relation) {
            let r = elem;
            if ((r.startnodeid != null && r.startnodeid == emn.id) || (r.endnodeid != null && r.endnodeid == emn.id)) {
              this._elems = this._removeElementFromArray(this._elems, r);
            }
          }
        }
        this._elems = this._removeElementFromArray(this._elems, emn);
      } else {
        this._elems = this._removeElementFromArray(this._elems, emn);
      }
    }
    this._currToEdit = null;
  }

  _removeElementFromArray(array, element) {
    for (let i = 0; i < array.length; i++) {
      let em = array[i];
      if (em.id != null && em.id == element.id) {
        return [...array.slice(0, i), ...array.slice(i + 1)];
      }
    }
  }

  /**
   * @param id
   * @roseuid 3E0A6E1B00AB
   */
  removeElementById(id) {
    const elems = this._elems;
    for (let i = 0; i < elems.length; i++) {
      let em = elems[i];
      if (em.id != null && em.id == id) {
        this._elems = [...this._elems.slice(0, i), ...this._elems.slice(i + 1)];
      }
    }
  }

  /**
   * @param x
   * @param y
   * @return cn.myapps.runtime.workflow.Element
   * @roseuid 3E0A6E1B00C9
   */
  chkSelectedElement(x, y) {
    const elems = this._elems;
    for (let i = 0; i < elems.length; i++) {
      if (elems[i] instanceof PaintElement) {
        if (elems[i].isSelected(x, y)) {
          return elems[i];
        }
      }
    }
    return null;
  }

  getFlowstatus() {
    return this.flowstatus;
  }

  /**
   * 设置流程运转路径
   *
   * @param
   */
  setFlowpath(path) {
    if (this.flowpath == null || this.flowpath.trim().length <= 0) {
      this.flowpath = path;
    } else {
      this.flowpath = this.flowpath + ";" + path;
    }
  }

  /**
   * 获取流程运转路径
   *
   * @param
   */
  getFlowpath() {
    let colls = [];
    if (flowpath != null && flowpath.trim().length > 0) {
      let path = this.flowpath.split(";");
      for (let i = 0; i < path.length; i++) {
        let t = path[i].split(",");
        colls.add(t);
      }
    }
    return colls;
  }

  /**
   * 获取流程运转路径最后审核结点
   *
   * @param
   */
  getFlowpathLastNode() {
    let colls = getFlowpath();
    let obj = colls.toArray();
    let nodeid = "";
    let node = null;
    if (obj.length >= 1) {
      let path = obj[obj.length - 1];
      nodeid = path[0];
    }
    if (nodeid != null && nodeid.trim().length > 0) {
      node = getElementByID(nodeid);
    }
    return node;
  }

  /**
   * 设置流程状态
   *
   * @param
   */
  setFlowstatus(status) {
    // if (this.flowstatus == FLOWSTATUS_OPEN_NOSTART &&
    // ( (status & FLOWSTATUS_OPEN_START) > 0)) {
    // this.flowstatus = status;
    // Node n = getFirstNode(); //设起始点
    // setCurrentNode(n);
    // }
    // else
    if (this.flowstatus == FlowType.FLOWSTATUS_OPEN_NOSTART && (status & (FlowType.FLOWSTATUS_OPEN_RUN_RUNNING | FlowType.FLOWSTATUS_CLOSE_TERMINAT)) > 0) {
      this.flowstatus = status;
    } else if (
      this.flowstatus == FlowType.FLOWSTATUS_OPEN_RUN_RUNNING &&
      (status & (FlowType.FLOWSTATUS_OPEN_RUN_SUSPEND | FlowType.FLOWSTATUS_CLOSE_COMPLETE | FlowType.FLOWSTATUS_CLOSE_TERMINAT | FlowType.FLOWSTATUS_OPEN_RUN_RUNNING)) > 0
    ) {
      this.flowstatus = status;
    } else if (this.flowstatus == FlowType.FLOWSTATUS_OPEN_RUN_SUSPEND && (status & (FlowType.FLOWSTATUS_OPEN_RUN_RUNNING | FlowType.FLOWSTATUS_OPEN_RUN_SUSPEND | FlowType.FLOWSTATUS_CLOSE_ABORT)) > 0) {
      this.flowstatus = status;
    } else {
      throw new OBPMValidateException("{*[core.workflow.status.error]*}");
    }
    // //保存流程流转路径
    // if (this.flowstatus == FLOWSTATUS_OPEN_START) {
    // setFlowpath(getCurrentNode().id + "," + START);
    // }else if (this.flowstatus == FLOWSTATUS_OPEN_RUN_RUNNING){
    // setFlowpath(getCurrentNode().id + "," + PASS);
    // }else if (this.flowstatus == FLOWSTATUS_OPEN_RUN_SUSPEND){
    // setFlowpath(getCurrentNode().id + "," + SUSPEND);
    // }else if (this.flowstatus == FLOWSTATUS_CLOSE_TERMINAT){
    // setFlowpath(getCurrentNode().id + "," + TERMINATE);
    // }else if (this.flowstatus == FLOWSTATUS_CLOSE_ABORT){
    // setFlowpath(getCurrentNode().id + "," + ABORT);
    // }else if (this.flowstatus == FLOWSTATUS_CLOSE_COMPLETE){
    // setFlowpath(getCurrentNode().id + "," + COMPLETE);
    // }
  }

  /**
   * 获取当前结点
   *
   * @param
   */
  getFirstCurrentNode() {
    let ems = getAllElements();
    let enum11 = ems.elements();
    while (enum11.hasMoreElements()) {
      let item = enum11.nextElement();
      if (item instanceof Node) {
        let nd = item;
        if (nd._iscurrent) {
          return nd;
        }
      }
    }

    return null;
  }

  /**
   * 获取流程的第一个结点
   *
   * @param
   */
  getFirstNode() {
    let ems = getAllElements();
    let enum11 = ems.elements();
    while (enum11.hasMoreElements()) {
      let item = enum11.nextElement();
      if (item instanceof Node) {
        let nd = item;
        if (nd instanceof StartNode) {
          return nd;
        }
      }
    }
    return null;
  }

  /**
   * 获取所有开始节点
   *
   * @return
   */
  getStartNodeList() {
    let ems = getAllElements();
    let enum11 = ems.elements();
    let colls = [];
    while (enum11.hasMoreElements()) {
      let item = num11.nextElement();
      if (item instanceof Node) {
        let nd = item;
        if (nd instanceof StartNode) {
          colls.add(nd);
        }
      }
    }
    return colls;
  }

  /**
   * 获取当前结点的所有下一个Relation即步骤
   *
   * @param
   */
  getNodeNextRelation(nd) {
    if (nd == null) {
      return null;
    }
    let rv = [];
    let enum11 = this._elems.elements();
    while (enum11.hasMoreElements()) {
      let item = enum11.nextElement();
      // 添加子元素
      if (item instanceof Node) {
        let node = item;
        let subElements = node.getSubelems();
        for (let iterator = subElements.iterator(); iterator.hasNext(); ) {
          let subElment = iterator.next();
          if (subElment instanceof Relation) {
            let r = subElment;
            if (r.startnodeid != null && r.startnodeid.equals(nd.id)) {
              rv.push(r);
            }
          }
        }
      }

      if (item instanceof Relation) {
        let r = item;
        if (r.startnodeid != null && r.startnodeid.equals(nd.id)) {
          rv.push(item);
        }
      }
    }

    return rv;
  }

  /**
   * 获取当前结点的所有上一个Relation即步骤
   *
   * @param
   */
  getNodeBeforeRelation(nd, ispassed) {
    if (nd == null) {
      return null;
    }
    let rv = [];
    for (let i = 0; i < this._elems.length; i++) {
      let em = this._elems[i];
      if (em instanceof Relation) {
        let relation = em;
        if (relation.id != null && relation.endnodeid == nd.id) {
          if (ispassed) {
            if (relation.ispassed) {
              rv.push(relation);
            }
          } else {
            rv.push(relation);
          }
        }
      }
    }
    return rv;
  }

  /**
   * 根据当前relation获取下一结点
   *
   * @param
   * @throws Exception
   */
  getNextNode(r, doc, params, user) {
    let end = null;
    let runner = JavaScriptFactory.getInstance(_sessionid, _applicationid);
    try {
      if (doc != null) {
        runner.initBSFManager(doc, params, user, []);
      }
    } catch (e1) {
      e1.printStackTrace();
    }
    let flag = new Boolean(true);

    let condition = r.condition;
    let filtercondition = r.filtercondition; // marky
    condition = StringUtil.dencodeHTML(condition);
    filtercondition = StringUtil.dencodeHTML(filtercondition); // marky
    try {
      let labelId = getId() + "-" + r.id + "-" + r.startnodeid + "-" + r.endnodeid;
      if (r.editMode != null && r.editMode.equals(Relation.EDITMODE_VIEW)) {
        // '00':view
        if (!filtercondition.equals("") && filtercondition != null) {
          // marky
          filtercondition.replaceAll("\n", " ");

          let label = new StringBuffer();

          label
            .append("RELATION(")
            .append(labelId)
            .append(r.name + ")")
            .append(".filtercondition");

          let obj = runner.run(label.toString(), filtercondition);
          if (obj instanceof Boolean) {
            flag = obj;
          }
        }
      } else {
        if (!condition.equals("") && condition != null) {
          condition.replaceAll("\n", " ");

          let label = new StringBuffer();
          label
            .append("RELATION(")
            .append(labelId)
            .append(r.name + ")")
            .append(".condition");

          let obj = runner.run(label.toString(), condition);
          if (obj instanceof Boolean) {
            flag = obj;
          }
        }
      }
    } catch (e) {
      e.printStackTrace();
    }

    if (flag.booleanValue()) {
      end = r.getEndnode();
    }

    return end;
  }

  /**
   * 获取当前任一relation中上一结点
   *
   * @param
   */
  getStartNode(r) {
    let end = r.getStartnode();
    return end;
  }

  /**
   * 将结点设为当前结点
   *
   * @param
   */
  setCurrentNode(current) {
    if (current == null) {
      return;
    }
    // Vector ems = getAllElements();
    // Enumeration enum1 = ems.elements();
    // while (enum1.hasMoreElements()) {
    // Element item = (Element) enum1.nextElement();
    // if (item instanceof Node) {
    // Node nd = (Node) item;
    // if (nd.iscurrent) {
    // nd.iscurrent = false;
    // }
    // if (nd.id != null && current.id !=null && nd.id.equals(current.id)) {
    // nd.iscurrent = true;
    // }
    // }
    // }
    current._iscurrent = true;
  }

  /**
   * @return java.util.Vector
   * @roseuid 3E0A6E1B00E7
   */
  getAllElements() {
    let vct = [];
    const elems = this._elems;
    for (let i = 0; i < elems.length; i++) {
      vct.push(elems[i]);

      if (elems[i]._subelems != null) {
        const _subelems = elems[i]._subelems;
        for (let j = 0; j < _subelems.length; j++) {
          vct.push(_subelems[j]);
        }
      }
    }
    return vct;
  }

  getAllNodes() {
    let rtn = [];
    let elements = getAllElements();
    for (let iterator = elements.iterator(); iterator.hasNext(); ) {
      let element = iterator.next();
      if (element instanceof Node) {
        rtn.add(element);
      }
    }
    return rtn;
  }

  /**
   * 根据开始节点和结束接点获取关系
   *
   * @return
   */
  getRelation(startnodeid, endnodeid) {
    let colls = this.getAllElements();
    for (let iter = colls.iterator(); iter.hasNext(); ) {
      let element = iter.next();
      if (element instanceof Relation) {
        let relation = element;
        if (startnodeid.equals(relation.startnodeid) && endnodeid.equals(relation.endnodeid)) {
          return relation;
        }
      }
    }
    return null;
  }

  validate(runner, startnodeid, endnodeid) {
    let relation = this.getRelation(startnodeid, endnodeid);
    if (relation != null) {
      let relationId = getId() + startnodeid + endnodeid;
      let script = StringUtil.dencodeHTML(relation.validateScript);
      if (script != null && !script.equals("")) {
        let label = new StringBuffer();
        label
          .append("RELATION(")
          .append(relationId)
          .append(relation.name + ")")
          .append(".Validate");
        let rtn = runner.run(label.toString(), script);
        return rtn;
      }
    }
    return null;
  }

  runAction(runner, startnodeid, endnodeid) {
    let relation = this.getRelation(startnodeid, endnodeid);
    if (relation != null) {
      let action = relation.action;
      if (action != null && action.trim().length > 0) {
        action = StringUtil.dencodeHTML(action);

        let label = new StringBuffer();
        // relationId = flowid + startnodeid + endnodeid 标识流程线段唯一
        let relationId = getId() + "-" + startnodeid + "-" + endnodeid;
        label.append("RELATION(").append(relationId).append(").");
        label.append(relation.name).append(".Action");
        let rtn = runner.run(label.toString(), action);
        if (rtn instanceof String && !StringUtil.isBlank(rtn)) {
          throw new OBPMValidateException(rtn, new RunActionException(rtn));
        }
      }
    }
  }
  //xh 待分析
  informationCheck() {
    var errorStr = "";
    const elems = this._elems;
    for (let i = 0; i < elems.length; i++) {
      errorStr += elems[i].errorCheck();
    }
    return errorStr;
  }

  /**
   * @param id
   * @return cn.myapps.runtime.workflow.Element
   * @roseuid 3E0A6E1B00F1
   */
  getElementByID(id) {
    if (id == null || id.trim().length == 0) {
      return null;
    }

    const elems = this._elems;
    for (let i = 0; i < elems.length; i++) {
      let em = elems[i];
      if (em && em.id != null && em.id == id) {
        return em;
      }

      if (em && em.getSubelems() != null) {
        for (let j = 0; j < em.getSubelems().length; j++) {
          let subem = em.getSubelems()[j];
          if (subem.id != null && subem.id == id) {
            return subem;
          }
        }
      }
    }
    return null;
  }

  getNodeByID(id) {
    let element = getElementByID(id);
    if (element instanceof Node) {
      return element;
    }
    return null;
  }

  /**
   * 根据当前节点获取上一步所有节点 happy
   *
   * @param node
   * @return
   */
  getBackSetpNode(node) {
    let nodes = [];
    let allrelation = getNodeBackStepRelation(node);
    for (let iter = allrelation.iterator(); iter.hasNext(); ) {
      let re = iter.next();
      if (re.endnodeid.equals(node.id)) {
        let n = getStartNode(re);
        if (!(n instanceof AutoNode) && !(n instanceof StartNode)) {
          nodes.add(n);
        }
      }
    }
    return nodes;
  }

  /**
   * 根据当前节点获取上一步所有节点(包含所有类型的节点)happy
   *
   * @param node
   * @return
   */
  getBackSetpElement(node) {
    let nodes = [];
    let allrelation = getNodeBackStepRelation(node);
    for (let iter = allrelation.iterator(); iter.hasNext(); ) {
      let re = iter.next();
      if (re.endnodeid.equals(node.id)) {
        let n = getStartNode(re);
        nodes.add(n);
      }
    }
    return nodes;
  }

  /**
   * 根据当前节点获取所有上一步Relation happy
   *
   * @param nd
   * @return
   */
  getNodeBackStepRelation(nd) {
    if (nd == null) {
      return null;
    }
    let rv = [];
    for (let iter = _elems.iterator(); iter.hasNext(); ) {
      let subem = iter.next();
      if (subem instanceof Relation) {
        let relation = subem;
        if (relation.id != null && relation.endnodeid.equals(nd.id)) {
          rv.add(relation);
        }
      }
    }
    return rv;
  }

  /**
   * 获取流程流转Relation
   *
   * @param
   */
  getElementByBeginEndNodeID(startid, endid) {
    if (startid == null || startid.trim().length == 0 || endid == null || endid.trim().length == 0) {
      return null;
    }

    for (let e = _elems.elements(); e.hasMoreElements(); ) {
      let em = e.nextElement();
      if (em.getSubelems() != null) {
        for (let sube = em.getSubelems().elements(); sube.hasMoreElements(); ) {
          let subem = sube.nextElement();
          if (subem instanceof Relation) {
            let relation = subem;
            if (relation.id != null && relation.startnodeid.equals(startid) && relation.endnodeid.equals(endid)) {
              return subem;
            }
          }
        }
      }
    }
    return null;
  }

  /**
   * @throws java.lang.Exception
   * @roseuid 3E0A6E1B010F
   */
  jbInit(e) {
    this.canvas.onmousemove = (e) => {
      this.mouseMove(e);
    };

    this.canvas.onclick = (e) => {
      this.mouseClick(e);
    };
    this.canvas.onmousedown = (e) => {
      this.mouseDown(e);
    };
    this.canvas.onmouseup = (e) => {
      this.mouseUp(e);
    };
    document.onkeydown = (e) => {
      this.handleKeyDown(e);
    };
    document.onkeyup = (e) => {
      this.handleKeyUp(e);
    };
    this.canvas.onmouseover = (e) => {
      //this_mouseEntered(e);
    };
    this.canvas.ondrop=(e)=>{
      this.mouseDown(e);
    }
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0123
   */
  mouseClick(e) {
    let x = e.clientX;
    let y = e.clientY;
    x = x / this.scaleNum;
    y = y / this.scaleNum;
    let em = this.chkSelectedElement(x, y);
    if (em != null) {
      console.log("选中em ", em);
    }

    // yx判断不是中文才重新执行
    if (getCookie("designerLanguage") != "zh") {
      // 重新渲染多语言
      jQuery(document).ready(function () {
        let lang = getCookie("designerLanguage");
        // clearTimeout(timer)
        jQuery.i18n.properties({
          //加载资浏览器语言对应的资源文件
          name: "strings", //资源文件名称
          path: "i18n/", //资源文件路径
          language: lang,
          cache: false,
          mode: "map", //用Map的方式使用资源文件中的值
          callback: function () {
            //加载成功后设置显示内容
            for (let i in $.i18n.map) {
              $('[data-lang="' + i + '"]').text($.i18n.map[i]);
            }
            // document.title = $.i18n.map['title'];
          },
        });
      });
    }
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0137
   */
  mouseDown(e) {
    let { x, y } = this.getLocationWithCanvas(this.canvas, e.clientX, e.clientY);
    x = x / this.scaleNum;
    y = y / this.scaleNum;
    switch (this._statues) {
      case FlowDiagram.ACTION_ADD_AUTONODE:
        this.addAutoNode("", "", 0, x, y);
        break;
      case FlowDiagram.ACTION_ADD_COMPLETENODE:
        this.addCompleteNode("", "", 0, x, y);
        break;
      case FlowDiagram.ACTION_ADD_MANUALNODE:
        this.addManualNode("", "", 0, x, y);
        break;
      case FlowDiagram.ACTION_ADD_RELATION:
        this.addRelation("", "", "", "", "", "", "", "");
        break;
      case FlowDiagram.ACTION_ADD_STARTNODE:
        this.addStartNode("", "", x, y);
        break;
      case FlowDiagram.ACTION_ADD_GATEWAYNODE:
        this.addGatewayNode("", "", x, y);
        break;
      case FlowDiagram.ACTION_ADD_SUSPENDNODE:
        this.addSuspendNode("", "", x, y);
        break;
      case FlowDiagram.ACTION_ADD_SUBFLOW:
        this.addSubFlow("", "", 0, x, y);
        break;
      default:
        break;
    }

    let isChangeCursor = false;

    let em = this.chkSelectedElement(x, y);
    if (em != null) {
      if (em instanceof Node) {
        // 设置抓取点
        em._handlePoint.x = x - em.x;
        em._handlePoint.y = y - em.y;
      }

      this._changed = true;
      if (this._statues == FlowDiagram.ACTION_REMOVE) {
        // deleteMSG = null;
        if (em instanceof Relation) {
          let r = em;
          if (r.ispassed) {
            this.deleteMSG = "相关流程已处理,不能删除!";
          }
        }
        if (em instanceof Node) {
          let nd = em;
          if (em.id != null && nd._iscurrent) {
            this.deleteMSG = "当前节点在处理中,不能删除!";
          } else {
            let ems = this.getAllElements();
            for (let i = 0; i < ems.length && this.deleteMSG == "null"; i++) {
              let elem = ems[i];
              if (elem instanceof Relation) {
                let rl = elem;
                if (((rl.startnodeid != null && rl.startnodeid == em.id) || (rl.endnodeid != null && rl.endnodeid == em.id)) && rl.ispassed) {
                  this.deleteMSG = "相关流程已处理,不能删除!";
                }
              }
            }
          }
        }
        if (this.deleteMSG == "null" || this.deleteMSG == null || this.deleteMSG == "") {
          this.removeElement(em);
        } else {
          alert(this.deleteMSG);
        }
      } else if (this._statues == FlowDiagram.ACTION_BREAK_LINE) {
        // add by gusd

        if (em instanceof Relation) {
          this._selected = em;
          let relation = this._selected;
          relation.setBreakpoint(new Point(x, y));
          isChangeCursor = true;
        }
      } // add by gusd
      else {
        if (this._selected instanceof Relation && em instanceof Node) {
          let r = this._selected;
          if (r.getStartnode() == null && em != null) {
            r.setStartnode(em);
            let node = em;
            let point = new Point(node.x, node.y);
            r.addVector(point);
          }
        } else if (em instanceof Relation) {
          this.changeStatues(FlowDiagram.ACTION_BREAK_LINE);
          this._selected = em;
          let relation = this._selected;
          // yx 如果按下时在原有点就改变changevector这个值
          let press = relation.checkPressBreakPoint(relation.vector, x, y)
          // 有按下点的位置
          if (press) {
            relation.setChangevector(1);
          } else {
            relation.setChangevector(-1);
            relation.setBreakpoint(new Point(x, y));
          }

          relation.setCurrentselect(true);
          isChangeCursor = true;
        } else {
          this._selected = em;
          this._selected.moveTo(x, y);
        }
      }
      this.repaint();
    } else {
      // em == null
      if (this._statues == FlowDiagram.ACTION_ADD_RELATION && this._selected instanceof Relation) {
        let r = this._selected;
        if (r.getStartnode() == null) {
          this.removeElement(r);
          r = null;
          this._selected = null;
        }
      } else {
        if (this._selected instanceof Node) {
          // ((Node)_selected).
        }
        this._selected = null;
        this._currToEdit = null;
      }
      this.repaint();
    }
    if (!isChangeCursor) {
      // isChangeCursor为true时，用户准备拖拉流程线，不释放鼠标指针的拖拉样式
      this.changeStatues(FlowDiagram.ACTION_NORMAL);
      isChangeCursor = false;
    }
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0155
   */
  mouseUp(e) {
    // yx 清空辅助线
    AuxiliaryLine.getInstance().setProps({points: null});

    let { x, y } = this.getLocationWithCanvas(this.canvas, e.clientX, e.clientY);
    x = x / this.scaleNum;
    y = y / this.scaleNum;
    let em = this.chkSelectedElement(x, y);
    if (this._selected != null && this._selected instanceof Relation && (em == null || em instanceof Node)) {
      let r = this._selected;
      r.setCurrentselect(false);
      if (r.getEndnode() == null) {
        if (em != null) {
          let have = false;
          let that = this;
          let haveNum = 0;
          this._elems.forEach(function (val, ind) {
            if (val instanceof Relation) {
              if (val.startnodeid == r.startnodeid && val.endnodeid == em.id) {
                haveNum = ind;
                have = true;
              }
            }
          });
          if (have) {
            this._elems = this._removeElementFromArray(this._elems, r);
            alert("同方向关联线有且仅有一条");
          } else {
            this._changed = true;
            r.setEndnode(em);
            let node = em;

            let point = new Point(node.x, node.y);
            r.addVector(point);
          }
        }
        if (em == null) {
          //|| (false && r.getStartnode().id.equals(r.getEndnode().id))) {
          this._elems = this._removeElementFromArray(this._elems, r);
        }
      } else {
        let nx = x;
        let ny = y;
        console.log('mouseup')
        let pos = r.getChangevector();
        r.setCurrentselect(false);
        if (pos == -1) {
          // 原先按下的点不是原有折点
          let bool = r.checkDistance(new Point(nx, ny));
          if (!bool) {
            console.log('检查鼠标释放的点拖拉的距离是否小于一个常量，如果是，则当作没有拖拉')
            r.addVector(new Point(nx, ny)); // 如果鼠标释放的点拖拉的距离大于一个常量，则当作一个新的折点
          }
        } else {
          if(r.delVector){
            console.log('mouseupadd', x, y)
            // 记录原来的折点位置用于添加的时候才做修改
            r.addChangeVector(new Point(nx, ny), r.delIndex);
          }else{
            // 原先按下的点为原有折点，鼠标释放后要改变原有折点的坐标
            console.log('原先按下的点为原有折点，鼠标释放后要改变原有折点的坐标')
            r.changeVector(r.vector, new Point(nx, ny));
            r.setChangevector(-1);
          }
        }
      }
    }
    // 设置当前选中
    if (this._selected != null && em != null && Object.is(this._selected, em)) {
      this._currToEdit = em;

      if (em instanceof Node) {
        let nd = em;

        let p = new Point(nd.x + nd._imgrect.width / 2, nd.y + nd._imgrect.height / 2);

        // 忽略掉10个像素差异
        let nx = p.x;
        let ny = p.y;

        //nx = Math.round((nx + 10) / 20) * 20;
        //ny = Math.round((ny + 10) / 20) * 20;

        em.x = nx - nd._imgrect.width / 2;
        em.y = ny - nd._imgrect.height / 2;
      }

      // yx
      if (this.ctrlDown) {/*如果按下了ctrl*/
        const flag = this.isInZoom(this._selected)
        if(!flag){ // 如果不在数组中那么就添加到选中的数据
          // 判断当前的元素是否为线，如果为线就不添加到数组中
          if(!(this._selected instanceof Relation)){
            this.selectArr.push(this._selected)
          }
        }else{ // 如果在原来的数据那么就从原来的数组中移除
          this.selectArr = this._removeElementFromArray(this.selectArr, this._selected)
        }
        console.log('如果点中元素不在selected里，就添加，否则从selected里删除，并清空当前元素')
      } else {/*如果没有按下ctrl，直接清空selected，保留当前元素*/
        this.selectArr = [];
        console.log('没有按下ctrl，直接清空selected')
      }
    }

    // yx 如果没有选中ctrl数组那么就置为空
    if(this._selected == null){
      this.selectArr = [];
      this.ctrlEle = null;
    }
    this.ctrlEle = this._selected;

    this.changeStatues(FlowDiagram.ACTION_NORMAL);
    this._selected = null;

    this.repaint();
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0169
   */
  mouseMove(e) {
    //console.log("mouseMove");
    let { x, y } = this.getLocationWithCanvas(this.canvas, e.clientX, e.clientY);
    x = x / this.scaleNum;
    y = y / this.scaleNum;
    if (e.buttons <= 0) {
      if (this._statues == FlowDiagram.ACTION_BREAK_LINE) {
      } else {
        let tg = this.g;
        tg.setColor(Resources.COLOR.black);
        let em = this.chkSelectedElement(x, y);
        if (em != null && !(em instanceof Relation)) {
          this.changeStatues(this._statues);
          // if(this._statues == ACTION_REMOVE){

          // }
          let nd = em;
          if (em.isSelected(x, y)) {
            let snote = "";
            if (nd.note == null || nd.note == "null") {
              snote = "";
            } else if (nd.note.length <= 9) {
              snote = nd.note;
            } else {
              snote = nd.note.substring(0, 9) + "...";
            }

            nd.showTips(tg); // 显示注释
          }
        } else {
          this.repaint();
        }
      }
    } else {
      this.mouseDrag(e);
    }
  }

  /**
   * @param e
   * @roseuid 3E0A6E1B0187
   */
  mouseDrag(e) {
    let { x, y } = this.getLocationWithCanvas(this.canvas, e.clientX, e.clientY);
    x = x / this.scaleNum;
    y = y / this.scaleNum;
    if (this._selected != null && this._statues != FlowDiagram.ACTION_BREAK_LINE && this._selected instanceof Relation) {
      // 从一个结点到另一个结点画流程的拖拉过程中
      if (this._selected.getEndnode() == null) {
        this._selected.moveTo(x, y);
        this.repaint();
      }
    } else if (this._selected != null && this._statues != FlowDiagram.ACTION_BREAK_LINE) {
      // 接近其他元素的時候自動靠過去
      this.closeEleChange(this._selected)

      // yx 画辅助线
      const auxiliaryLinePoints = AuxiliaryLine.getInstance().getProp('points');
      const movedX = Math.abs(this._selected.getProp('x') - x);
			const movedY = Math.abs(this._selected.getProp('y') - y);
      if(auxiliaryLinePoints && auxiliaryLinePoints.abscissa && !auxiliaryLinePoints.ordinate && movedX < 3) {
        // this._selected.setProps({y: y});
      } else if(auxiliaryLinePoints && !auxiliaryLinePoints.abscissa && auxiliaryLinePoints.ordinate && movedY < 3) {	
        // this._selected.setProps({x: x});
      } else if(auxiliaryLinePoints && auxiliaryLinePoints.abscissa && auxiliaryLinePoints.ordinate && (movedY < 3 && movedX < 3)) {
        return false;
      } else {
        // this._selected.setProps({x: x, y: y});
      }
      
      const points = this.getDrawAuxiliaryLinePoint(this._selected);
        
      if(points) {
        AuxiliaryLine.getInstance().setProps({points: points});
      } else {
        AuxiliaryLine.getInstance().setProps({points: null});
      }

      this._selected.moveTo(x, y);
      this.repaint();
    } else if (this._selected != null && this._selected instanceof Relation && this._statues == FlowDiagram.ACTION_BREAK_LINE) {
      // 拖拉流程线产生折点的过程
      console.log('drag', this._selected.delVector)
      const pos = this._selected.getChangevector(); // 检查拖拉点是否原有折点
      console.log('pos', pos)
      this._selected.setCurrentselect(true);
      if (pos == -1) { // 拖拉点不是原有折点，把鼠标移动点作为临时的_movepoint
        this._selected.setMovepoint({ x: x, y: y });
      } else { // 拖拉点是原有折点,鼠标移动点当作原有折点的新位置
        console.log('drag添加的')
        this._selected.changeVector(this._selected.vector, { x: x, y: y });
      }
      if(this.delActor){
        this._selected.setMovepoint({ x: x, y: y });
      }
      this.repaint();
    } // end
  }

  /**
   * yx 键盘按下
   */
  handleKeyDown(e) {
    switch (e.keyCode) {
      case 17:
        this.ctrlDown = true;
        // 如果是第一个就加上，如果不是第一个就只执行下面的函数
        if(this.selectArr.length == 0 && this._currToEdit && !(this._currToEdit instanceof Relation)){
          this.selectArr.push(this._currToEdit)
        }
        break;
      default:
        break;
    }
  }

  /**
   * yx 键盘抬起
   */
  handleKeyUp(e) {
    switch (e.keyCode) {
      case 17:
        this.ctrlDown = false;
        break;
      default:
        break;
    }
  }

  isAssignBack(node) {
    // 删除节点时判断此节点是否为指定回退节点
    let isAssignBack = false;
    for (let e = this.getAllElements().elements(); e.hasMoreElements(); ) {
      let em = e.nextElement();
      if (em != null && em instanceof ManualNode) {
        let nd = em;
        if (nd.exceedaction != null && nd.exceedaction.trim() == FlowType.DOBACKTONODE && nd.backnodeid.equals(node.id)) {
          return true;
        }
      }
    }
    return isAssignBack;
  }

  /**
   * 获取当前节点关联的所有下一个节点
   *
   * @param currnodeid
   *            当前节点
   * @return 当前节点关联的所有下一个节点
   */
  getNextNodeList(currnodeid, doc, params, user) {
    let em = this.getElementByID(currnodeid);
    if (em instanceof Node) {
      let node = em;
      let relations = this.getNodeNextRelation(node);
      let colls = [];
      if (relations != null) {
        let it = relations.iterator();

        while (it.hasNext()) {
          let nextNode = this.getNextNode(it.next(), doc, params, user);
          if (nextNode != null) {
            colls.add(nextNode);
          }
        }
      }
      return colls;
    }
    return Collections.EMPTY_LIST;
  }

  /**
   * 获取结点列表
   */
  getNodeListByIds(ids) {
    let rtn = [];
    if (ids != null) {
      for (let i = 0; i < ids.length; i++) {
        let em = this.getElementByID(ids[i]);
        if (em instanceof Node) {
          rtn.add(em);
        }
      }
    }
    return rtn;
  }

  /**
   * 获取子流程ID列表
   *
   * @return Collection<String>
   */
  getSubFlowNodeList() {
    let rtn = [];
    let elements = getAllElements();

    try {
      for (let iterator = elements.iterator(); iterator.hasNext(); ) {
        let elemnt = iterator.next();
        if (elemnt instanceof SubFlow) {
          rtn.add(elemnt);
        }
      }
    } catch (e) {
      e.printStackTrace();
    }

    return rtn;
  }
}

FlowDiagram.ACTION_NORMAL = 0x00000000;
FlowDiagram.ACTION_REMOVE = 0x00000001;
FlowDiagram.ACTION_ADD_ABORTNODE = 0x00000010;
FlowDiagram.ACTION_ADD_AUTONODE = 0x00000011;
FlowDiagram.ACTION_ADD_COMPLETENODE = 0x00000012;
FlowDiagram.ACTION_ADD_MANUALNODE = 0x00000013;
FlowDiagram.ACTION_ADD_STARTNODE = 0x00000014;
FlowDiagram.ACTION_ADD_SUSPENDNODE = 0x00000015;
FlowDiagram.ACTION_ADD_TERMINATENODE = 0x00000016;
FlowDiagram.ACTION_ADD_SUBFLOW = 0x00000017;
FlowDiagram.ACTION_ADD_GATEWAYNODE = 0x00000018;
FlowDiagram.ACTION_ADD_RELATION = 0x00001000;
FlowDiagram.ACTION_EDIT_NODE = 0x10000010;
FlowDiagram.ACTION_EDIT_RELATION = 0x10001000;
FlowDiagram.ACTION_BREAK_LINE = 0x00100000;

export default FlowDiagram;
