diff --git a/app.platform/src/main/java/app/platform/Application.java b/app.platform/src/main/java/app/platform/Application.java index d3c2e122..e8a7f8e9 100644 --- a/app.platform/src/main/java/app/platform/Application.java +++ b/app.platform/src/main/java/app/platform/Application.java @@ -2,18 +2,9 @@ package app.platform; import io.sc.platform.core.ApplicationLauncher; import io.sc.platform.core.PlatformSpringBootServletInitializer; -import io.sc.platform.core.util.DateUtil; -import io.sc.platform.core.util.FileUtil; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.WebApplicationInitializer; -import java.io.File; -import java.io.FileInputStream; -import java.text.ParseException; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * 应用程序入口 */ @@ -22,193 +13,4 @@ public class Application extends PlatformSpringBootServletInitializer implements public static void main(String[] args) throws Exception { ApplicationLauncher.run(Application.class,args); } - public static void main2(String[] args) throws Exception { -// ApplicationLauncher.run(Application.class,args); -// if(1==1){ -// return; -// } - - File dir =new File("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/TABLE"); - String[] files =dir.list(); - Arrays.sort(files); - StringBuilder result =new StringBuilder(); - for(String fileName : files){ - if(!fileName.endsWith(".sql")){ - continue; - } - StringBuilder sb =new StringBuilder(); - String file ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/TABLE/" + fileName; - String outputFile ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/output/" + fileName; - List list =FileUtil.readStringAsList(new FileInputStream(file),"UTF-8"); - for(String s : list){ - String line =s; - line =replace_CONSTRAINT(line); - line =replace_COMPRESS(line); - line =replaceGt4000(line); - line =replace_PARTITION(line); - line =replace_PRIMARY(line); - line =replace_DROP(line); - if(line.trim().equals("")){ - if(sb.length()>2) { - sb.setLength(sb.length() - 2); - sb.append(" "); - } - } - sb.append(line).append("\n"); - } - sb.setLength(sb.length()-2); - sb.append(";"); - //FileUtil.writeString(outputFile,sb.toString(),"UTF-8"); - result.append(sb.toString()).append("\n\n"); - } - FileUtil.writeString("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/ddl001/table/MODIFYUSER/output/result.sql",result.toString(),"UTF-8"); - } - - public static void main3(String[] args) throws Exception { - File dir =new File("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/TABLE"); - String[] files =dir.list(); - Arrays.sort(files); - StringBuilder result =new StringBuilder(); - for(String fileName : files){ - if(!fileName.endsWith(".sql") || "CS_SCRIPT_CONFIG.0.0.sql".equalsIgnoreCase(fileName) - || "CS_SCRIPT_CONFIG.0.0.sql".equalsIgnoreCase(fileName)){ - continue; - } - StringBuilder sb =new StringBuilder(); - sb.append("TRUNCATE TABLE ").append(fileName.substring(0,fileName.length()-8)).append(";").append("\n"); - String file ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/TABLE/" + fileName; - String outputFile ="/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/output/" + fileName; - List list =FileUtil.readStringAsList(new FileInputStream(file),"UTF-8"); - for(String s : list){ - String line =s; - line =replace_DATE(line); - line =replace_DATE2(line); - line =replace_COMMIT(line); - sb.append(line).append("\n"); - } - result.append(sb.toString()).append("\n\n"); - } - FileUtil.writeString("/Users/wangshaoping/wspsc/work/yusys/工程项目/宁波银行/rwa/数据资料/安硕/1716611768596_export_file/MODIFYUSER/output/result.sql",result.toString(),"UTF-8"); - } - - private static String replace_CONSTRAINT(String str){ - Pattern PATTERN =Pattern.compile("(CONSTRAINT\\s(\"(.+?)\") NOT\\sNULL\\sENABLE)"); - Matcher matcher =PATTERN.matcher(str); - Map founds =new HashMap(); - while (matcher.find()) { - founds.put(matcher.group(1),matcher.group(2)); - } - String result =str; - if(founds!=null && founds.size()>0) { - for(String placeholder : founds.keySet()) { - result =result.replace(placeholder," NOT NULL"); - } - } - return result; - } - - private static String replace_COMPRESS(String str){ - Pattern PATTERN =Pattern.compile("(COMPRESS\\s(.+))"); - Matcher matcher =PATTERN.matcher(str); - Map founds =new HashMap(); - while (matcher.find()) { - founds.put(matcher.group(1),""); - } - String result =str; - if(founds!=null && founds.size()>0) { - for(String placeholder : founds.keySet()) { - result =result.replace(placeholder,founds.get(placeholder)); - } - } - return result; - } - - private static String replace_PRIMARY(String str){ - if(str.indexOf("PRIMARY")>-1){ - return ""; - } - return str; - } - - private static String replaceGt4000(String str){ - //System.out.println(str); - Pattern PATTERN =Pattern.compile("(VARCHAR2\\((\\d+)\\))"); - Matcher matcher =PATTERN.matcher(str); - Map founds =new HashMap(); - while (matcher.find()) { - String number =matcher.group(2); - if(Integer.parseInt(number)>4000){ - number ="VARCHAR2(4000)"; - }else{ - number ="VARCHAR2(" + number + ")"; - } - founds.put(matcher.group(1),number); - } - - String result =str; - if(founds!=null && founds.size()>0) { - for(String placeholder : founds.keySet()) { - result =result.replace(placeholder,founds.get(placeholder)); - } - } - return result; - } - - private static String replace_PARTITION(String str){ - if(str.indexOf("partition")>-1){ - return ""; - } - return str; - } - - private static String replace_DROP(String str){ - if(str.indexOf("DROP")>-1){ - //return ""; - } - return str; - } - - private static String replace_DATE(String str) throws ParseException { - Pattern PATTERN =Pattern.compile("('(\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}:\\d{2}:\\d{2})')"); - Matcher matcher =PATTERN.matcher(str); - Map founds =new HashMap(); - while (matcher.find()) { - Date d = DateUtil.parseDate(matcher.group(2)); - founds.put(matcher.group(1),"TO_TIMESTAMP('" + matcher.group(2) + ":0','YYYY-MM-DD HH24:MI:SS:FF')"); - } - - String result =str; - if(founds!=null && founds.size()>0) { - for(String placeholder : founds.keySet()) { - result =result.replace(placeholder,"null"); - } - } - return result; - } - - private static String replace_DATE2(String str) throws ParseException { - Pattern PATTERN =Pattern.compile("('(\\d{4}\\-\\d{2}\\-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d{3})')"); - Matcher matcher =PATTERN.matcher(str); - Map founds =new HashMap(); - while (matcher.find()) { - Date d = DateUtil.parseDate(matcher.group(2)); - founds.put(matcher.group(1),"TO_TIMESTAMP('" + matcher.group(2) + ":0','YYYY-MM-DD HH24:MI:SS:FF')"); - } - - String result =str; - if(founds!=null && founds.size()>0) { - for(String placeholder : founds.keySet()) { - result =result.replace(placeholder,"null"); - } - } - return result; - } - - private static String replace_COMMIT(String str){ - if(str.indexOf("commit")>-1){ - return ""; - } - return str; - } - } diff --git a/erm.frontend/package.json b/erm.frontend/package.json index 4f29bd45..fc0c94cf 100644 --- a/erm.frontend/package.json +++ b/erm.frontend/package.json @@ -1,116 +1,114 @@ { - "name": "erm.frontend", - "version": "8.1.44", - "description": "", - "private": false, - "keywords": [ - - ], - "author": "", - "license": "ISC", - "scripts": { - "dev": "nodemon", - "serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs", - "build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs", - "prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs", - "sync": "platform sync", - "clean": "rm -rf ./node_modules && rm -rf pnpm-lock.yaml" - }, - "engines": { - "node": ">=18", - "pnpm": ">=7" - }, - "publishConfig": { - "registry": "http://nexus.sc.io:8000/repository/npm-releases/", - "access": "public" - }, - "devDependencies": { - "@babel/core": "7.24.4", - "@babel/preset-env": "7.24.4", - "@babel/preset-typescript": "7.24.1", - "@babel/plugin-transform-class-properties": "7.24.1", - "@babel/plugin-transform-object-rest-spread": "7.24.1", - "@quasar/app-webpack": "3.12.5", - "@quasar/cli": "2.4.0", - "@types/mockjs": "1.0.10", - "@types/node": "20.12.7", - "@typescript-eslint/eslint-plugin": "7.7.1", - "@typescript-eslint/parser": "7.7.1", - "@vue/compiler-sfc": "3.4.24", - "@webpack-cli/serve": "2.0.5", - "autoprefixer": "10.4.19", - "babel-loader": "9.1.3", - "clean-webpack-plugin": "4.0.0", - "copy-webpack-plugin": "12.0.2", - "cross-env": "7.0.3", - "css-loader": "7.1.1", - "eslint": "8.56.0", - "eslint-config-prettier": "9.1.0", - "eslint-plugin-prettier": "5.1.3", - "eslint-plugin-vue": "9.25.0", - "eslint-webpack-plugin": "4.1.0", - "html-webpack-plugin": "5.6.0", - "json5": "2.2.3", - "mini-css-extract-plugin": "2.9.0", - "nodemon": "3.1.0", - "postcss": "8.4.38", - "postcss-import": "16.1.0", - "postcss-loader": "8.1.1", - "postcss-preset-env": "9.5.9", - "prettier": "3.2.5", - "sass": "1.75.0", - "sass-loader": "14.2.1", - "typescript": "5.4.5", - "vue-loader": "17.4.2", - "webpack": "5.91.0", - "webpack-bundle-analyzer": "4.10.2", - "webpack-cli": "5.1.4", - "webpack-dev-server": "5.0.4", - "webpack-merge": "5.10.0", - "@vue/babel-plugin-jsx": "1.2.2" - }, - "dependencies": { - "@codemirror/autocomplete": "6.16.0", - "@codemirror/commands": "6.5.0", - "@codemirror/lang-html": "6.4.9", - "@codemirror/lang-java": "6.0.1", - "@codemirror/lang-javascript": "6.2.2", - "@codemirror/lang-json": "6.0.1", - "@codemirror/lang-sql": "6.6.3", - "@codemirror/lang-xml": "6.1.0", - "@codemirror/language": "6.10.1", - "@codemirror/search": "6.5.6", - "@codemirror/state": "6.4.1", - "@codemirror/view": "6.26.3", - "@maxgraph/core": "0.10.0", - "@quasar/extras": "1.16.11", - "@vueuse/core": "10.9.0", - "axios": "1.6.8", - "codemirror": "6.0.1", - "dayjs": "1.11.10", - "echarts": "5.5.0", - "exceljs": "4.4.0", - "file-saver": "2.0.5", - "luckyexcel": "1.0.1", - "mockjs": "1.1.0", - "pinia": "2.1.7", - "platform-core": "8.1.231", - "quasar": "2.15.3", - "tailwindcss": "3.4.3", - "vue": "3.4.24", - "vue-dompurify-html": "5.0.1", - "vue-i18n": "9.13.1", - "vue-router": "4.3.2", - "@univerjs/core": "0.1.13", - "@univerjs/design": "0.1.13", - "@univerjs/docs": "0.1.13", - "@univerjs/docs-ui": "0.1.13", - "@univerjs/engine-formula": "0.1.13", - "@univerjs/engine-render": "0.1.13", - "@univerjs/facade": "0.1.13", - "@univerjs/sheets": "0.1.13", - "@univerjs/sheets-formula": "0.1.13", - "@univerjs/sheets-ui": "0.1.13", - "@univerjs/ui": "0.1.13" - } + "name": "erm.frontend", + "version": "8.1.44", + "description": "", + "private": false, + "keywords": [], + "author": "", + "license": "ISC", + "scripts": { + "dev": "nodemon", + "serve": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack serve --config webpack.env.serve.cjs", + "build": "node ./util-components-generator.cjs && cross-env NODE_ENV=development webpack --config webpack.env.build.cjs", + "prod": "node ./util-components-generator.cjs && cross-env NODE_ENV=production webpack --config webpack.env.prod.cjs", + "sync": "platform sync", + "clean": "rm -rf ./node_modules && rm -rf pnpm-lock.yaml" + }, + "engines": { + "node": ">=18", + "pnpm": ">=7" + }, + "publishConfig": { + "registry": "http://nexus.sc.io:8000/repository/npm-releases/", + "access": "public" + }, + "devDependencies": { + "@babel/core": "7.24.4", + "@babel/preset-env": "7.24.4", + "@babel/preset-typescript": "7.24.1", + "@babel/plugin-transform-class-properties": "7.24.1", + "@babel/plugin-transform-object-rest-spread": "7.24.1", + "@quasar/app-webpack": "3.12.5", + "@quasar/cli": "2.4.0", + "@types/mockjs": "1.0.10", + "@types/node": "20.12.7", + "@typescript-eslint/eslint-plugin": "7.7.1", + "@typescript-eslint/parser": "7.7.1", + "@vue/compiler-sfc": "3.4.24", + "@webpack-cli/serve": "2.0.5", + "autoprefixer": "10.4.19", + "babel-loader": "9.1.3", + "clean-webpack-plugin": "4.0.0", + "copy-webpack-plugin": "12.0.2", + "cross-env": "7.0.3", + "css-loader": "7.1.1", + "eslint": "8.56.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-prettier": "5.1.3", + "eslint-plugin-vue": "9.25.0", + "eslint-webpack-plugin": "4.1.0", + "html-webpack-plugin": "5.6.0", + "json5": "2.2.3", + "mini-css-extract-plugin": "2.9.0", + "nodemon": "3.1.0", + "postcss": "8.4.38", + "postcss-import": "16.1.0", + "postcss-loader": "8.1.1", + "postcss-preset-env": "9.5.9", + "prettier": "3.2.5", + "sass": "1.75.0", + "sass-loader": "14.2.1", + "typescript": "5.4.5", + "vue-loader": "17.4.2", + "webpack": "5.91.0", + "webpack-bundle-analyzer": "4.10.2", + "webpack-cli": "5.1.4", + "webpack-dev-server": "5.0.4", + "webpack-merge": "5.10.0", + "@vue/babel-plugin-jsx": "1.2.2" + }, + "dependencies": { + "@codemirror/autocomplete": "6.16.0", + "@codemirror/commands": "6.5.0", + "@codemirror/lang-html": "6.4.9", + "@codemirror/lang-java": "6.0.1", + "@codemirror/lang-javascript": "6.2.2", + "@codemirror/lang-json": "6.0.1", + "@codemirror/lang-sql": "6.6.3", + "@codemirror/lang-xml": "6.1.0", + "@codemirror/language": "6.10.1", + "@codemirror/search": "6.5.6", + "@codemirror/state": "6.4.1", + "@codemirror/view": "6.26.3", + "@maxgraph/core": "0.10.0", + "@quasar/extras": "1.16.11", + "@vueuse/core": "10.9.0", + "axios": "1.6.8", + "codemirror": "6.0.1", + "dayjs": "1.11.10", + "echarts": "5.5.0", + "exceljs": "4.4.0", + "file-saver": "2.0.5", + "luckyexcel": "1.0.1", + "mockjs": "1.1.0", + "pinia": "2.1.7", + "platform-core": "8.1.245", + "quasar": "2.15.3", + "tailwindcss": "3.4.3", + "vue": "3.4.24", + "vue-dompurify-html": "5.0.1", + "vue-i18n": "9.13.1", + "vue-router": "4.3.2", + "@univerjs/core": "0.1.13", + "@univerjs/design": "0.1.13", + "@univerjs/docs": "0.1.13", + "@univerjs/docs-ui": "0.1.13", + "@univerjs/engine-formula": "0.1.13", + "@univerjs/engine-render": "0.1.13", + "@univerjs/facade": "0.1.13", + "@univerjs/sheets": "0.1.13", + "@univerjs/sheets-formula": "0.1.13", + "@univerjs/sheets-ui": "0.1.13", + "@univerjs/ui": "0.1.13" + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 334e9902..461ba7da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -38,7 +38,7 @@ application_version=1.0.0 platform_group=io.sc platform_version=8.1.44 platform_plugin_version=8.1.44 -platform_core_frontend_version=8.1.231 +platform_core_frontend_version=8.1.245 ########################################################### # dependencies version diff --git a/io.sc.engine.mv.frontend/package.json b/io.sc.engine.mv.frontend/package.json index e113fbd8..adde1af3 100644 --- a/io.sc.engine.mv.frontend/package.json +++ b/io.sc.engine.mv.frontend/package.json @@ -92,7 +92,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.231", + "platform-core": "8.1.245", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.engine.rule.frontend/package.json b/io.sc.engine.rule.frontend/package.json index 1a5ef341..7ff4026a 100644 --- a/io.sc.engine.rule.frontend/package.json +++ b/io.sc.engine.rule.frontend/package.json @@ -92,7 +92,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.244", + "platform-core": "8.1.245", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages.json b/io.sc.engine.rule.frontend/src/i18n/messages.json index 6033d76c..144c7616 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages.json @@ -20,6 +20,7 @@ "re.resources.grid.toolbar.deploy.online":"Online", "re.resources.grid.toolbar.deploy.online.tip":"Are you sure to on line the resource?", "re.resources.grid.toolbar.deploy.offline":"Offline", + "re.resources.grid.toolbar.deploy.offline.tip":"Are you sure to off line the resource?", "re.resources.grid.toolbar.importExample": "Import Example", "re.resources.grid.entity.effectiveDate": "Effective Date", @@ -98,6 +99,9 @@ "re.resources.designer.processor.grid.entity.rule": "Rule", "re.resources.designer.processor.grid.entity.singleRule": "Single Rule", + "re.resources.designer.processor.dialog.decisionTree.title": "Decision Tree Designer", + "re.resources.designer.processor.dialog.executionFlow.title": "Execution Flow Designer", + "re.resources.designer.testCase.grid.title": "Test Case List", "re.resources.designer.testCase.grid.entity.testResult": "Result", "re.resources.designer.testCase.grid.entity.lastTestDate": "Test Date", @@ -191,6 +195,10 @@ "re.migration.remove.action":"Remove", "re.migration.remove.action.tip":"Are you sure to remove the all data?", + "re.workflow.dialog.title":"Workflow Approving", + "re.workflow.dialog.tip":"Tip: workflow approving needed, the resource will be active after approved!", + "re.workflow.dialog.entity.treatment":"Treatment", + "re.workflow.task.grid.title":"Task List", "re.workflow.task.grid.toolbar.viewResource":"View Resource", "re.workflow.task.grid.toolbar.viewAttachment":"View Attachment", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json b/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json index 9cf20b5b..ec1c61e1 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages_tw_CN.json @@ -20,6 +20,7 @@ "re.resources.grid.toolbar.deploy.online":"上線", "re.resources.grid.toolbar.deploy.online.tip":"您確定要上線資源嗎?", "re.resources.grid.toolbar.deploy.offline":"下線", + "re.resources.grid.toolbar.deploy.offline.tip":"您確定要下線資源嗎?", "re.resources.grid.toolbar.importExample": "導入示例", "re.resources.grid.entity.effectiveDate": "生效日期", @@ -98,6 +99,9 @@ "re.resources.designer.processor.grid.entity.rule": "規則", "re.resources.designer.processor.grid.entity.singleRule": "單規則", + "re.resources.designer.processor.dialog.decisionTree.title": "決策樹設計器", + "re.resources.designer.processor.dialog.executionFlow.title": "執行流設計器", + "re.resources.designer.testCase.grid.title": "測試用例列表", "re.resources.designer.testCase.grid.entity.testResult": "測試結果", "re.resources.designer.testCase.grid.entity.lastTestDate": "測試日期", @@ -191,6 +195,10 @@ "re.migration.remove.action":"刪除數據", "re.migration.remove.action.tip":"您確定要刪除所有數據嗎?", + "re.workflow.dialog.title":"審批流程", + "re.workflow.dialog.tip":"提示: 該資源發佈需要流程審批, 待審批通過後方能生效!", + "re.workflow.dialog.entity.treatment":"說明", + "re.workflow.task.grid.title":"任務列表", "re.workflow.task.grid.toolbar.viewResource":"查看資源", "re.workflow.task.grid.toolbar.viewAttachment":"查看附件", diff --git a/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json b/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json index ed705694..6469dfb6 100644 --- a/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json +++ b/io.sc.engine.rule.frontend/src/i18n/messages_zh_CN.json @@ -20,6 +20,7 @@ "re.resources.grid.toolbar.deploy.online":"上线", "re.resources.grid.toolbar.deploy.online.tip":"您确定要上线资源吗?", "re.resources.grid.toolbar.deploy.offline":"下线", + "re.resources.grid.toolbar.deploy.offline.tip":"您确定要下线资源吗?", "re.resources.grid.toolbar.importExample": "导入示例", "re.resources.grid.entity.effectiveDate": "生效日期", @@ -98,6 +99,9 @@ "re.resources.designer.processor.grid.entity.rule": "规则", "re.resources.designer.processor.grid.entity.singleRule": "单规则", + "re.resources.designer.processor.dialog.decisionTree.title": "决策树设计器", + "re.resources.designer.processor.dialog.executionFlow.title": "执行流设计器", + "re.resources.designer.testCase.grid.title": "测试用例列表", "re.resources.designer.testCase.grid.entity.testResult": "测试结果", "re.resources.designer.testCase.grid.entity.lastTestDate": "测试日期", @@ -190,6 +194,10 @@ "re.migration.remove.subTitle":"请注意: 此操作将删除引擎中配置的所有数据, 且不可逆!", "re.migration.remove.action":"删除数据", "re.migration.remove.action.tip":"您确定要删除所有数据吗?", + + "re.workflow.dialog.title":"审批流程", + "re.workflow.dialog.tip":"提示: 该资源发布需要流程审批, 待审批通过后方能生效!", + "re.workflow.dialog.entity.treatment":"说明", "re.workflow.task.grid.title":"任务列表", "re.workflow.task.grid.toolbar.viewResource":"查看资源", diff --git a/io.sc.engine.rule.frontend/src/views/resources/AddAttachmentDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/AddAttachmentDialog.vue new file mode 100644 index 00000000..b6025a8a --- /dev/null +++ b/io.sc.engine.rule.frontend/src/views/resources/AddAttachmentDialog.vue @@ -0,0 +1,113 @@ + + diff --git a/io.sc.engine.rule.frontend/src/views/resources/AttachmentDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/AttachmentDialog.vue index 94a15397..8229b09e 100644 --- a/io.sc.engine.rule.frontend/src/views/resources/AttachmentDialog.vue +++ b/io.sc.engine.rule.frontend/src/views/resources/AttachmentDialog.vue @@ -3,18 +3,33 @@
+
diff --git a/io.sc.engine.rule.frontend/src/views/resources/designer/DecisionTreeDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/designer/DecisionTreeDialog.vue new file mode 100644 index 00000000..4e439869 --- /dev/null +++ b/io.sc.engine.rule.frontend/src/views/resources/designer/DecisionTreeDialog.vue @@ -0,0 +1,35 @@ + + diff --git a/io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue index e7d9a691..259e7dfa 100644 --- a/io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue +++ b/io.sc.engine.rule.frontend/src/views/resources/designer/DesignerDialog.vue @@ -62,7 +62,6 @@ no-caps inline-label align="left" - class="px-2" @update:model-value=" (value) => { if (value === 'testcase') { @@ -77,7 +76,7 @@ - + - + { +const open = (resource, readOnly) => { + readOnlyRef.value = Tools.isUndefinedOrNull(readOnly) ? false : readOnly; currentSelectedResourceRef.value = resource; dialogRef.value.show(); }; diff --git a/io.sc.engine.rule.frontend/src/views/resources/designer/ExecutionFlowDialog.vue b/io.sc.engine.rule.frontend/src/views/resources/designer/ExecutionFlowDialog.vue new file mode 100644 index 00000000..e47743e8 --- /dev/null +++ b/io.sc.engine.rule.frontend/src/views/resources/designer/ExecutionFlowDialog.vue @@ -0,0 +1,35 @@ + + diff --git a/io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue b/io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue index e5272249..01bdecb4 100644 --- a/io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue +++ b/io.sc.engine.rule.frontend/src/views/resources/designer/Parameter.vue @@ -1,343 +1,345 @@ diff --git a/io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue b/io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue index f195a362..ed1adbf4 100644 --- a/io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue +++ b/io.sc.engine.rule.frontend/src/views/workflow/Workflow.vue @@ -1,223 +1,277 @@ diff --git a/io.sc.engine.rule.server/build.gradle b/io.sc.engine.rule.server/build.gradle index 64c60157..f8ef635e 100644 --- a/io.sc.engine.rule.server/build.gradle +++ b/io.sc.engine.rule.server/build.gradle @@ -11,6 +11,10 @@ dependencies { project(":io.sc.engine.rule.client"), project(":io.sc.engine.rule.client.spring"), project(":io.sc.engine.rule.frontend"), + + project(":org.webjars.codemirror-5.37.0"), + project(":org.webjars.mxgraph-3.9.12"), + project(":org.webjars.jquery-1.12.4"), ) } diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/common/initializer/RuleEngineWorkFlowInitializer.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/common/initializer/RuleEngineWorkFlowInitializer.java new file mode 100644 index 00000000..fb101aac --- /dev/null +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/common/initializer/RuleEngineWorkFlowInitializer.java @@ -0,0 +1,72 @@ +package io.sc.engine.rule.server.common.initializer; + +import io.sc.engine.rule.server.service.RuleEngineServerConfigurationService; +import io.sc.platform.core.Environment; +import io.sc.platform.core.initializer.ApplicationInitializer; +import io.sc.platform.core.initializer.ApplicationInitializerExecuteException; +import io.sc.platform.core.util.FileUtil; +import io.sc.platform.flowable.enums.ProcessStatus; +import io.sc.platform.flowable.jpa.entity.ProcessEntity; +import io.sc.platform.flowable.service.ProcessEntityService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContext; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +public class RuleEngineWorkFlowInitializer implements ApplicationInitializer{ + private static final Logger log =LoggerFactory.getLogger(RuleEngineWorkFlowInitializer.class); + private Boolean isInitialized =null; + private ApplicationContext applicationContext; + private ProcessEntityService processEntityService; + + @Override + public void init(ApplicationContext applicationContext) { + this.processEntityService =applicationContext.getBean(ProcessEntityService.class); + this.applicationContext =applicationContext; + } + + @Override + public int getOrder() { + return 1100; + } + + @Override + public synchronized boolean isInitialized() { + if(isInitialized!=null) { + return isInitialized; + } + List entities =processEntityService.getRepository().findByKey(RuleEngineServerConfigurationService.RESOURCE_DEPLOY_WORKFLOW_KEY); + if(entities==null || entities.isEmpty()){ + isInitialized =false; + }else { + isInitialized = true; + } + return isInitialized; + } + + @Override + public void execute() throws ApplicationInitializerExecuteException { + try { + ProcessEntity entity = new ProcessEntity(); + entity.setCategory(RuleEngineServerConfigurationService.RESOURCE_DEPLOY_WORKFLOW_KEY); + entity.setKey(RuleEngineServerConfigurationService.RESOURCE_DEPLOY_WORKFLOW_KEY); + entity.setName(applicationContext.getMessage(RuleEngineServerConfigurationService.RESOURCE_DEPLOY_WORKFLOW_CATEGORY + "." + RuleEngineServerConfigurationService.RESOURCE_DEPLOY_WORKFLOW_KEY, null, Locale.getDefault())); + entity.setStatus(ProcessStatus.RELEASE); + entity.setVersion(1); + entity.setCanClaimTask(true); + entity.setXml(getRuleEngineWorkflowXmlContent()); + entity = processEntityService.add(entity); + processEntityService.deploy(entity.getId()); + }catch (Exception e){ + log.error("",e); + throw new ApplicationInitializerExecuteException(e); + } + } + + private String getRuleEngineWorkflowXmlContent() throws IOException { + return FileUtil.readString("classpath:/workflow/io/sc/engine/rule/Sample.bpmn", Environment.DEFAULT_CHARSET_NAME); + } +} diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/model/controller/ParameterProcessorWebController.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/model/controller/ParameterProcessorWebController.java index aedd5259..822c2321 100644 --- a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/model/controller/ParameterProcessorWebController.java +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/model/controller/ParameterProcessorWebController.java @@ -12,6 +12,7 @@ import io.sc.engine.rule.server.model.vo.ParameterProcessorVo; import io.sc.engine.rule.server.model.vo.ParameterValidatorVo; import io.sc.engine.rule.server.util.CodeAndNameMapping; import io.sc.engine.rule.server.util.VariableCodeAndNameReplacer; +import io.sc.platform.core.annotation.IgnoreResponseBodyAdvice; import io.sc.platform.mvc.controller.support.RestCrudController; import io.sc.platform.orm.util.EntityVoUtil; import org.springframework.beans.factory.annotation.Autowired; @@ -50,7 +51,7 @@ public class ParameterProcessorWebController extends RestCrudController attachments =attachmentCache.get(getAttachmentBussinessKey(releasableEntity)); - generateReleaseableResourceAttachmentHtml(releasableEntity,attachments); + if(attachments!=null && !attachments.isEmpty()){ + releasableEntity.setAttachmentCount(attachments.size()); + } } break; default: @@ -767,7 +783,10 @@ public class ResourceServiceImpl extends DaoServiceImpl attachments) { - if(entity!=null && attachments!=null && attachments.size()>0) { - StringBuilder sb =new StringBuilder(); - for(AttachmentVo attachment : attachments) { - sb.append("").append(attachment.getName()).append("").append("
"); - } - entity.setAttachmentHtml(sb.toString()); - } - } - - private void generateResourceTaskInfo(ReleasableResourceEntity entity, HistoricTaskInstance task) { - if(entity!=null && task!=null) { - entity.setTaskName(task.getName()); - entity.setTaskAssignee(task.getAssignee()); - } + return entity.getCode() + ":" + entity.getVersion(); } private void updateResourceAndRoleRelationship(String roleId,String resourceId) { diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/vo/ReleasableResourceVo.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/vo/ReleasableResourceVo.java index 760e4828..b3ebc818 100644 --- a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/vo/ReleasableResourceVo.java +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/resource/vo/ReleasableResourceVo.java @@ -21,9 +21,9 @@ public abstract class ReleasableResourceVo extends ResourceVo { //引入外部包 protected String imports; - //附件链接HTML文本 + //附件个数 @Transient - protected String attachmentHtml; + protected int attachmentCount; //审批任务节点名称 @Transient @@ -65,12 +65,12 @@ public abstract class ReleasableResourceVo extends ResourceVo { this.imports = imports; } - public String getAttachmentHtml() { - return attachmentHtml; + public int getAttachmentCount() { + return attachmentCount; } - public void setAttachmentHtml(String attachmentHtml) { - this.attachmentHtml = attachmentHtml; + public void setAttachmentCount(int attachmentCount) { + this.attachmentCount = attachmentCount; } public String getTaskName() { diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/controller/ResourceDeployApprovingController.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/controller/ResourceDeployApprovingController.java index aeb59854..265477f2 100644 --- a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/controller/ResourceDeployApprovingController.java +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/controller/ResourceDeployApprovingController.java @@ -15,8 +15,6 @@ import java.util.List; /** * 资源管理器 Controller - * @author wangshaoping - * */ @Controller @RequestMapping("/api/re/resource/workflow") diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/impl/RuleEngineWorkflowServiceImpl.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/impl/RuleEngineWorkflowServiceImpl.java index c99bfda5..a259b6be 100644 --- a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/impl/RuleEngineWorkflowServiceImpl.java +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/impl/RuleEngineWorkflowServiceImpl.java @@ -130,7 +130,7 @@ public class RuleEngineWorkflowServiceImpl implements RuleEngineWorkflowService{ wrapper.setResourceName(entity.getName()); wrapper.setResourceVersion(entity.getVersion()); wrapper.setResourceStatus(entity.getStatus()); - wrapper.setAttachments(generateAttachmentsHtml(entity)); + wrapper.setAttachmentCount(getAttachmentCount(entity)); wrappers.add(wrapper); } } @@ -152,7 +152,6 @@ public class RuleEngineWorkflowServiceImpl implements RuleEngineWorkflowService{ List wrappers =new ArrayList(); for(HistoricTaskInstance task : historyTasks){ if(task.getEndTime()!=null) { - RuleEngineApprovingHistoryTaskWrapper wrapper =new RuleEngineApprovingHistoryTaskWrapper(); wrapper.setProcessDefinitionId(task.getProcessDefinitionId()); wrapper.setProcessInstanceId(task.getProcessInstanceId()); @@ -164,7 +163,6 @@ public class RuleEngineWorkflowServiceImpl implements RuleEngineWorkflowService{ wrapper.setTaskClaimTime(task.getClaimTime()); wrapper.setTaskEndTime(task.getEndTime()); wrapper.setTaskTreatment(getTaskTreatment(task.getId(),"
")); - wrappers.add(wrapper); } } @@ -186,20 +184,14 @@ public class RuleEngineWorkflowServiceImpl implements RuleEngineWorkflowService{ } } - private String generateAttachmentsHtml(ReleasableResourceEntity entity) { + private int getAttachmentCount(ReleasableResourceEntity entity) { if(entity!=null) { - String code =entity.getCode(); - Integer version =entity.getVersion(); - List attachments =attachmentService.findByBussinessKey(code + ":" + version); + List attachments =attachmentService.findByBussinessKey(entity.getId()); if(attachments!=null && attachments.size()>0) { - StringBuilder sb =new StringBuilder(); - for(AttachmentVo attachment : attachments) { - sb.append("").append(attachment.getName()).append("").append("
"); - } - return sb.toString(); + return attachments.size(); } } - return null; + return 0; } private String getTaskTreatment(String taskId,String replaceString) { @@ -210,8 +202,7 @@ public class RuleEngineWorkflowServiceImpl implements RuleEngineWorkflowService{ for(Comment comment : comments) { sb.append(comment.getFullMessage()).append("\n"); } - String result =sb.toString(); - return result.replace("\r", "").replace("\n", replaceString); + return sb.toString(); } } return null; diff --git a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/support/RuleEngineApprovingTaskWrapper.java b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/support/RuleEngineApprovingTaskWrapper.java index 3cf910f2..c227c84b 100644 --- a/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/support/RuleEngineApprovingTaskWrapper.java +++ b/io.sc.engine.rule.server/src/main/java/io/sc/engine/rule/server/workflow/service/support/RuleEngineApprovingTaskWrapper.java @@ -21,7 +21,7 @@ public class RuleEngineApprovingTaskWrapper { private String resourceName; //资源名称 private Integer resourceVersion; //资源版本 private DeployStatus resourceStatus; //资源状态 - private String attachments; //附件链接 + private int attachmentCount; //附件个数 public String getProcessDefinitionId() { return processDefinitionId; @@ -107,10 +107,6 @@ public class RuleEngineApprovingTaskWrapper { public void setResourceStatus(DeployStatus resourceStatus) { this.resourceStatus = resourceStatus; } - public String getAttachments() { - return attachments; - } - public void setAttachments(String attachments) { - this.attachments = attachments; - } + public int getAttachmentCount() { return attachmentCount; } + public void setAttachmentCount(int attachmentCount) { this.attachmentCount = attachmentCount; } } diff --git a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/liquibase.json b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/liquibase.json index 093672c0..b2524d1a 100644 --- a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/liquibase.json +++ b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/liquibase.json @@ -4,7 +4,8 @@ "order" : 11000, "description":"决策引擎表结构", "locations":[ - "classpath:/liquibase/RE_1.0.0_20220515__Rule Engine Database Schema DDL.xml" + "classpath:/liquibase/RE_1.0.0_20220515__Rule Engine Database Schema DDL.xml", + "classpath:/liquibase/RE_1.0.0_20220515__Rule Engine Database Data.xml" ] } ] \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/messages.json b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/messages.json index 21578f2d..1271731e 100644 --- a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/messages.json +++ b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/messages.json @@ -10,24 +10,11 @@ { "includes":[ + "io/sc/engine/rule/server/i18n/other", + "io/sc/engine/rule/server/i18n/parameters", + "io/sc/engine/rule/server/i18n/workflow", "io/sc/engine/rule/server/sample/i18n/messages", - "org/wsp/engine/rule/server/i18n/enums", - "org/wsp/engine/rule/server/i18n/example", - "org/wsp/engine/rule/server/i18n/exception", - "org/wsp/engine/rule/server/i18n/menu", - "org/wsp/engine/rule/server/i18n/parameter", - - "org/wsp/engine/rule/server/i18n/messages", - "io/sc/engine/rule/server/i18n/dictionary/messages", - "org/wsp/engine/rule/server/i18n/graph/graph_editor", - "org/wsp/engine/rule/server/i18n/lib/messages", - "org/wsp/engine/rule/server/i18n/model/messages", - "org/wsp/engine/rule/server/i18n/resource/messages", - "org/wsp/engine/rule/server/i18n/scorecard/messages", - "org/wsp/engine/rule/server/i18n/testcase/messages", - "org/wsp/engine/rule/server/i18n/migration/messages", - - "org/wsp/engine/rule/server/i18n/workflow/messages" + "io/sc/engine/rule/server/i18n/dictionary/messages" ], "excludes":[] } diff --git a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameter.json b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameters.json similarity index 61% rename from io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameter.json rename to io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameters.json index baa28dca..53fca44f 100644 --- a/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameter.json +++ b/io.sc.engine.rule.server/src/main/resources/META-INF/platform/plugins/parameters.json @@ -12,30 +12,18 @@ [ //决策引擎服务器 {"id":"parameter.re.server","order":1000}, - //决策引擎服务器/发布时是否需要审批流程 + //决策引擎服务器/模型发布前是否需要审批流程 { "id" :"parameter.re.server.deploy.approve.workflow.enable", "parentId" :"parameter.re.server", "code" :"parameter.re.server.deploy.approve.workflow.enable", "defaultValue" :"false", "options" :{ - "false" : "#{no}", - "true" : "#{yes}" + "true" : "parameter.re.server.deploy.approve.workflow.enable.true", + "false" : "parameter.re.server.deploy.approve.workflow.enable.false" }, "order" : 0 }, - //决策引擎服务器/是否需要上传附件 - { - "id" :"parameter.re.server.attachment.enable", - "parentId" :"parameter.re.server", - "code" :"parameter.re.server.attachment.enable", - "defaultValue" :"true", - "options" :{ - "false" : "#{no}", - "true" : "#{yes}" - }, - "order" : 50 - }, //模型引擎服务器/当模型代码为空时,返回模型定义的策略 { "id" :"parameter.re.server.modelDefine.nullVersion.strategy", @@ -43,8 +31,8 @@ "code" :"parameter.re.server.modelDefine.nullVersion.strategy", "defaultValue" :"last", "options" :{ - "last_deploy" : "#{parameter.re.server.modelDefine.nullVersion.strategy.last_deploy}", - "last" : "#{parameter.re.server.modelDefine.nullVersion.strategy.last}" + "last_deploy" : "parameter.re.server.modelDefine.nullVersion.strategy.last_deploy", + "last" : "parameter.re.server.modelDefine.nullVersion.strategy.last" }, "order" : 100 } diff --git a/io.sc.engine.rule.server/src/main/resources/META-INF/services/io.sc.platform.core.initializer.ApplicationInitializer b/io.sc.engine.rule.server/src/main/resources/META-INF/services/io.sc.platform.core.initializer.ApplicationInitializer new file mode 100644 index 00000000..25946280 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/META-INF/services/io.sc.platform.core.initializer.ApplicationInitializer @@ -0,0 +1 @@ +io.sc.engine.rule.server.common.initializer.RuleEngineWorkFlowInitializer \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other.properties new file mode 100644 index 00000000..ffe1dffd --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other.properties @@ -0,0 +1,43 @@ +# \u7EC4\u4EF6\u9762\u677F\u6807\u9898 +mxgraph.re.editor.Palette.title=Available + +# \u5F00\u59CB\u7EC4\u4EF6 +mxgraph.re.editor.component.start.title=Start + +# \u6761\u4EF6\u5224\u65AD\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.title=Condition +mxgraph.re.editor.component.condition.entity.condition=Condition + +# \u5E76\u53D1\u7EC4\u4EF6 +mxgraph.re.editor.component.parallel.title=Paralleler + +# \u6761\u4EF6\u5206\u503C\u8FDE\u7EBF\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.edge.conditionValue=Condition Value +mxgraph.re.editor.component.condition.edge.type=Value Type +mxgraph.re.editor.component.condition.edge.type.string=String +mxgraph.re.editor.component.condition.edge.type.number=Number +mxgraph.re.editor.component.condition.edge.type.boolean=Boolean + +# \u8868\u8FBE\u5F0F\u7EC4\u4EF6 +mxgraph.re.editor.component.expression.title=Expression +mxgraph.re.editor.component.expression.entity.expression=Expression +mxgraph.re.editor.component.expression.entity.commands=Commands + +# \u6307\u4EE4\u96C6\u7EC4\u4EF6 +mxgraph.re.editor.component.commandSet.title=Command Set +mxgraph.re.editor.component.commandSet.entity.commands=Command Set + +# \u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.resourceAbstract.title=Resource +mxgraph.re.editor.component.resourceAbstract.entity.resource=Resource + +# \u53EF\u914D\u7F6E\u8F93\u5165\u8F93\u51FA\u6307\u4EE4\u7684\u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.configurableResourceAbstract.title=Configurable Resource +mxgraph.re.editor.component.configurableResourceAbstract.entity.resource=Resource +mxgraph.re.editor.component.configurableResourceAbstract.entity.inputCommands=Input Command Set +mxgraph.re.editor.component.configurableResourceAbstract.entity.outputCommands=Output Command Set + +# \u6A21\u578B\u7EC4\u4EF6 +mxgraph.re.editor.component.submodel.title=Model +mxgraph.re.editor.component.submodel.entity.model=Model + diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_tw_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_tw_CN.properties new file mode 100644 index 00000000..3c02a46e --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_tw_CN.properties @@ -0,0 +1,42 @@ +# \u7EC4\u4EF6\u9762\u677F\u6807\u9898 +mxgraph.re.editor.Palette.title=\u53EF\u7528\u7EC4\u4EF6 + +# \u5F00\u59CB\u7EC4\u4EF6 +mxgraph.re.editor.component.start.title=\u5F00\u59CB + +# \u6761\u4EF6\u5224\u65AD\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.title=\u6761\u4EF6\u5224\u65AD +mxgraph.re.editor.component.condition.entity.condition=\u6761\u4EF6 + +# \u5E76\u53D1\u7EC4\u4EF6 +mxgraph.re.editor.component.parallel.title=\u5E76\u53D1\u5668 + +# \u6761\u4EF6\u5206\u503C\u8FDE\u7EBF\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.edge.conditionValue=\u6761\u4EF6\u503C +mxgraph.re.editor.component.condition.edge.type=\u503C\u7C7B\u578B +mxgraph.re.editor.component.condition.edge.type.string=\u5B57\u7B26\u4E32 +mxgraph.re.editor.component.condition.edge.type.number=\u6570\u5B57 +mxgraph.re.editor.component.condition.edge.type.boolean=\u5E03\u5C14 + +# \u8868\u8FBE\u5F0F\u7EC4\u4EF6 +mxgraph.re.editor.component.expression.title=\u8868\u8FBE\u5F0F +mxgraph.re.editor.component.expression.entity.expression=\u8868\u8FBE\u5F0F +mxgraph.re.editor.component.expression.entity.commands=\u9644\u52A0\u6307\u4EE4\u96C6 + +# \u6307\u4EE4\u96C6\u7EC4\u4EF6 +mxgraph.re.editor.component.commandSet.title=\u6307\u4EE4\u96C6 +mxgraph.re.editor.component.commandSet.entity.commands=\u6307\u4EE4\u96C6 + +# \u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.resourceAbstract.title=\u8D44\u6E90 +mxgraph.re.editor.component.resourceAbstract.entity.resource=\u8D44\u6E90 + +# \u53EF\u914D\u7F6E\u8F93\u5165\u8F93\u51FA\u6307\u4EE4\u7684\u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.configurableResourceAbstract.title=\u8F93\u5165\u8F93\u51FA\u6307\u4EE4\u8D44\u6E90 +mxgraph.re.editor.component.configurableResourceAbstract.entity.resource=\u8D44\u6E90 +mxgraph.re.editor.component.configurableResourceAbstract.entity.inputCommands=\u8F93\u5165\u6307\u4EE4\u96C6 +mxgraph.re.editor.component.configurableResourceAbstract.entity.outputCommands=\u8F93\u51FA\u6307\u4EE4\u96C6 + +# \u6A21\u578B\u7EC4\u4EF6 +mxgraph.re.editor.component.submodel.title=\u5B50\u6A21\u578B +mxgraph.re.editor.component.submodel.entity.model=\u5B50\u6A21\u578B \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_zh_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_zh_CN.properties new file mode 100644 index 00000000..3c02a46e --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/other_zh_CN.properties @@ -0,0 +1,42 @@ +# \u7EC4\u4EF6\u9762\u677F\u6807\u9898 +mxgraph.re.editor.Palette.title=\u53EF\u7528\u7EC4\u4EF6 + +# \u5F00\u59CB\u7EC4\u4EF6 +mxgraph.re.editor.component.start.title=\u5F00\u59CB + +# \u6761\u4EF6\u5224\u65AD\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.title=\u6761\u4EF6\u5224\u65AD +mxgraph.re.editor.component.condition.entity.condition=\u6761\u4EF6 + +# \u5E76\u53D1\u7EC4\u4EF6 +mxgraph.re.editor.component.parallel.title=\u5E76\u53D1\u5668 + +# \u6761\u4EF6\u5206\u503C\u8FDE\u7EBF\u7EC4\u4EF6 +mxgraph.re.editor.component.condition.edge.conditionValue=\u6761\u4EF6\u503C +mxgraph.re.editor.component.condition.edge.type=\u503C\u7C7B\u578B +mxgraph.re.editor.component.condition.edge.type.string=\u5B57\u7B26\u4E32 +mxgraph.re.editor.component.condition.edge.type.number=\u6570\u5B57 +mxgraph.re.editor.component.condition.edge.type.boolean=\u5E03\u5C14 + +# \u8868\u8FBE\u5F0F\u7EC4\u4EF6 +mxgraph.re.editor.component.expression.title=\u8868\u8FBE\u5F0F +mxgraph.re.editor.component.expression.entity.expression=\u8868\u8FBE\u5F0F +mxgraph.re.editor.component.expression.entity.commands=\u9644\u52A0\u6307\u4EE4\u96C6 + +# \u6307\u4EE4\u96C6\u7EC4\u4EF6 +mxgraph.re.editor.component.commandSet.title=\u6307\u4EE4\u96C6 +mxgraph.re.editor.component.commandSet.entity.commands=\u6307\u4EE4\u96C6 + +# \u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.resourceAbstract.title=\u8D44\u6E90 +mxgraph.re.editor.component.resourceAbstract.entity.resource=\u8D44\u6E90 + +# \u53EF\u914D\u7F6E\u8F93\u5165\u8F93\u51FA\u6307\u4EE4\u7684\u8D44\u6E90\u6458\u8981\u4FE1\u606F\u7EC4\u4EF6 +mxgraph.re.editor.component.configurableResourceAbstract.title=\u8F93\u5165\u8F93\u51FA\u6307\u4EE4\u8D44\u6E90 +mxgraph.re.editor.component.configurableResourceAbstract.entity.resource=\u8D44\u6E90 +mxgraph.re.editor.component.configurableResourceAbstract.entity.inputCommands=\u8F93\u5165\u6307\u4EE4\u96C6 +mxgraph.re.editor.component.configurableResourceAbstract.entity.outputCommands=\u8F93\u51FA\u6307\u4EE4\u96C6 + +# \u6A21\u578B\u7EC4\u4EF6 +mxgraph.re.editor.component.submodel.title=\u5B50\u6A21\u578B +mxgraph.re.editor.component.submodel.entity.model=\u5B50\u6A21\u578B \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter.properties deleted file mode 100644 index f9ff1a51..00000000 --- a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter.properties +++ /dev/null @@ -1,9 +0,0 @@ -#================================================ -# \u6700\u65b0\u6a21\u578b\u5b9a\u4e49\u52a0\u8f7d\u7b56\u7565\u7cfb\u7edf\u914d\u7f6e\u53c2\u6570\u9009\u9879 -#================================================ -parameter.re.server=\u51b3\u7b56\u5f15\u64ce(\u670d\u52a1\u5668) -parameter.re.server.deploy.approve.workflow.enable=Enable Model Deploy Work Flow -parameter.re.server.attachment.enable=Enable Upload Attachment -parameter.re.server.modelDefine.nullVersion.strategy=Load Model Define Strategy When Version is Empty -parameter.re.server.modelDefine.nullVersion.strategy.last_deploy=Last (Deployed) -parameter.re.server.modelDefine.nullVersion.strategy.last=Last \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter_zh_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter_zh_CN.properties deleted file mode 100644 index 8a154cc6..00000000 --- a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameter_zh_CN.properties +++ /dev/null @@ -1,9 +0,0 @@ -#================================================ -# \u6700\u65b0\u6a21\u578b\u5b9a\u4e49\u52a0\u8f7d\u7b56\u7565\u7cfb\u7edf\u914d\u7f6e\u53c2\u6570\u9009\u9879 -#================================================ -parameter.re.server=\u51b3\u7b56\u5f15\u64ce(\u670d\u52a1\u5668) -parameter.re.server.deploy.approve.workflow.enable=\u662f\u5426\u5f00\u542f\u6a21\u578b\u53d1\u5e03\u5ba1\u6279\u6d41\u7a0b -parameter.re.server.attachment.enable=\u662f\u5426\u5141\u8bb8\u4e0a\u4f20\u9644\u4ef6 -parameter.re.server.modelDefine.nullVersion.strategy=\u5f53\u6a21\u578b\u7248\u672c\u4e3a\u7a7a\u65f6,\u52a0\u8f7d\u6a21\u578b\u5b9a\u4e49\u7684\u7b56\u7565 -parameter.re.server.modelDefine.nullVersion.strategy.last_deploy=\u6700\u65b0(\u5df2\u53d1\u5e03) -parameter.re.server.modelDefine.nullVersion.strategy.last=\u6700\u65b0 \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters.properties new file mode 100644 index 00000000..13b021da --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters.properties @@ -0,0 +1,9 @@ +parameter.re.server=Rule Engine + +parameter.re.server.deploy.approve.workflow.enable=Workflow needed before rule online +parameter.re.server.deploy.approve.workflow.enable.true=Yes +parameter.re.server.deploy.approve.workflow.enable.false=No + +parameter.re.server.modelDefine.nullVersion.strategy=Load Model Define Strategy When Version is Empty +parameter.re.server.modelDefine.nullVersion.strategy.last_deploy=Last (Deployed) +parameter.re.server.modelDefine.nullVersion.strategy.last=Last \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_tw_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_tw_CN.properties new file mode 100644 index 00000000..5af8bfaf --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_tw_CN.properties @@ -0,0 +1,9 @@ +parameter.re.server=\u6C7A\u7B56\u5F15\u64CE(\u670D\u52D9\u5668) + +parameter.re.server.deploy.approve.workflow.enable=\u6A21\u578B\u767C\u4F48\u524D\u662F\u5426\u9700\u8981\u6D41\u7A0B\u5BE9\u6279 +parameter.re.server.deploy.approve.workflow.enable.true=\u662F +parameter.re.server.deploy.approve.workflow.enable.false=\u5426 + +parameter.re.server.modelDefine.nullVersion.strategy=\u7576\u6A21\u578B\u7248\u672C\u70BA\u7A7A\u6642,\u52A0\u8F09\u6A21\u578B\u5B9A\u7FA9\u7684\u7B56\u7565 +parameter.re.server.modelDefine.nullVersion.strategy.last_deploy=\u6700\u65B0(\u5DF2\u767C\u4F48) +parameter.re.server.modelDefine.nullVersion.strategy.last=\u6700\u65B0 \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_zh_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_zh_CN.properties new file mode 100644 index 00000000..bd1f7a57 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/parameters_zh_CN.properties @@ -0,0 +1,9 @@ +parameter.re.server=\u51B3\u7B56\u5F15\u64CE(\u670D\u52A1\u5668) + +parameter.re.server.deploy.approve.workflow.enable=\u6A21\u578B\u53D1\u5E03\u524D\u662F\u5426\u9700\u8981\u6D41\u7A0B\u5BA1\u6279 +parameter.re.server.deploy.approve.workflow.enable.true=\u662F +parameter.re.server.deploy.approve.workflow.enable.false=\u5426 + +parameter.re.server.modelDefine.nullVersion.strategy=\u5F53\u6A21\u578B\u7248\u672C\u4E3A\u7A7A\u65F6,\u52A0\u8F7D\u6A21\u578B\u5B9A\u4E49\u7684\u7B56\u7565 +parameter.re.server.modelDefine.nullVersion.strategy.last_deploy=\u6700\u65B0(\u5DF2\u53D1\u5E03) +parameter.re.server.modelDefine.nullVersion.strategy.last=\u6700\u65B0 \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow.properties new file mode 100644 index 00000000..314c1494 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow.properties @@ -0,0 +1 @@ +WORK_FLOW.RULE_ENGINE_APPROVING=Rule Engine Workflow \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_tw_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_tw_CN.properties new file mode 100644 index 00000000..299bc061 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_tw_CN.properties @@ -0,0 +1 @@ +WORK_FLOW.RULE_ENGINE_APPROVING=\u6C7A\u7B56\u5F15\u64CE\u5BE9\u6279\u6D41\u7A0B \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_zh_CN.properties b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_zh_CN.properties new file mode 100644 index 00000000..0d48e299 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/io/sc/engine/rule/server/i18n/workflow_zh_CN.properties @@ -0,0 +1 @@ +WORK_FLOW.RULE_ENGINE_APPROVING=\u51B3\u7B56\u5F15\u64CE\u5BA1\u6279\u6D41\u7A0B \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/liquibase/RE_1.0.0_20220515__Rule Engine Database Data.xml b/io.sc.engine.rule.server/src/main/resources/liquibase/RE_1.0.0_20220515__Rule Engine Database Data.xml new file mode 100644 index 00000000..7a9d01b2 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/liquibase/RE_1.0.0_20220515__Rule Engine Database Data.xml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/io.sc.engine.rule.server/src/main/resources/liquibase/io/sc/engine/rule/sys_dictionary.csv b/io.sc.engine.rule.server/src/main/resources/liquibase/io/sc/engine/rule/sys_dictionary.csv new file mode 100644 index 00000000..6fad2ea8 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/liquibase/io/sc/engine/rule/sys_dictionary.csv @@ -0,0 +1,6 @@ +"SYS_DICTIONARY",,,,,,,,,, +"ID_","CODE_","VALUE_","ORDER_","JPA_VERSION_","DATA_COME_FROM_","CREATOR_","CREATE_DATE_","LAST_MODIFIER_","LAST_MODIFYDATE_","CORP_CODE_" +"ID","","","排序","JPA乐观锁版本","","创建人","创建日期","最后修改人","最后修改日期","" +"VARCHAR","VARCHAR","VARCHAR","INTEGER","INTEGER","VARCHAR","VARCHAR","TIMESTAMP","VARCHAR","TIMESTAMP","VARCHAR" +"java.lang.String","java.lang.String","java.lang.String","java.lang.Integer","java.lang.Integer","java.lang.String","java.lang.String","java.sql.Timestamp","java.lang.String","java.sql.Timestamp","java.lang.String" +"debe6a2c-da11-46f9-a66d-28481e1da319","WORK_FLOW","RULE_ENGINE_APPROVING","2",,"INPUT","system","2024-03-11 12:50:35.0","system","2024-03-11 12:50:35.0","_PRIMARY_" \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/css/alert.css b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/css/alert.css new file mode 100644 index 00000000..65b22227 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/css/alert.css @@ -0,0 +1,130 @@ +/*为手机端定制的一款皮肤(引用此皮肤时无需引用alert.css) 最后更新2016-07-27*/ +/* +alert_overlay 背景遮罩 +alert_msg 消息框主体 +alert_content 内容容器 +alert_buttons 底部按钮容器 +alert_btn 两个按钮公用class +alert_btn_ok 确定按钮 +alert_btn_cancel 取消按钮 +*/ +.alert_overlay { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + z-index: 1000; + background: rgba(0, 0, 0, .1) +} + +.alert_msg { + position: fixed; + width: 280px; + left: 50%; + margin-left: -140px; + top: 20%; + z-index: 1000; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: 0 0 15px rgba(0, 0, 0, .3); + background: #fff; + animation: alertshow .2s ease +} + +.alert_content { + padding: 20px; + font-size: 14px; + text-align: left +} + +.alert_buttons { + text-align: center; + border-top: 1px solid #ccc; + -webkit-user-select: none +} + +.alert_buttons .alert_btn { + display: inline-block; + width: 50%; + border: none; + height: 45px; + line-height: 45px; + font-size: 14px; + outline: 0; + -webkit-appearance: none; + background: #fff; + -webkit-tap-highlight-color: transparent; + border-radius: 0 0 4px 4px; +} + +.alert_buttons .alert_btn:only-child { + width: 100% +} + +.alert_buttons .alert_btn:first-child + .alert_btn { + border-left: 1px solid #ccc; + border-radius: 0 0 4px 0; +} + +.alert_tips { + position: fixed; + z-index: 10176523; + width: 100%; + top: 55%; + pointer-events: none; + text-align: center; +} + +.alert_tips div { + box-siziong: border-box; + display: inline-block; + padding: 15px; + border-radius: 10px; + background: rgba(0, 0, 0, .7); + min-width: 50px; + max-width: 230px; + text-align: center; + color: #fff; + animation: tipsshow 3s .01s ease; + opacity: 0; +} + +@keyframes alertshow { + 0% { + opacity: 0; + transform: scale(.5) + } + + 70% { + opacity: .7; + transform: scale(1.05) + } + + 100% { + opacity: 1; + transform: scale(1) + } +} + +@keyframes tipsshow { + 0% { + opacity: 0; + transform: scale(1.4) rotateX(-360deg) + } + + 20% { + opacity: 1; + transform: scale(1) rotateX(0deg) + } + + 80% { + opacity: 1; + transform: scale(1) rotateX(0deg) + } + + 100% { + opacity: 0; + transform: scale(1.4) rotateX(360deg) + } +} \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/save.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/save.png new file mode 100644 index 00000000..54f7163c Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/save.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/xml.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/xml.png new file mode 100644 index 00000000..938a4f99 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/images/xml.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.js new file mode 100644 index 00000000..befb65d2 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.js @@ -0,0 +1,59 @@ +/** + * jQuery/Zepto 弹窗插件 + * 调用方法(中括号的为可选参数): + * $.alert(string[,function]) + * $.confirm(string[,function]) + * $.tips(string[,number]) + * version:2016-11-30 + */ +!function($){ + //所有的css,可以自定义,css和Dom结构请参考 https://github.com/ydq/alert + var css='.alert_overlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1000;background:rgba(0,0,0,.05);-webkit-backdrop-filter:blur(3px)}.pc .alert_msg{width:320px}.mob .alert_msg{width:260px;border-radius:4px}.alert_msg{box-sizing:border-box;position:absolute;left:50%;top:30%;border:1px solid #ccc;box-shadow:0 2px 15px rgba(0,0,0,.3);background:#fff;transition:all .2s cubic-bezier(.8,.5,.2,1.4);-webkit-transform:translate(-50%,-50%) scale(.5);opacity:0;transform:translate(-50%,-50%) scale(.5)}.alert_show .alert_msg{opacity:1;transform:translate(-50%,-50%) scale(1);-webkit-transform:translate(-50%,-50%) scale(1)}.alert_content{padding:20px 15px;font-size:14px;text-align:left}.alert_tips{position:fixed;z-index:10176523}.pc .alert_buttons{padding:6px;border-top:1px solid #ccc;text-align:right;box-shadow:0 1px 0 #fff inset;background:#eee;-webkit-user-select:none}.pc .alert_buttons .alert_btn{padding:4px 8px;margin:0 2px;border:1px solid #ccc;background:#eee;cursor:pointer;border-radius:2px;font-size:14px;outline:0;-webkit-appearance:none}.pc .alert_buttons .alert_btn:hover{border-color:#ccc;box-shadow:0 1px 2px #ccc;background:#eaeaea}.pc .alert_buttons .alert_btn:active{box-shadow:0 1px 2px #ccc inset;background:#e6e6e6}.pc.alert_tips{top:50px;right:50px}.pc.alert_tips div{background:rgba(0,0,0,.7);position:relative;color:#fff;font-size:16px;padding:10px 15px;border-radius:2px;margin-bottom:20px;box-shadow:0 0 3px #000;display:none;float:right;clear:both}.mob .alert_buttons{text-align:center;border-top:1px solid #ccc;-webkit-user-select:none}.mob .alert_buttons .alert_btn{display:inline-block;width:50%;border:0;height:40px;font-size:14px;outline:0;-webkit-appearance:none;background:#fff;-webkit-tap-highlight-color:transparent;border-radius:0 0 4px 4px}.mob .alert_buttons .alert_btn:only-child{width:100%}.mob .alert_buttons .alert_btn:first-child+.alert_btn{border-left:1px solid #ccc;border-radius:0 0 4px 0}.mob.alert_tips{width:100%;top:55%;pointer-events:none;text-align:center}.mob.alert_tips div{box-sizing:border-box;display:inline-block;padding:15px;border-radius:10px;background:rgba(0,0,0,.7);min-width:50px;max-width:230px;text-align:center;color:#fff;animation:tipsshow 3s .01s ease;-webkit-animation:tipsshow 3s .01s ease;opacity:0}@keyframes tipsshow{0%{opacity:0;transform:scale(1.4) rotateX(-360deg)}20%,80%{opacity:1;transform:scale(1) rotateX(0deg)}to{transform:scale(1.4) rotateX(360deg)}}@-webkit-keyframes tipsshow{0%,to{opacity:0}0%{-webkit-transform:scale(1.4) rotateX(-360deg)}20%,80%{opacity:1;-webkit-transform:scale(1) rotateX(0deg)}to{opacity:0;-webkit-transform:scale(1.4) rotateX(360deg)}}'; + $('head').append(''); + $._ismob=/i(Phone|Pod)|Android|phone/i.test(navigator.userAgent) + $._isalert=$._isload=0 + $.alert=function(){ + if(arguments.length){ + $._isalert=1; + return $.confirm.apply($,arguments); + } + } + $.confirm=function(){ + var args=arguments,d; + if(args.length){ + var fn=args[1],_click = function(e){typeof fn=='function'?(fn.call(d,e.data.r)!=!1&&d.close()):d.close()}; + d = $('
'+args[0]+'
') + .on('contextmenu',!1) + .on('click','.alert_btn_ok',{r:!0},_click) + .on('click','.alert_btn_cancel',{r:!1},_click) + $._isload?d.find('.alert_content').css('text-align','center').parent().css({width:'auto',borderRadius:'4px'}).find('.alert_buttons').remove():($._isalert&&d.find('.alert_btn_cancel').remove()) + d.appendTo('body').find('.alert_btn_ok').focus();//让对话框打开后支持直接键盘回车触发确定按钮点击 + d.ok = function(t){d.find('.alert_btn_ok').text(t||'Okey');return d} + d.cancel = function(t){d.find('.alert_btn_cancel').text(t||'Cancel');return d} + d.content = function(t){t&&d.find('.alert_content').html(t);return d} + d.close = function(){d.one('webkitTransitionEnd transitionEnd',function(){d.remove();}).removeClass('alert_show')} + d.addClass('alert_show') + } + $._isalert=$._isload=0; + return d; + }, + $.tips=function(m,t){ + if(m){ + if($._ismob){ + $('.alert_tips').remove(); + $('
'+m+'
').appendTo('body').one('webkitAnimationEnd animationEnd',function(){$(this).remove()}) + }else{ + var tipsContainer = $('.alert_tips'); + tipsContainer.length||(tipsContainer=$('
').appendTo('body')); + $('
'+m+'
').appendTo(tipsContainer).fadeIn('fast').delay(t||2e3).slideUp('fast',function(){$(this).remove();}); + } + } + } + $.load=function(){ + $('.alert_overlay').remove(); + $._isload =1; + var d = $.confirm.call($,arguments[0]||"Loading..."); + $.loaded = d.close; + return d; + } +}($) diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.min.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.min.js new file mode 100644 index 00000000..8a1429f9 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor-ext/js/alert.min.js @@ -0,0 +1,2 @@ +/*jQuery/Zepto弹窗插件 | version:2016-11-30*/ +!function(t){var e=".alert_overlay{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1000;background:rgba(0,0,0,.05);-webkit-backdrop-filter:blur(3px)}.pc .alert_msg{width:320px}.mob .alert_msg{width:260px;border-radius:4px}.alert_msg{box-sizing:border-box;position:absolute;left:50%;top:30%;border:1px solid #ccc;box-shadow:0 2px 15px rgba(0,0,0,.3);background:#fff;transition:all .2s cubic-bezier(.8,.5,.2,1.4);-webkit-transform:translate(-50%,-50%) scale(.5);opacity:0;transform:translate(-50%,-50%) scale(.5)}.alert_show .alert_msg{opacity:1;transform:translate(-50%,-50%) scale(1);-webkit-transform:translate(-50%,-50%) scale(1)}.alert_content{padding:20px 15px;font-size:14px;text-align:left}.alert_tips{position:fixed;z-index:10176523}.pc .alert_buttons{padding:6px;border-top:1px solid #ccc;text-align:right;box-shadow:0 1px 0 #fff inset;background:#eee;-webkit-user-select:none}.pc .alert_buttons .alert_btn{padding:4px 8px;margin:0 2px;border:1px solid #ccc;background:#eee;cursor:pointer;border-radius:2px;font-size:14px;outline:0;-webkit-appearance:none}.pc .alert_buttons .alert_btn:hover{border-color:#ccc;box-shadow:0 1px 2px #ccc;background:#eaeaea}.pc .alert_buttons .alert_btn:active{box-shadow:0 1px 2px #ccc inset;background:#e6e6e6}.pc.alert_tips{top:50px;right:50px}.pc.alert_tips div{background:rgba(0,0,0,.7);position:relative;color:#fff;font-size:16px;padding:10px 15px;border-radius:2px;margin-bottom:20px;box-shadow:0 0 3px #000;display:none;float:right;clear:both}.mob .alert_buttons{text-align:center;border-top:1px solid #ccc;-webkit-user-select:none}.mob .alert_buttons .alert_btn{display:inline-block;width:50%;border:0;height:40px;font-size:14px;outline:0;-webkit-appearance:none;background:#fff;-webkit-tap-highlight-color:transparent;border-radius:0 0 4px 4px}.mob .alert_buttons .alert_btn:only-child{width:100%}.mob .alert_buttons .alert_btn:first-child+.alert_btn{border-left:1px solid #ccc;border-radius:0 0 4px 0}.mob.alert_tips{width:100%;top:55%;pointer-events:none;text-align:center}.mob.alert_tips div{box-sizing:border-box;display:inline-block;padding:15px;border-radius:10px;background:rgba(0,0,0,.7);min-width:50px;max-width:230px;text-align:center;color:#fff;animation:tipsshow 3s .01s ease;-webkit-animation:tipsshow 3s .01s ease;opacity:0}@keyframes tipsshow{0%{opacity:0;transform:scale(1.4) rotateX(-360deg)}20%,80%{opacity:1;transform:scale(1) rotateX(0deg)}to{transform:scale(1.4) rotateX(360deg)}}@-webkit-keyframes tipsshow{0%,to{opacity:0}0%{-webkit-transform:scale(1.4) rotateX(-360deg)}20%,80%{opacity:1;-webkit-transform:scale(1) rotateX(0deg)}to{opacity:0;-webkit-transform:scale(1.4) rotateX(360deg)}}";t("head").append('"),t._ismob=/i(Phone|Pod)|Android|phone/i.test(navigator.userAgent),t._isalert=t._isload=0,t.alert=function(){if(arguments.length)return t._isalert=1,t.confirm.apply(t,arguments)},t.confirm=function(){var e,o=arguments;if(o.length){var a=o[1],n=function(t){"function"==typeof a?0!=a.call(e,t.data.r)&&e.close():e.close()};e=t('
'+o[0]+'
').on("contextmenu",!1).on("click",".alert_btn_ok",{r:!0},n).on("click",".alert_btn_cancel",{r:!1},n),t._isload?e.find(".alert_content").css("text-align","center").parent().css({width:"auto",borderRadius:"4px"}).find(".alert_buttons").remove():t._isalert&&e.find(".alert_btn_cancel").remove(),e.appendTo("body").find(".alert_btn_ok").focus(),e.ok=function(t){return e.find(".alert_btn_ok").text(t||"Okey"),e},e.cancel=function(t){return e.find(".alert_btn_cancel").text(t||"Cancel"),e},e.content=function(t){return t&&e.find(".alert_content").html(t),e},e.close=function(){e.one("webkitTransitionEnd transitionEnd",function(){e.remove()}).removeClass("alert_show")},e.addClass("alert_show")}return t._isalert=t._isload=0,e},t.tips=function(e,o){if(e)if(t._ismob)t(".alert_tips").remove(),t('
'+e+"
").appendTo("body").one("webkitAnimationEnd animationEnd",function(){t(this).remove()});else{var a=t(".alert_tips");a.length||(a=t('
').appendTo("body")),t("
"+e+"
").appendTo(a).fadeIn("fast").delay(o||2e3).slideUp("fast",function(){t(this).remove()})}},t.load=function(){t(".alert_overlay").remove(),t._isload=1;var e=t.confirm.call(t,arguments[0]||"Loading...");return t.loaded=e.close,e}}($); diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/codemirror.css b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/codemirror.css new file mode 100644 index 00000000..8efefff2 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/codemirror.css @@ -0,0 +1,3 @@ +.CodeMirror-hints{ + z-index:1000000; +} \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/base64.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/base64.js new file mode 100644 index 00000000..1e870ac4 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/base64.js @@ -0,0 +1,151 @@ + +/** +* +* Base64 encode / decode +* http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input, binary) { + binary = (binary != null) ? binary : false; + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + if (!binary) + { + input = Base64._utf8_encode(input); + } + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input, binary) { + binary = (binary != null) ? binary : false; + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + if (!binary) + { + output = Base64._utf8_decode(output); + } + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/pako.min.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/pako.min.js new file mode 100644 index 00000000..92405bd2 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/deflate/pako.min.js @@ -0,0 +1,3 @@ +/* pako 1.0.3 nodeca/pako */ +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.pako=t()}}(function(){return function t(e,a,i){function n(s,o){if(!a[s]){if(!e[s]){var l="function"==typeof require&&require;if(!o&&l)return l(s,!0);if(r)return r(s,!0);var h=new Error("Cannot find module '"+s+"'");throw h.code="MODULE_NOT_FOUND",h}var d=a[s]={exports:{}};e[s][0].call(d.exports,function(t){var a=e[s][1][t];return n(a?a:t)},d,d.exports,t,e,a,i)}return a[s].exports}for(var r="function"==typeof require&&require,s=0;s0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=o.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(a!==b)throw new Error(d[a]);if(e.header&&o.deflateSetHeader(this.strm,e.header),e.dictionary){var n;if(n="string"==typeof e.dictionary?h.string2buf(e.dictionary):"[object ArrayBuffer]"===_.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,a=o.deflateSetDictionary(this.strm,n),a!==b)throw new Error(d[a]);this._dict_set=!0}}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}function s(t,e){return e=e||{},e.gzip=!0,n(t,e)}var o=t("./zlib/deflate"),l=t("./utils/common"),h=t("./utils/strings"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=Object.prototype.toString,u=0,c=4,b=0,g=1,m=2,w=-1,p=0,v=8;i.prototype.push=function(t,e){var a,i,n=this.strm,r=this.options.chunkSize;if(this.ended)return!1;i=e===~~e?e:e===!0?c:u,"string"==typeof t?n.input=h.string2buf(t):"[object ArrayBuffer]"===_.call(t)?n.input=new Uint8Array(t):n.input=t,n.next_in=0,n.avail_in=n.input.length;do{if(0===n.avail_out&&(n.output=new l.Buf8(r),n.next_out=0,n.avail_out=r),a=o.deflate(n,i),a!==g&&a!==b)return this.onEnd(a),this.ended=!0,!1;0!==n.avail_out&&(0!==n.avail_in||i!==c&&i!==m)||("string"===this.options.to?this.onData(h.buf2binstring(l.shrinkBuf(n.output,n.next_out))):this.onData(l.shrinkBuf(n.output,n.next_out)))}while((n.avail_in>0||0===n.avail_out)&&a!==g);return i===c?(a=o.deflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===b):i!==m||(this.onEnd(b),n.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===b&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=l.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Deflate=i,a.deflate=n,a.deflateRaw=r,a.gzip=s},{"./utils/common":3,"./utils/strings":4,"./zlib/deflate":8,"./zlib/messages":13,"./zlib/zstream":15}],2:[function(t,e,a){"use strict";function i(t){if(!(this instanceof i))return new i(t);this.options=o.assign({chunkSize:16384,windowBits:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0===(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=s.inflateInit2(this.strm,e.windowBits);if(a!==h.Z_OK)throw new Error(d[a]);this.header=new _,s.inflateGetHeader(this.strm,this.header)}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}var s=t("./zlib/inflate"),o=t("./utils/common"),l=t("./utils/strings"),h=t("./zlib/constants"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=t("./zlib/gzheader"),u=Object.prototype.toString;i.prototype.push=function(t,e){var a,i,n,r,d,f,_=this.strm,c=this.options.chunkSize,b=this.options.dictionary,g=!1;if(this.ended)return!1;i=e===~~e?e:e===!0?h.Z_FINISH:h.Z_NO_FLUSH,"string"==typeof t?_.input=l.binstring2buf(t):"[object ArrayBuffer]"===u.call(t)?_.input=new Uint8Array(t):_.input=t,_.next_in=0,_.avail_in=_.input.length;do{if(0===_.avail_out&&(_.output=new o.Buf8(c),_.next_out=0,_.avail_out=c),a=s.inflate(_,h.Z_NO_FLUSH),a===h.Z_NEED_DICT&&b&&(f="string"==typeof b?l.string2buf(b):"[object ArrayBuffer]"===u.call(b)?new Uint8Array(b):b,a=s.inflateSetDictionary(this.strm,f)),a===h.Z_BUF_ERROR&&g===!0&&(a=h.Z_OK,g=!1),a!==h.Z_STREAM_END&&a!==h.Z_OK)return this.onEnd(a),this.ended=!0,!1;_.next_out&&(0!==_.avail_out&&a!==h.Z_STREAM_END&&(0!==_.avail_in||i!==h.Z_FINISH&&i!==h.Z_SYNC_FLUSH)||("string"===this.options.to?(n=l.utf8border(_.output,_.next_out),r=_.next_out-n,d=l.buf2string(_.output,n),_.next_out=r,_.avail_out=c-r,r&&o.arraySet(_.output,_.output,n,r,0),this.onData(d)):this.onData(o.shrinkBuf(_.output,_.next_out)))),0===_.avail_in&&0===_.avail_out&&(g=!0)}while((_.avail_in>0||0===_.avail_out)&&a!==h.Z_STREAM_END);return a===h.Z_STREAM_END&&(i=h.Z_FINISH),i===h.Z_FINISH?(a=s.inflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===h.Z_OK):i!==h.Z_SYNC_FLUSH||(this.onEnd(h.Z_OK),_.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===h.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Inflate=i,a.inflate=n,a.inflateRaw=r,a.ungzip=n},{"./utils/common":3,"./utils/strings":4,"./zlib/constants":6,"./zlib/gzheader":9,"./zlib/inflate":11,"./zlib/messages":13,"./zlib/zstream":15}],3:[function(t,e,a){"use strict";var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;a.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var a=e.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(var i in a)a.hasOwnProperty(i)&&(t[i]=a[i])}}return t},a.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var n={arraySet:function(t,e,a,i,n){if(e.subarray&&t.subarray)return void t.set(e.subarray(a,a+i),n);for(var r=0;r=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,a.string2buf=function(t){var e,a,i,r,s,o=t.length,l=0;for(r=0;r>>6,e[s++]=128|63&a):a<65536?(e[s++]=224|a>>>12,e[s++]=128|a>>>6&63,e[s++]=128|63&a):(e[s++]=240|a>>>18,e[s++]=128|a>>>12&63,e[s++]=128|a>>>6&63,e[s++]=128|63&a);return e},a.buf2binstring=function(t){return i(t,t.length)},a.binstring2buf=function(t){for(var e=new n.Buf8(t.length),a=0,i=e.length;a4)h[n++]=65533,a+=s-1;else{for(r&=2===s?31:3===s?15:7;s>1&&a1?h[n++]=65533:r<65536?h[n++]=r:(r-=65536,h[n++]=55296|r>>10&1023,h[n++]=56320|1023&r)}return i(h,n)},a.utf8border=function(t,e){var a;for(e=e||t.length,e>t.length&&(e=t.length),a=e-1;a>=0&&128===(192&t[a]);)a--;return a<0?e:0===a?e:a+o[t[a]]>e?a:e}},{"./common":3}],5:[function(t,e,a){"use strict";function i(t,e,a,i){for(var n=65535&t|0,r=t>>>16&65535|0,s=0;0!==a;){s=a>2e3?2e3:a,a-=s;do n=n+e[i++]|0,r=r+n|0;while(--s);n%=65521,r%=65521}return n|r<<16|0}e.exports=i},{}],6:[function(t,e,a){"use strict";e.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],7:[function(t,e,a){"use strict";function i(){for(var t,e=[],a=0;a<256;a++){t=a;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[a]=t}return e}function n(t,e,a,i){var n=r,s=i+a;t^=-1;for(var o=i;o>>8^n[255&(t^e[o])];return t^-1}var r=i();e.exports=n},{}],8:[function(t,e,a){"use strict";function i(t,e){return t.msg=D[e],e}function n(t){return(t<<1)-(t>4?9:0)}function r(t){for(var e=t.length;--e>=0;)t[e]=0}function s(t){var e=t.state,a=e.pending;a>t.avail_out&&(a=t.avail_out),0!==a&&(R.arraySet(t.output,e.pending_buf,e.pending_out,a,t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))}function o(t,e){C._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,s(t.strm)}function l(t,e){t.pending_buf[t.pending++]=e}function h(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function d(t,e,a,i){var n=t.avail_in;return n>i&&(n=i),0===n?0:(t.avail_in-=n,R.arraySet(e,t.input,t.next_in,n,a),1===t.state.wrap?t.adler=N(t.adler,e,n,a):2===t.state.wrap&&(t.adler=O(t.adler,e,n,a)),t.next_in+=n,t.total_in+=n,n)}function f(t,e){var a,i,n=t.max_chain_length,r=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-ft?t.strstart-(t.w_size-ft):0,h=t.window,d=t.w_mask,f=t.prev,_=t.strstart+dt,u=h[r+s-1],c=h[r+s];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do if(a=e,h[a+s]===c&&h[a+s-1]===u&&h[a]===h[r]&&h[++a]===h[r+1]){r+=2,a++;do;while(h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&r<_);if(i=dt-(_-r),r=_-dt,i>s){if(t.match_start=e,s=i,i>=o)break;u=h[r+s-1],c=h[r+s]}}while((e=f[e&d])>l&&0!==--n);return s<=t.lookahead?s:t.lookahead}function _(t){var e,a,i,n,r,s=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=s+(s-ft)){R.arraySet(t.window,t.window,s,s,0),t.match_start-=s,t.strstart-=s,t.block_start-=s,a=t.hash_size,e=a;do i=t.head[--e],t.head[e]=i>=s?i-s:0;while(--a);a=s,e=a;do i=t.prev[--e],t.prev[e]=i>=s?i-s:0;while(--a);n+=s}if(0===t.strm.avail_in)break;if(a=d(t.strm,t.window,t.strstart+t.lookahead,n),t.lookahead+=a,t.lookahead+t.insert>=ht)for(r=t.strstart-t.insert,t.ins_h=t.window[r],t.ins_h=(t.ins_h<t.pending_buf_size-5&&(a=t.pending_buf_size-5);;){if(t.lookahead<=1){if(_(t),0===t.lookahead&&e===I)return vt;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+a;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,o(t,!1),0===t.strm.avail_out))return vt;if(t.strstart-t.block_start>=t.w_size-ft&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.strstart>t.block_start&&(o(t,!1),0===t.strm.avail_out)?vt:vt}function c(t,e){for(var a,i;;){if(t.lookahead=ht&&(t.ins_h=(t.ins_h<=ht)if(i=C._tr_tally(t,t.strstart-t.match_start,t.match_length-ht),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=ht){t.match_length--;do t.strstart++,t.ins_h=(t.ins_h<=ht&&(t.ins_h=(t.ins_h<4096)&&(t.match_length=ht-1)),t.prev_length>=ht&&t.match_length<=t.prev_length){n=t.strstart+t.lookahead-ht,i=C._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-ht),t.lookahead-=t.prev_length-1,t.prev_length-=2;do++t.strstart<=n&&(t.ins_h=(t.ins_h<=ht&&t.strstart>0&&(n=t.strstart-1,i=s[n],i===s[++n]&&i===s[++n]&&i===s[++n])){r=t.strstart+dt;do;while(i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&nt.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=ht?(a=C._tr_tally(t,1,t.match_length-ht),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function m(t,e){for(var a;;){if(0===t.lookahead&&(_(t),0===t.lookahead)){if(e===I)return vt;break}if(t.match_length=0,a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function w(t,e,a,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=a,this.max_chain=i,this.func=n}function p(t){t.window_size=2*t.w_size,r(t.head),t.max_lazy_match=Z[t.level].max_lazy,t.good_match=Z[t.level].good_length,t.nice_match=Z[t.level].nice_length,t.max_chain_length=Z[t.level].max_chain,t.strstart=0,t.block_start=0,t.lookahead=0,t.insert=0,t.match_length=t.prev_length=ht-1,t.match_available=0,t.ins_h=0}function v(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=V,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new R.Buf16(2*ot),this.dyn_dtree=new R.Buf16(2*(2*rt+1)),this.bl_tree=new R.Buf16(2*(2*st+1)),r(this.dyn_ltree),r(this.dyn_dtree),r(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new R.Buf16(lt+1),this.heap=new R.Buf16(2*nt+1),r(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new R.Buf16(2*nt+1),r(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function k(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_type=Q,e=t.state,e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=e.wrap?ut:wt,t.adler=2===e.wrap?0:1,e.last_flush=I,C._tr_init(e),H):i(t,K)}function y(t){var e=k(t);return e===H&&p(t.state),e}function x(t,e){return t&&t.state?2!==t.state.wrap?K:(t.state.gzhead=e,H):K}function z(t,e,a,n,r,s){if(!t)return K;var o=1;if(e===Y&&(e=6),n<0?(o=0,n=-n):n>15&&(o=2,n-=16),r<1||r>$||a!==V||n<8||n>15||e<0||e>9||s<0||s>W)return i(t,K);8===n&&(n=9);var l=new v;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=n,l.w_size=1<L||e<0)return t?i(t,K):K;if(o=t.state,!t.output||!t.input&&0!==t.avail_in||o.status===pt&&e!==F)return i(t,0===t.avail_out?P:K);if(o.strm=t,a=o.last_flush,o.last_flush=e,o.status===ut)if(2===o.wrap)t.adler=0,l(o,31),l(o,139),l(o,8),o.gzhead?(l(o,(o.gzhead.text?1:0)+(o.gzhead.hcrc?2:0)+(o.gzhead.extra?4:0)+(o.gzhead.name?8:0)+(o.gzhead.comment?16:0)),l(o,255&o.gzhead.time),l(o,o.gzhead.time>>8&255),l(o,o.gzhead.time>>16&255),l(o,o.gzhead.time>>24&255),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,255&o.gzhead.os),o.gzhead.extra&&o.gzhead.extra.length&&(l(o,255&o.gzhead.extra.length),l(o,o.gzhead.extra.length>>8&255)),o.gzhead.hcrc&&(t.adler=O(t.adler,o.pending_buf,o.pending,0)),o.gzindex=0,o.status=ct):(l(o,0),l(o,0),l(o,0),l(o,0),l(o,0),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,zt),o.status=wt);else{var _=V+(o.w_bits-8<<4)<<8,u=-1;u=o.strategy>=G||o.level<2?0:o.level<6?1:6===o.level?2:3,_|=u<<6,0!==o.strstart&&(_|=_t),_+=31-_%31,o.status=wt,h(o,_),0!==o.strstart&&(h(o,t.adler>>>16),h(o,65535&t.adler)),t.adler=1}if(o.status===ct)if(o.gzhead.extra){for(d=o.pending;o.gzindex<(65535&o.gzhead.extra.length)&&(o.pending!==o.pending_buf_size||(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending!==o.pending_buf_size));)l(o,255&o.gzhead.extra[o.gzindex]),o.gzindex++;o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),o.gzindex===o.gzhead.extra.length&&(o.gzindex=0,o.status=bt)}else o.status=bt;if(o.status===bt)if(o.gzhead.name){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.gzindex=0,o.status=gt)}else o.status=gt;if(o.status===gt)if(o.gzhead.comment){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.status=mt)}else o.status=mt;if(o.status===mt&&(o.gzhead.hcrc?(o.pending+2>o.pending_buf_size&&s(t),o.pending+2<=o.pending_buf_size&&(l(o,255&t.adler),l(o,t.adler>>8&255),t.adler=0,o.status=wt)):o.status=wt),0!==o.pending){if(s(t),0===t.avail_out)return o.last_flush=-1,H}else if(0===t.avail_in&&n(e)<=n(a)&&e!==F)return i(t,P);if(o.status===pt&&0!==t.avail_in)return i(t,P);if(0!==t.avail_in||0!==o.lookahead||e!==I&&o.status!==pt){var c=o.strategy===G?m(o,e):o.strategy===X?g(o,e):Z[o.level].func(o,e);if(c!==yt&&c!==xt||(o.status=pt),c===vt||c===yt)return 0===t.avail_out&&(o.last_flush=-1),H;if(c===kt&&(e===U?C._tr_align(o):e!==L&&(C._tr_stored_block(o,0,0,!1),e===T&&(r(o.head),0===o.lookahead&&(o.strstart=0,o.block_start=0,o.insert=0))),s(t),0===t.avail_out))return o.last_flush=-1,H}return e!==F?H:o.wrap<=0?j:(2===o.wrap?(l(o,255&t.adler),l(o,t.adler>>8&255),l(o,t.adler>>16&255),l(o,t.adler>>24&255),l(o,255&t.total_in),l(o,t.total_in>>8&255),l(o,t.total_in>>16&255),l(o,t.total_in>>24&255)):(h(o,t.adler>>>16),h(o,65535&t.adler)),s(t),o.wrap>0&&(o.wrap=-o.wrap),0!==o.pending?H:j)}function E(t){var e;return t&&t.state?(e=t.state.status,e!==ut&&e!==ct&&e!==bt&&e!==gt&&e!==mt&&e!==wt&&e!==pt?i(t,K):(t.state=null,e===wt?i(t,M):H)):K}function A(t,e){var a,i,n,s,o,l,h,d,f=e.length;if(!t||!t.state)return K;if(a=t.state,s=a.wrap,2===s||1===s&&a.status!==ut||a.lookahead)return K;for(1===s&&(t.adler=N(t.adler,e,f,0)),a.wrap=0,f>=a.w_size&&(0===s&&(r(a.head),a.strstart=0,a.block_start=0,a.insert=0),d=new R.Buf8(a.w_size),R.arraySet(d,e,f-a.w_size,a.w_size,0),e=d,f=a.w_size),o=t.avail_in,l=t.next_in,h=t.input,t.avail_in=f,t.next_in=0,t.input=e,_(a);a.lookahead>=ht;){i=a.strstart,n=a.lookahead-(ht-1);do a.ins_h=(a.ins_h<>>24,b>>>=y,g-=y,y=k>>>16&255,0===y)A[o++]=65535&k;else{if(!(16&y)){if(0===(64&y)){k=m[(65535&k)+(b&(1<>>=y,g-=y),g<15&&(b+=E[r++]<>>24,b>>>=y,g-=y,y=k>>>16&255,!(16&y)){if(0===(64&y)){k=w[(65535&k)+(b&(1<d){t.msg="invalid distance too far back",a.mode=i;break t}if(b>>>=y,g-=y,y=o-l,z>y){if(y=z-y,y>_&&a.sane){t.msg="invalid distance too far back",a.mode=i;break t}if(B=0,S=c,0===u){if(B+=f-y,y2;)A[o++]=S[B++],A[o++]=S[B++],A[o++]=S[B++],x-=3;x&&(A[o++]=S[B++],x>1&&(A[o++]=S[B++]))}else{B=o-z;do A[o++]=A[B++],A[o++]=A[B++],A[o++]=A[B++],x-=3;while(x>2);x&&(A[o++]=A[B++],x>1&&(A[o++]=A[B++]))}break}}break}}while(r>3,r-=x,g-=x<<3,b&=(1<>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function n(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new w.Buf16(320),this.work=new w.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function r(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=T,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new w.Buf32(bt),e.distcode=e.distdyn=new w.Buf32(gt),e.sane=1,e.back=-1,Z):N}function s(t){var e;return t&&t.state?(e=t.state,e.wsize=0,e.whave=0,e.wnext=0,r(t)):N}function o(t,e){var a,i;return t&&t.state?(i=t.state,e<0?(a=0,e=-e):(a=(e>>4)+1,e<48&&(e&=15)),e&&(e<8||e>15)?N:(null!==i.window&&i.wbits!==e&&(i.window=null),i.wrap=a,i.wbits=e,s(t))):N}function l(t,e){var a,i;return t?(i=new n,t.state=i,i.window=null,a=o(t,e),a!==Z&&(t.state=null),a):N}function h(t){return l(t,wt)}function d(t){if(pt){var e;for(g=new w.Buf32(512),m=new w.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(y(z,t.lens,0,288,g,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;y(B,t.lens,0,32,m,0,t.work,{bits:5}),pt=!1}t.lencode=g,t.lenbits=9,t.distcode=m,t.distbits=5}function f(t,e,a,i){var n,r=t.state;return null===r.window&&(r.wsize=1<=r.wsize?(w.arraySet(r.window,e,a-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(n=r.wsize-r.wnext,n>i&&(n=i),w.arraySet(r.window,e,a-i,n,r.wnext),i-=n,i?(w.arraySet(r.window,e,a-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=n,r.wnext===r.wsize&&(r.wnext=0),r.whave>>8&255,a.check=v(a.check,Et,2,0),_=0,u=0,a.mode=F;break}if(a.flags=0,a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&_)<<8)+(_>>8))%31){t.msg="incorrect header check",a.mode=_t;break}if((15&_)!==U){t.msg="unknown compression method",a.mode=_t;break}if(_>>>=4,u-=4,yt=(15&_)+8,0===a.wbits)a.wbits=yt;else if(yt>a.wbits){t.msg="invalid window size",a.mode=_t;break}a.dmax=1<>8&1),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=L;case L:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>8&255,Et[2]=_>>>16&255,Et[3]=_>>>24&255,a.check=v(a.check,Et,4,0)),_=0,u=0,a.mode=H;case H:for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>8),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=j;case j:if(1024&a.flags){for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0}else a.head&&(a.head.extra=null);a.mode=K;case K:if(1024&a.flags&&(g=a.length,g>l&&(g=l),g&&(a.head&&(yt=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Array(a.head.extra_len)),w.arraySet(a.head.extra,n,s,g,yt)),512&a.flags&&(a.check=v(a.check,n,g,s)),l-=g,s+=g,a.length-=g),a.length))break t;a.length=0,a.mode=M;case M:if(2048&a.flags){if(0===l)break t;g=0;do yt=n[s+g++],a.head&&yt&&a.length<65536&&(a.head.name+=String.fromCharCode(yt));while(yt&&g>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=X;break;case q:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>=7&u,u-=7&u,a.mode=ht;break}for(;u<3;){if(0===l)break t;l--,_+=n[s++]<>>=1,u-=1,3&_){case 0:a.mode=J;break;case 1:if(d(a),a.mode=at,e===A){_>>>=2,u-=2;break t}break;case 2:a.mode=$;break;case 3:t.msg="invalid block type",a.mode=_t}_>>>=2,u-=2;break;case J:for(_>>>=7&u,u-=7&u;u<32;){if(0===l)break t;l--,_+=n[s++]<>>16^65535)){t.msg="invalid stored block lengths",a.mode=_t;break}if(a.length=65535&_,_=0,u=0,a.mode=Q,e===A)break t;case Q:a.mode=V;case V:if(g=a.length){if(g>l&&(g=l),g>h&&(g=h),0===g)break t;w.arraySet(r,n,s,g,o),l-=g,s+=g,h-=g,o+=g,a.length-=g;break}a.mode=X;break;case $:for(;u<14;){if(0===l)break t; +l--,_+=n[s++]<>>=5,u-=5,a.ndist=(31&_)+1,_>>>=5,u-=5,a.ncode=(15&_)+4,_>>>=4,u-=4,a.nlen>286||a.ndist>30){t.msg="too many length or distance symbols",a.mode=_t;break}a.have=0,a.mode=tt;case tt:for(;a.have>>=3,u-=3}for(;a.have<19;)a.lens[At[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,zt={bits:a.lenbits},xt=y(x,a.lens,0,19,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid code lengths set",a.mode=_t;break}a.have=0,a.mode=et;case et:for(;a.have>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=gt,u-=gt,a.lens[a.have++]=wt;else{if(16===wt){for(Bt=gt+2;u>>=gt,u-=gt,0===a.have){t.msg="invalid bit length repeat",a.mode=_t;break}yt=a.lens[a.have-1],g=3+(3&_),_>>>=2,u-=2}else if(17===wt){for(Bt=gt+3;u>>=gt,u-=gt,yt=0,g=3+(7&_),_>>>=3,u-=3}else{for(Bt=gt+7;u>>=gt,u-=gt,yt=0,g=11+(127&_),_>>>=7,u-=7}if(a.have+g>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=_t;break}for(;g--;)a.lens[a.have++]=yt}}if(a.mode===_t)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=_t;break}if(a.lenbits=9,zt={bits:a.lenbits},xt=y(z,a.lens,0,a.nlen,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid literal/lengths set",a.mode=_t;break}if(a.distbits=6,a.distcode=a.distdyn,zt={bits:a.distbits},xt=y(B,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,zt),a.distbits=zt.bits,xt){t.msg="invalid distances set",a.mode=_t;break}if(a.mode=at,e===A)break t;case at:a.mode=it;case it:if(l>=6&&h>=258){t.next_out=o,t.avail_out=h,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=u,k(t,b),o=t.next_out,r=t.output,h=t.avail_out,s=t.next_in,n=t.input,l=t.avail_in,_=a.hold,u=a.bits,a.mode===X&&(a.back=-1);break}for(a.back=0;St=a.lencode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,a.length=wt,0===mt){a.mode=lt;break}if(32&mt){a.back=-1,a.mode=X;break}if(64&mt){t.msg="invalid literal/length code",a.mode=_t;break}a.extra=15&mt,a.mode=nt;case nt:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=rt;case rt:for(;St=a.distcode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,64&mt){t.msg="invalid distance code",a.mode=_t;break}a.offset=wt,a.extra=15&mt,a.mode=st;case st:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=_t;break}a.mode=ot;case ot:if(0===h)break t;if(g=b-h,a.offset>g){if(g=a.offset-g,g>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=_t;break}g>a.wnext?(g-=a.wnext,m=a.wsize-g):m=a.wnext-g,g>a.length&&(g=a.length),bt=a.window}else bt=r,m=o-a.offset,g=a.length;g>h&&(g=h),h-=g,a.length-=g;do r[o++]=bt[m++];while(--g);0===a.length&&(a.mode=it);break;case lt:if(0===h)break t;r[o++]=a.length,h--,a.mode=it;break;case ht:if(a.wrap){for(;u<32;){if(0===l)break t;l--,_|=n[s++]<=1&&0===j[N];N--);if(O>N&&(O=N),0===N)return b[g++]=20971520,b[g++]=20971520,w.bits=1,0;for(C=1;C0&&(t===o||1!==N))return-1;for(K[1]=0,Z=1;Zr||t===h&&T>s)return 1;for(var Y=0;;){Y++,B=Z-I,m[R]z?(S=M[P+m[R]],E=L[H+m[R]]):(S=96,E=0),p=1<>I)+v]=B<<24|S<<16|E|0;while(0!==v);for(p=1<>=1;if(0!==p?(F&=p-1,F+=p):F=0,R++,0===--j[Z]){if(Z===N)break;Z=e[a+m[R]]}if(Z>O&&(F&y)!==k){for(0===I&&(I=O),x+=C,D=Z-I,U=1<r||t===h&&T>s)return 1;k=F&y,b[k]=O<<24|D<<16|x-g|0}}return 0!==F&&(b[x+F]=Z-I<<24|64<<16|0),w.bits=O,0}},{"../utils/common":3}],13:[function(t,e,a){"use strict";e.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],14:[function(t,e,a){"use strict";function i(t){for(var e=t.length;--e>=0;)t[e]=0}function n(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}function r(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function s(t){return t<256?lt[t]:lt[256+(t>>>7)]}function o(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function l(t,e,a){t.bi_valid>W-a?(t.bi_buf|=e<>W-t.bi_valid,t.bi_valid+=a-W):(t.bi_buf|=e<>>=1,a<<=1;while(--e>0);return a>>>1}function f(t){16===t.bi_valid?(o(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}function _(t,e){var a,i,n,r,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,_=e.stat_desc.extra_bits,u=e.stat_desc.extra_base,c=e.stat_desc.max_length,b=0;for(r=0;r<=X;r++)t.bl_count[r]=0;for(l[2*t.heap[t.heap_max]+1]=0,a=t.heap_max+1;ac&&(r=c,b++),l[2*i+1]=r,i>h||(t.bl_count[r]++,s=0,i>=u&&(s=_[i-u]),o=l[2*i],t.opt_len+=o*(r+s),f&&(t.static_len+=o*(d[2*i+1]+s)));if(0!==b){do{for(r=c-1;0===t.bl_count[r];)r--;t.bl_count[r]--,t.bl_count[r+1]+=2,t.bl_count[c]--,b-=2}while(b>0);for(r=c;0!==r;r--)for(i=t.bl_count[r];0!==i;)n=t.heap[--a],n>h||(l[2*n+1]!==r&&(t.opt_len+=(r-l[2*n+1])*l[2*n],l[2*n+1]=r),i--)}}function u(t,e,a){var i,n,r=new Array(X+1),s=0;for(i=1;i<=X;i++)r[i]=s=s+a[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=d(r[o]++,o))}}function c(){var t,e,a,i,r,s=new Array(X+1);for(a=0,i=0;i>=7;i8?o(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function m(t,e,a,i){g(t),i&&(o(t,a),o(t,~a)),N.arraySet(t.pending_buf,t.window,e,a,t.pending),t.pending+=a}function w(t,e,a,i){var n=2*e,r=2*a;return t[n]>1;a>=1;a--)p(t,r,a);n=l;do a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],p(t,r,1),i=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=i,r[2*n]=r[2*a]+r[2*i],t.depth[n]=(t.depth[a]>=t.depth[i]?t.depth[a]:t.depth[i])+1,r[2*a+1]=r[2*i+1]=n,t.heap[1]=n++,p(t,r,1);while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],_(t,e),u(r,h,t.bl_count)}function y(t,e,a){var i,n,r=-1,s=e[1],o=0,l=7,h=4;for(0===s&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=s,s=e[2*(i+1)+1],++o=3&&0===t.bl_tree[2*nt[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}function B(t,e,a,i){var n;for(l(t,e-257,5),l(t,a-1,5),l(t,i-4,4),n=0;n>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return D;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return I;for(e=32;e0?(t.strm.data_type===U&&(t.strm.data_type=S(t)),k(t,t.l_desc),k(t,t.d_desc),s=z(t),n=t.opt_len+3+7>>>3,r=t.static_len+3+7>>>3,r<=n&&(n=r)):n=r=a+5,a+4<=n&&e!==-1?A(t,e,a,i):t.strategy===O||r===n?(l(t,(F<<1)+(i?1:0),3),v(t,st,ot)):(l(t,(L<<1)+(i?1:0),3),B(t,t.l_desc.max_code+1,t.d_desc.max_code+1,s+1),v(t,t.dyn_ltree,t.dyn_dtree)),b(t),i&&g(t)}function C(t,e,a){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&a,t.last_lit++,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(ht[a]+M+1)]++,t.dyn_dtree[2*s(e)]++),t.last_lit===t.lit_bufsize-1}var N=t("../utils/common"),O=4,D=0,I=1,U=2,T=0,F=1,L=2,H=3,j=258,K=29,M=256,P=M+1+K,Y=30,q=19,G=2*P+1,X=15,W=16,J=7,Q=256,V=16,$=17,tt=18,et=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],at=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],it=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],nt=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],rt=512,st=new Array(2*(P+2));i(st);var ot=new Array(2*Y);i(ot);var lt=new Array(rt);i(lt);var ht=new Array(j-H+1);i(ht);var dt=new Array(K);i(dt);var ft=new Array(Y);i(ft);var _t,ut,ct,bt=!1;a._tr_init=E,a._tr_stored_block=A,a._tr_flush_block=R,a._tr_tally=C,a._tr_align=Z},{"../utils/common":3}],15:[function(t,e,a){"use strict";function i(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}e.exports=i},{}],"/":[function(t,e,a){"use strict";var i=t("./lib/utils/common").assign,n=t("./lib/deflate"),r=t("./lib/inflate"),s=t("./lib/zlib/constants"),o={};i(o,n,r,s),e.exports=o},{"./lib/deflate":1,"./lib/inflate":2,"./lib/utils/common":3,"./lib/zlib/constants":6}]},{},[])("/")}); diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/checkmark.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/checkmark.gif new file mode 100644 index 00000000..d79444da Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/checkmark.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/clear.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/clear.gif new file mode 100644 index 00000000..c6acf0a4 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/clear.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/close.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/close.png new file mode 100644 index 00000000..d319efb6 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/close.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/collapsed.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/collapsed.gif new file mode 100644 index 00000000..ce977743 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/collapsed.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.gif new file mode 100644 index 00000000..9c94ea5a Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.png new file mode 100644 index 00000000..0aeac2db Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/dropdown.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/edit.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/edit.gif new file mode 100644 index 00000000..3c076073 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/edit.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/expanded.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/expanded.gif new file mode 100644 index 00000000..461297fc Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/expanded.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/grid.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/grid.gif new file mode 100644 index 00000000..f4e7063e Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/grid.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-fixed.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-fixed.png new file mode 100644 index 00000000..b4b600b1 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-fixed.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-main.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-main.png new file mode 100644 index 00000000..ee067ff8 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-main.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-rotate.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-rotate.png new file mode 100644 index 00000000..f3dd46e2 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-rotate.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-secondary.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-secondary.png new file mode 100644 index 00000000..b4a30900 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-secondary.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-terminal.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-terminal.png new file mode 100644 index 00000000..ec03b316 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/handle-terminal.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/help.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/help.png new file mode 100644 index 00000000..17ee2eb8 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/help.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/locked.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/locked.png new file mode 100644 index 00000000..8dbac824 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/locked.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/logo.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/logo.png new file mode 100644 index 00000000..053a1eb2 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/logo.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/nocolor.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/nocolor.png new file mode 100644 index 00000000..aec4534d Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/nocolor.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/refresh.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/refresh.png new file mode 100644 index 00000000..b1312044 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/refresh.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/round-drop.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/round-drop.png new file mode 100644 index 00000000..b5e2fb60 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/round-drop.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/search.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/search.png new file mode 100644 index 00000000..d763a72d Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/search.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/tooltip.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/tooltip.png new file mode 100644 index 00000000..ad20c04e Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/tooltip.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/transparent.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/transparent.gif new file mode 100644 index 00000000..76040f2b Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/transparent.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-down.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-down.png new file mode 100644 index 00000000..d03b98fc Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-down.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-left.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-left.png new file mode 100644 index 00000000..44b84255 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-left.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-right.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-right.png new file mode 100644 index 00000000..98656281 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-right.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-up.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-up.png new file mode 100644 index 00000000..6d936767 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/triangle-up.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/unlocked.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/unlocked.png new file mode 100644 index 00000000..5ea44c1d Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/images/unlocked.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/index.html b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/index.html new file mode 100644 index 00000000..eeed4286 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/index.html @@ -0,0 +1,110 @@ + + + + + Grapheditor + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Actions.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Actions.js new file mode 100644 index 00000000..e7f7bef3 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Actions.js @@ -0,0 +1,1413 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs the actions object for the given UI. + */ +function Actions(editorUi) +{ + this.editorUi = editorUi; + this.actions = new Object(); + this.init(); +}; + +/** + * Adds the default actions. + */ +Actions.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var isGraphEnabled = function() + { + return Action.prototype.isEnabled.apply(this, arguments) && graph.isEnabled(); + }; + + // File actions + this.addAction('new...', function() { graph.openLink(ui.getUrl()); }); + this.addAction('open...', function() + { + window.openNew = true; + window.openKey = 'open'; + + ui.openFile(); + }); + this.addAction('import...', function() + { + window.openNew = false; + window.openKey = 'import'; + + // Closes dialog after open + window.openFile = new OpenFile(mxUtils.bind(this, function() + { + ui.hideDialog(); + })); + + window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) + { + try + { + var doc = mxUtils.parseXml(xml); + editor.graph.setSelectionCells(editor.graph.importGraphModel(doc.documentElement)); + } + catch (e) + { + mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); + } + })); + + // Removes openFile if dialog is closed + ui.showDialog(new OpenDialog(this).container, 320, 220, true, true, function() + { + window.openFile = null; + }); + }).isEnabled = isGraphEnabled; + this.addAction('save', function() { ui.saveFile(false); }, null, null, Editor.ctrlKey + '+S').isEnabled = isGraphEnabled; + this.addAction('saveAs...', function() { ui.saveFile(true); }, null, null, Editor.ctrlKey + '+Shift+S').isEnabled = isGraphEnabled; + this.addAction('export...', function() { ui.showDialog(new ExportDialog(ui).container, 300, 230, true, true); }); + this.addAction('editDiagram...', function() + { + var dlg = new EditDiagramDialog(ui); + ui.showDialog(dlg.container, 620, 420, true, false); + dlg.init(); + }); + this.addAction('pageSetup...', function() { ui.showDialog(new PageSetupDialog(ui).container, 320, 220, true, true); }).isEnabled = isGraphEnabled; + this.addAction('print...', function() { ui.showDialog(new PrintDialog(ui).container, 300, 180, true, true); }, null, 'sprite-print', Editor.ctrlKey + '+P'); + this.addAction('preview', function() { mxUtils.show(graph, null, 10, 10); }); + + // Edit actions + this.addAction('undo', function() { ui.undo(); }, null, 'sprite-undo', Editor.ctrlKey + '+Z'); + this.addAction('redo', function() { ui.redo(); }, null, 'sprite-redo', (!mxClient.IS_WIN) ? Editor.ctrlKey + '+Shift+Z' : Editor.ctrlKey + '+Y'); + this.addAction('cut', function() { mxClipboard.cut(graph); }, null, 'sprite-cut', Editor.ctrlKey + '+X'); + this.addAction('copy', function() { mxClipboard.copy(graph); }, null, 'sprite-copy', Editor.ctrlKey + '+C'); + this.addAction('paste', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + mxClipboard.paste(graph); + } + }, false, 'sprite-paste', Editor.ctrlKey + '+V'); + this.addAction('pasteHere', function(evt) + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.getModel().beginUpdate(); + try + { + var cells = mxClipboard.paste(graph); + + if (cells != null) + { + var includeEdges = true; + + for (var i = 0; i < cells.length && includeEdges; i++) + { + includeEdges = includeEdges && graph.model.isEdge(cells[i]); + } + + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + var bb = null; + + if (cells.length == 1 && includeEdges) + { + var geo = graph.getCellGeometry(cells[0]); + + if (geo != null) + { + bb = geo.getTerminalPoint(true); + } + } + + bb = (bb != null) ? bb : graph.getBoundingBoxFromGeometry(cells, includeEdges); + + if (bb != null) + { + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + graph.cellsMoved(cells, x - bb.x, y - bb.y); + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + + this.addAction('copySize', function(evt) + { + var cell = graph.getSelectionCell(); + + if (graph.isEnabled() && cell != null && graph.getModel().isVertex(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + ui.copiedSize = new mxRectangle(geo.x, geo.y, geo.width, geo.height); + } + } + }, null, null, 'Alt+Shit+X'); + + this.addAction('pasteSize', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty() && ui.copiedSize != null) + { + graph.getModel().beginUpdate(); + + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + geo.width = ui.copiedSize.width; + geo.height = ui.copiedSize.height; + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shit+V'); + + + function deleteCells(includeEdges) + { + // Cancels interactive operations + graph.escape(); + var cells = graph.getDeletableCells(graph.getSelectionCells()); + + if (cells != null && cells.length > 0) + { + var parents = graph.model.getParents(cells); + graph.removeCells(cells, includeEdges); + + // Selects parents for easier editing of groups + if (parents != null) + { + var select = []; + + for (var i = 0; i < parents.length; i++) + { + if (graph.model.contains(parents[i]) && + (graph.model.isVertex(parents[i]) || + graph.model.isEdge(parents[i]))) + { + select.push(parents[i]); + } + } + + graph.setSelectionCells(select); + } + } + }; + + this.addAction('delete', function(evt) + { + deleteCells(evt != null && mxEvent.isShiftDown(evt)); + }, null, null, 'Delete'); + this.addAction('deleteAll', function() + { + deleteCells(true); + }, null, null, Editor.ctrlKey + '+Delete'); + this.addAction('duplicate', function() + { + graph.setSelectionCells(graph.duplicateCells()); + }, null, null, Editor.ctrlKey + '+D'); + this.put('turn', new Action(mxResources.get('turn') + ' / ' + mxResources.get('reverse'), function() + { + graph.turnShapes(graph.getSelectionCells()); + }, null, null, Editor.ctrlKey + '+R')); + this.addAction('selectVertices', function() { graph.selectVertices(); }, null, null, Editor.ctrlKey + '+Shift+I'); + this.addAction('selectEdges', function() { graph.selectEdges(); }, null, null, Editor.ctrlKey + '+Shift+E'); + this.addAction('selectAll', function() { graph.selectAll(null, true); }, null, null, Editor.ctrlKey + '+A'); + this.addAction('selectNone', function() { graph.clearSelection(); }, null, null, Editor.ctrlKey + '+Shift+A'); + this.addAction('lockUnlock', function() + { + if (!graph.isSelectionEmpty()) + { + graph.getModel().beginUpdate(); + try + { + var defaultValue = graph.isCellMovable(graph.getSelectionCell()) ? 1 : 0; + graph.toggleCellStyles(mxConstants.STYLE_MOVABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_RESIZABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_ROTATABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_DELETABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, defaultValue); + graph.toggleCellStyles('connectable', defaultValue); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+L'); + + // Navigation actions + this.addAction('home', function() { graph.home(); }, null, null, 'Home'); + this.addAction('exitGroup', function() { graph.exitGroup(); }, null, null, Editor.ctrlKey + '+Shift+Home'); + this.addAction('enterGroup', function() { graph.enterGroup(); }, null, null, Editor.ctrlKey + '+Shift+End'); + this.addAction('collapse', function() { graph.foldCells(true); }, null, null, Editor.ctrlKey + '+Home'); + this.addAction('expand', function() { graph.foldCells(false); }, null, null, Editor.ctrlKey + '+End'); + + // Arrange actions + this.addAction('toFront', function() { graph.orderCells(false); }, null, null, Editor.ctrlKey + '+Shift+F'); + this.addAction('toBack', function() { graph.orderCells(true); }, null, null, Editor.ctrlKey + '+Shift+B'); + this.addAction('group', function() + { + if (graph.getSelectionCount() == 1) + { + graph.setCellStyles('container', '1'); + } + else + { + graph.setSelectionCell(graph.groupCells(null, 0)); + } + }, null, null, Editor.ctrlKey + '+G'); + this.addAction('ungroup', function() + { + if (graph.getSelectionCount() == 1 && graph.getModel().getChildCount(graph.getSelectionCell()) == 0) + { + graph.setCellStyles('container', '0'); + } + else + { + graph.setSelectionCells(graph.ungroupCells()); + } + }, null, null, Editor.ctrlKey + '+Shift+U'); + this.addAction('removeFromGroup', function() { graph.removeCellsFromParent(); }); + // Adds action + this.addAction('edit', function() + { + if (graph.isEnabled()) + { + graph.startEditingAtCell(); + } + }, null, null, 'F2/Enter'); + this.addAction('editData...', function() + { + var cell = graph.getSelectionCell() || graph.getModel().getRoot(); + ui.showDataDialog(cell); + }, null, null, Editor.ctrlKey + '+M'); + this.addAction('editTooltip...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var tooltip = ''; + + if (mxUtils.isNode(cell.value)) + { + var tmp = cell.value.getAttribute('tooltip'); + + if (tmp != null) + { + tooltip = tmp; + } + } + + var dlg = new TextareaDialog(ui, mxResources.get('editTooltip') + ':', tooltip, function(newValue) + { + graph.setTooltipForCell(cell, newValue); + }); + ui.showDialog(dlg.container, 320, 200, true, true); + dlg.init(); + } + }, null, null, 'Alt+Shift+T'); + this.addAction('openLink', function() + { + var link = graph.getLinkForCell(graph.getSelectionCell()); + + if (link != null) + { + graph.openLink(link); + } + }); + this.addAction('editLink...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var value = graph.getLinkForCell(cell) || ''; + + ui.showLinkDialog(value, mxResources.get('apply'), function(link) + { + link = mxUtils.trim(link); + graph.setLinkForCell(cell, (link.length > 0) ? link : null); + }); + } + }, null, null, 'Alt+Shift+L'); + this.addAction('insertLink...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + ui.showLinkDialog('', mxResources.get('insert'), function(link, docs) + { + link = mxUtils.trim(link); + + if (link.length > 0) + { + var icon = null; + var title = graph.getLinkTitle(link); + + if (docs != null && docs.length > 0) + { + icon = docs[0].iconUrl; + title = docs[0].name || docs[0].type; + title = title.charAt(0).toUpperCase() + title.substring(1); + + if (title.length > 30) + { + title = title.substring(0, 30) + '...'; + } + } + + var pt = graph.getFreeInsertPoint(); + var linkCell = new mxCell(title, new mxGeometry(pt.x, pt.y, 100, 40), + 'fontColor=#0000EE;fontStyle=4;rounded=1;overflow=hidden;' + ((icon != null) ? + 'shape=label;imageWidth=16;imageHeight=16;spacingLeft=26;align=left;image=' + icon : + 'spacing=10;')); + linkCell.vertex = true; + + graph.setLinkForCell(linkCell, link); + graph.cellSizeUpdated(linkCell, true); + + graph.getModel().beginUpdate(); + try + { + linkCell = graph.addCell(linkCell); + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [linkCell])); + } + finally + { + graph.getModel().endUpdate(); + } + + graph.setSelectionCell(linkCell); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + }); + } + }).isEnabled = isGraphEnabled; + this.addAction('link...', mxUtils.bind(this, function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled()) + { + if (graph.cellEditor.isContentEditing()) + { + var elt = graph.getSelectedElement(); + var link = graph.getParentByName(elt, 'A', graph.cellEditor.textarea); + var oldValue = ''; + + // Workaround for FF returning the outermost selected element after double + // click on a DOM hierarchy with a link inside (but not as topmost element) + if (link == null && elt != null && elt.getElementsByTagName != null) + { + // Finds all links in the selected DOM and uses the link + // where the selection text matches its text content + var links = elt.getElementsByTagName('a'); + + for (var i = 0; i < links.length && link == null; i++) + { + if (links[i].textContent == elt.textContent) + { + graph.selectNode(links[i]); + link = links[i]; + } + } + } + + if (link != null && link.nodeName == 'A') + { + oldValue = link.getAttribute('href') || ''; + } + + var selState = graph.cellEditor.saveSelection(); + + ui.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value) + { + graph.cellEditor.restoreSelection(selState); + + if (value != null) + { + graph.insertLink(value); + } + })); + } + else if (graph.isSelectionEmpty()) + { + this.get('insertLink').funct(); + } + else + { + this.get('editLink').funct(); + } + } + })).isEnabled = isGraphEnabled; + this.addAction('autosize', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().getChildCount(cell)) + { + graph.updateGroupBounds([cell], 20); + } + else + { + var state = graph.view.getState(cell); + var geo = graph.getCellGeometry(cell); + + if (graph.getModel().isVertex(cell) && state != null && state.text != null && + geo != null && graph.isWrapping(cell)) + { + geo = geo.clone(); + geo.height = state.text.boundingBox.height / graph.view.scale; + graph.getModel().setGeometry(cell, geo); + } + else + { + graph.updateCellSize(cell); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+Shift+Y'); + this.addAction('formattedText', function() + { + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + var value = '1'; + graph.stopEditing(); + + graph.getModel().beginUpdate(); + try + { + if (state.style['html'] == '1') + { + value = null; + var label = graph.convertValueToString(state.cell); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Removes newlines from HTML and converts breaks to newlines + // to match the HTML output in plain text + label = label.replace(/\n/g, '').replace(//g, '\n'); + } + + // Removes HTML tags + var temp = document.createElement('div'); + temp.innerHTML = label; + label = mxUtils.extractTextWithWhitespace(temp.childNodes); + + graph.cellLabelChanged(state.cell, label); + } + else + { + // Converts HTML tags to text + var label = mxUtils.htmlEntities(graph.convertValueToString(state.cell), false); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Converts newlines in plain text to breaks in HTML + // to match the plain text output + label = label.replace(/\n/g, '
'); + } + + graph.cellLabelChanged(state.cell, graph.sanitizeHtml(label)); + } + + graph.setCellStyles('html', value); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['html'], + 'values', [(value != null) ? value : '0'], 'cells', + graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('wordWrap', function() + { + var state = graph.getView().getState(graph.getSelectionCell()); + var value = 'wrap'; + + graph.stopEditing(); + + if (state != null && state.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') + { + value = null; + } + + graph.setCellStyles(mxConstants.STYLE_WHITE_SPACE, value); + }); + this.addAction('rotation', function() + { + var value = '0'; + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + value = state.style[mxConstants.STYLE_ROTATION] || value; + } + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), function(newValue) + { + if (newValue != null && newValue.length > 0) + { + graph.setCellStyles(mxConstants.STYLE_ROTATION, newValue); + } + }, mxResources.get('enterValue') + ' (' + mxResources.get('rotation') + ' 0-360)'); + + ui.showDialog(dlg.container, 375, 80, true, true); + dlg.init(); + }); + // View actions + this.addAction('resetView', function() + { + graph.zoomTo(1); + ui.resetScrollbars(); + }, null, null, Editor.ctrlKey + '+H'); + this.addAction('zoomIn', function(evt) { graph.zoomIn(); }, null, null, Editor.ctrlKey + ' + (Numpad) / Alt+Mousewheel'); + this.addAction('zoomOut', function(evt) { graph.zoomOut(); }, null, null, Editor.ctrlKey + ' - (Numpad) / Alt+Mousewheel'); + this.addAction('fitWindow', function() { graph.fit(); }, null, null, Editor.ctrlKey + '+Shift+H'); + this.addAction('fitPage', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + var scale = Math.floor(20 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = pad.y * graph.view.scale - 1; + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, (graph.container.scrollWidth - graph.container.clientWidth) / 2) - 1; + } + }), null, null, Editor.ctrlKey + '+J'); + this.addAction('fitTwoPages', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + + var scale = Math.floor(20 * Math.min(cw / (2 * fmt.width) / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = Math.min(pad.y, (graph.container.scrollHeight - graph.container.clientHeight) / 2); + graph.container.scrollLeft = Math.min(pad.x, (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + }), null, null, Editor.ctrlKey + '+Shift+J'); + this.addAction('fitPageWidth', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + + var scale = Math.floor(20 * cw / fmt.width / ps) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, + (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + })); + this.put('customZoom', new Action(mxResources.get('custom') + '...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.getView().getScale() * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + graph.zoomTo(val / 100); + } + }), mxResources.get('zoom') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }), null, null, Editor.ctrlKey + '+0')); + this.addAction('pageScale...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.pageScale * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + ui.setPageScale(val / 100); + } + }), mxResources.get('pageScale') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + })); + + // Option actions + var action = null; + action = this.addAction('grid', function() + { + graph.setGridEnabled(!graph.isGridEnabled()); + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, null, null, Editor.ctrlKey + '+Shift+G'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.isGridEnabled(); }); + action.setEnabled(false); + + action = this.addAction('guides', function() + { + graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled; + ui.fireEvent(new mxEventObject('guidesEnabledChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; }); + action.setEnabled(false); + + action = this.addAction('tooltips', function() + { + graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); }); + + action = this.addAction('collapseExpand', function() + { + var change = new ChangePageSetup(ui); + change.ignoreColor = true; + change.ignoreImage = true; + change.foldingEnabled = !graph.foldingEnabled; + + graph.model.execute(change); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.foldingEnabled; }); + action.isEnabled = isGraphEnabled; + action = this.addAction('scrollbars', function() + { + ui.setScrollbars(!ui.hasScrollbars()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.scrollbars; }); + action = this.addAction('pageView', mxUtils.bind(this, function() + { + ui.setPageVisible(!graph.pageVisible); + })); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.pageVisible; }); + action = this.addAction('connectionArrows', function() + { + graph.connectionArrowsEnabled = !graph.connectionArrowsEnabled; + ui.fireEvent(new mxEventObject('connectionArrowsChanged')); + }, null, null, 'Alt+Shift+A'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionArrowsEnabled; }); + action = this.addAction('connectionPoints', function() + { + graph.setConnectable(!graph.connectionHandler.isEnabled()); + ui.fireEvent(new mxEventObject('connectionPointsChanged')); + }, null, null, 'Alt+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); }); + action = this.addAction('copyConnect', function() + { + graph.connectionHandler.setCreateTarget(!graph.connectionHandler.isCreateTarget()); + ui.fireEvent(new mxEventObject('copyConnectChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isCreateTarget(); }); + action.isEnabled = isGraphEnabled; + action = this.addAction('autosave', function() + { + ui.editor.setAutosave(!ui.editor.autosave); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return ui.editor.autosave; }); + action.isEnabled = isGraphEnabled; + action.visible = false; + + // Help actions + this.addAction('help', function() + { + var ext = ''; + + if (mxResources.isLanguageSupported(mxClient.language)) + { + ext = '_' + mxClient.language; + } + + graph.openLink(RESOURCES_PATH + '/help' + ext + '.html'); + }); + + var showingAbout = false; + + this.put('about', new Action(mxResources.get('about') + ' Graph Editor...', function() + { + if (!showingAbout) + { + ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true, function() + { + showingAbout = false; + }); + + showingAbout = true; + } + }, null, null, 'F1')); + + // Font style actions + var toggleFontStyle = mxUtils.bind(this, function(key, style, fn, shortcut) + { + return this.addAction(key, function() + { + if (fn != null && graph.cellEditor.isContentEditing()) + { + fn(); + } + else + { + graph.stopEditing(false); + + graph.getModel().beginUpdate(); + try + { + graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style); + + // Removes bold and italic tags and CSS styles inside labels + if ((style & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontWeight = null; + + if (elt.nodeName == 'B') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontStyle = null; + + if (elt.nodeName == 'I') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.textDecoration = null; + + if (elt.nodeName == 'U') + { + graph.replaceElement(elt); + } + }); + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, shortcut); + }); + + toggleFontStyle('bold', mxConstants.FONT_BOLD, function() { document.execCommand('bold', false, null); }, Editor.ctrlKey + '+B'); + toggleFontStyle('italic', mxConstants.FONT_ITALIC, function() { document.execCommand('italic', false, null); }, Editor.ctrlKey + '+I'); + toggleFontStyle('underline', mxConstants.FONT_UNDERLINE, function() { document.execCommand('underline', false, null); }, Editor.ctrlKey + '+U'); + + // Color actions + this.addAction('fontColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR, 'forecolor', '000000'); }); + this.addAction('strokeColor...', function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); }); + this.addAction('fillColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); }); + this.addAction('gradientColor...', function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); }); + this.addAction('backgroundColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'backcolor'); }); + this.addAction('borderColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); }); + + // Format actions + this.addAction('vertical', function() { ui.menus.toggleStyle(mxConstants.STYLE_HORIZONTAL, true); }); + this.addAction('shadow', function() { ui.menus.toggleStyle(mxConstants.STYLE_SHADOW); }); + this.addAction('solid', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, null); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', [null, null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dashed', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dotted', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, '1 4'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', '1 4'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('sharp', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('rounded', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '1'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['1', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('toggleRounded', function() + { + if (!graph.isSelectionEmpty() && graph.isEnabled()) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + var state = graph.view.getState(cells[0]); + var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); + var value = (mxUtils.getValue(style, mxConstants.STYLE_ROUNDED, '0') == '1') ? '0' : '1'; + + graph.setCellStyles(mxConstants.STYLE_ROUNDED, value); + graph.setCellStyles(mxConstants.STYLE_CURVED, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', [value, '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('curved', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '1'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '1'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('collapsible', function() + { + var state = graph.view.getState(graph.getSelectionCell()); + var value = '1'; + + if (state != null && graph.getFoldingImage(state) != null) + { + value = '0'; + } + + graph.setCellStyles('collapsible', value); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['collapsible'], + 'values', [value], 'cells', graph.getSelectionCells())); + }); + this.addAction('editStyle...', mxUtils.bind(this, function() + { + var cells = graph.getSelectionCells(); + + if (cells != null && cells.length > 0) + { + var model = graph.getModel(); + + var dlg = new TextareaDialog(this.editorUi, mxResources.get('editStyle') + ':', + model.getStyle(cells[0]) || '', function(newValue) + { + if (newValue != null) + { + graph.setCellStyle(mxUtils.trim(newValue), cells); + } + }, null, null, 400, 220); + this.editorUi.showDialog(dlg.container, 420, 300, true, true); + dlg.init(); + } + }), null, null, Editor.ctrlKey + '+E'); + this.addAction('setAsDefaultStyle', function() + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + ui.setDefaultStyle(graph.getSelectionCell()); + } + }, null, null, Editor.ctrlKey + '+Shift+D'); + this.addAction('clearDefaultStyle', function() + { + if (graph.isEnabled()) + { + ui.clearDefaultStyle(); + } + }, null, null, Editor.ctrlKey + '+Shift+R'); + this.addAction('addWaypoint', function() + { + var cell = graph.getSelectionCell(); + + if (cell != null && graph.getModel().isEdge(cell)) + { + var handler = editor.graph.selectionCellsHandler.getHandler(cell); + + if (handler instanceof mxEdgeHandler) + { + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + + var parent = graph.getModel().getParent(cell); + var pgeo = graph.getCellGeometry(parent); + + while (graph.getModel().isVertex(parent) && pgeo != null) + { + dx += pgeo.x; + dy += pgeo.y; + + parent = graph.getModel().getParent(parent); + pgeo = graph.getCellGeometry(parent); + } + + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + handler.addPointAt(handler.state, x, y); + } + } + }); + this.addAction('removeWaypoint', function() + { + // TODO: Action should run with "this" set to action + var rmWaypointAction = ui.actions.get('removeWaypoint'); + + if (rmWaypointAction.handler != null) + { + // NOTE: Popupevent handled and action updated in Menus.createPopupMenu + rmWaypointAction.handler.removePoint(rmWaypointAction.handler.state, rmWaypointAction.index); + } + }); + this.addAction('clearWaypoints', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + cells = graph.addAllEdges(cells); + + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.points = null; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shift+C'); + action = this.addAction('subscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('subscript', false, null); + } + }), null, null, Editor.ctrlKey + '+,'); + action = this.addAction('superscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('superscript', false, null); + } + }), null, null, Editor.ctrlKey + '+.'); + this.addAction('image...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + var title = mxResources.get('image') + ' (' + mxResources.get('url') + '):'; + var state = graph.getView().getState(graph.getSelectionCell()); + var value = ''; + + if (state != null) + { + value = state.style[mxConstants.STYLE_IMAGE] || value; + } + + var selectionState = graph.cellEditor.saveSelection(); + + ui.showImageDialog(title, value, function(newValue, w, h) + { + // Inserts image into HTML text + if (graph.cellEditor.isContentEditing()) + { + graph.cellEditor.restoreSelection(selectionState); + graph.insertImage(newValue, w, h); + } + else + { + var cells = graph.getSelectionCells(); + + if (newValue != null && (newValue.length > 0 || cells.length > 0)) + { + var select = null; + + graph.getModel().beginUpdate(); + try + { + // Inserts new cell if no cell is selected + if (cells.length == 0) + { + var pt = graph.getFreeInsertPoint(); + cells = [graph.insertVertex(graph.getDefaultParent(), null, '', pt.x, pt.y, w, h, + 'shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;')]; + select = cells; + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); + } + + graph.setCellStyles(mxConstants.STYLE_IMAGE, (newValue.length > 0) ? newValue : null, cells); + + // Sets shape only if not already shape with image (label or image) + var state = graph.view.getState(cells[0]); + var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); + + if (style[mxConstants.STYLE_SHAPE] != 'image' && style[mxConstants.STYLE_SHAPE] != 'label') + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, 'image', cells); + } + else if (newValue.length == 0) + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, null, cells); + } + + if (graph.getSelectionCount() == 1) + { + if (w != null && h != null) + { + var cell = cells[0]; + var geo = graph.getModel().getGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.width = w; + geo.height = h; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + if (select != null) + { + graph.setSelectionCells(select); + graph.scrollCellToVisible(select[0]); + } + } + } + }, graph.cellEditor.isContentEditing(), !graph.cellEditor.isContentEditing()); + } + }).isEnabled = isGraphEnabled; + this.addAction('insertImage...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.clearSelection(); + ui.actions.get('image').funct(); + } + }).isEnabled = isGraphEnabled; + action = this.addAction('layers', mxUtils.bind(this, function() + { + if (this.layersWindow == null) + { + // LATER: Check outline window for initial placement + this.layersWindow = new LayersWindow(ui, document.body.offsetWidth - 280, 120, 220, 180); + this.layersWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('layers')); + } + else + { + this.layersWindow.window.setVisible(!this.layersWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+L'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.layersWindow != null && this.layersWindow.window.isVisible(); })); + action = this.addAction('formatPanel', mxUtils.bind(this, function() + { + ui.toggleFormatPanel(); + }), null, null, Editor.ctrlKey + '+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return ui.formatWidth > 0; })); + action = this.addAction('outline', mxUtils.bind(this, function() + { + if (this.outlineWindow == null) + { + // LATER: Check layers window for initial placement + this.outlineWindow = new OutlineWindow(ui, document.body.offsetWidth - 260, 100, 180, 180); + this.outlineWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('outline')); + } + else + { + this.outlineWindow.window.setVisible(!this.outlineWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+O'); + + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.outlineWindow != null && this.outlineWindow.window.isVisible(); })); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut) +{ + var title; + + if (key.substring(key.length - 3) == '...') + { + key = key.substring(0, key.length - 3); + title = mxResources.get(key) + '...'; + } + else + { + title = mxResources.get(key); + } + + return this.put(key, new Action(title, funct, enabled, iconCls, shortcut)); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.put = function(name, action) +{ + this.actions[name] = action; + + return action; +}; + +/** + * Returns the action for the given name or null if no such action exists. + */ +Actions.prototype.get = function(name) +{ + return this.actions[name]; +}; + +/** + * Constructs a new action for the given parameters. + */ +function Action(label, funct, enabled, iconCls, shortcut) +{ + mxEventSource.call(this); + this.label = label; + this.funct = this.createFunction(funct); + this.enabled = (enabled != null) ? enabled : true; + this.iconCls = iconCls; + this.shortcut = shortcut; + this.visible = true; +}; + +// Action inherits from mxEventSource +mxUtils.extend(Action, mxEventSource); + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.createFunction = function(funct) +{ + return funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setEnabled = function(value) +{ + if (this.enabled != value) + { + this.enabled = value; + this.fireEvent(new mxEventObject('stateChanged')); + } +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isEnabled = function() +{ + return this.enabled; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setToggleAction = function(value) +{ + this.toggleAction = value; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setSelectedCallback = function(funct) +{ + this.selectedCallback = funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isSelected = function() +{ + return this.selectedCallback(); +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Dialogs.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Dialogs.js new file mode 100644 index 00000000..16bec15b --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Dialogs.js @@ -0,0 +1,2542 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new open dialog. + */ +var OpenDialog = function() +{ + var iframe = document.createElement('iframe'); + iframe.style.backgroundColor = 'transparent'; + iframe.allowTransparency = 'true'; + iframe.style.borderStyle = 'none'; + iframe.style.borderWidth = '0px'; + iframe.style.overflow = 'hidden'; + iframe.frameBorder = '0'; + + // Adds padding as a workaround for box model in older IE versions + var dx = (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) ? 20 : 0; + + iframe.setAttribute('width', (((Editor.useLocalStorage) ? 640 : 320) + dx) + 'px'); + iframe.setAttribute('height', (((Editor.useLocalStorage) ? 480 : 220) + dx) + 'px'); + iframe.setAttribute('src', OPEN_FORM); + + this.container = iframe; +}; + +/** + * Constructs a new color dialog. + */ +var ColorDialog = function(editorUi, color, apply, cancelFn) +{ + this.editorUi = editorUi; + + var input = document.createElement('input'); + input.style.marginBottom = '10px'; + input.style.width = '216px'; + + // Required for picker to render in IE + if (mxClient.IS_IE) + { + input.style.marginTop = '10px'; + document.body.appendChild(input); + } + + this.init = function() + { + if (!mxClient.IS_TOUCH) + { + input.focus(); + } + }; + + var picker = new jscolor.color(input); + picker.pickerOnfocus = false; + picker.showPicker(); + + var div = document.createElement('div'); + jscolor.picker.box.style.position = 'relative'; + jscolor.picker.box.style.width = '230px'; + jscolor.picker.box.style.height = '100px'; + jscolor.picker.box.style.paddingBottom = '10px'; + div.appendChild(jscolor.picker.box); + + var center = document.createElement('center'); + + function createRecentColorTable() + { + var table = addPresets((ColorDialog.recentColors.length == 0) ? ['FFFFFF'] : + ColorDialog.recentColors, 11, 'FFFFFF', true); + table.style.marginBottom = '8px'; + + return table; + }; + + function addPresets(presets, rowLength, defaultColor, addResetOption) + { + rowLength = (rowLength != null) ? rowLength : 12; + var table = document.createElement('table'); + table.style.borderCollapse = 'collapse'; + table.setAttribute('cellspacing', '0'); + table.style.marginBottom = '20px'; + table.style.cellSpacing = '0px'; + var tbody = document.createElement('tbody'); + table.appendChild(tbody); + + var rows = presets.length / rowLength; + + for (var row = 0; row < rows; row++) + { + var tr = document.createElement('tr'); + + for (var i = 0; i < rowLength; i++) + { + (function(clr) + { + var td = document.createElement('td'); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + + if (clr == null) + { + clr = defaultColor; + } + + if (clr == 'none') + { + td.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')'; + } + else + { + td.style.backgroundColor = '#' + clr; + } + + tr.appendChild(td); + + if (clr != null) + { + td.style.cursor = 'pointer'; + + mxEvent.addListener(td, 'click', function() + { + if (clr == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(clr); + } + }); + } + })(presets[row * rowLength + i]); + } + + tbody.appendChild(tr); + } + + if (addResetOption) + { + var td = document.createElement('td'); + td.setAttribute('title', mxResources.get('reset')); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + td.style.backgroundImage = 'url(\'' + Dialog.prototype.closeImage + '\')'; + td.style.backgroundPosition = 'center center'; + td.style.backgroundRepeat = 'no-repeat'; + td.style.cursor = 'pointer'; + + tr.appendChild(td); + + mxEvent.addListener(td, 'click', function() + { + ColorDialog.resetRecentColors(); + table.parentNode.replaceChild(createRecentColorTable(), table); + }); + } + + center.appendChild(table); + + return table; + }; + + div.appendChild(input); + mxUtils.br(div); + + // Adds recent colors + createRecentColorTable(); + + // Adds presets + var table = addPresets(this.presetColors); + table.style.marginBottom = '8px'; + table = addPresets(this.defaultColors); + table.style.marginBottom = '16px'; + + div.appendChild(center); + + var buttons = document.createElement('div'); + buttons.style.textAlign = 'right'; + buttons.style.whiteSpace = 'nowrap'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + var applyFunction = (apply != null) ? apply : this.createApplyFunction(); + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + var color = input.value; + ColorDialog.addRecentColor(color, 12); + + if (color != 'none' && color.charAt(0) != '#') + { + color = '#' + color; + } + + applyFunction(color); + editorUi.hideDialog(); + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + buttons.appendChild(applyBtn); + + if (!editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + if (color != null) + { + if (color == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(color); + } + } + + div.appendChild(buttons); + this.picker = picker; + this.colorInput = input; + + // LATER: Only fires if input if focused, should always + // fire if this dialog is showing. + mxEvent.addListener(div, 'keydown', function(e) + { + if (e.keyCode == 27) + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + + mxEvent.consume(e); + } + }); + + this.container = div; +}; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.presetColors = ['E6D0DE', 'CDA2BE', 'B5739D', 'E1D5E7', 'C3ABD0', 'A680B8', 'D4E1F5', 'A9C4EB', '7EA6E0', 'D5E8D4', '9AC7BF', '67AB9F', 'D5E8D4', 'B9E0A5', '97D077', 'FFF2CC', 'FFE599', 'FFD966', 'FFF4C3', 'FFCE9F', 'FFB570', 'F8CECC', 'F19C99', 'EA6B66']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.defaultColors = ['none', 'FFFFFF', 'E6E6E6', 'CCCCCC', 'B3B3B3', '999999', '808080', '666666', '4D4D4D', '333333', '1A1A1A', '000000', 'FFCCCC', 'FFE6CC', 'FFFFCC', 'E6FFCC', 'CCFFCC', 'CCFFE6', 'CCFFFF', 'CCE5FF', 'CCCCFF', 'E5CCFF', 'FFCCFF', 'FFCCE6', + 'FF9999', 'FFCC99', 'FFFF99', 'CCFF99', '99FF99', '99FFCC', '99FFFF', '99CCFF', '9999FF', 'CC99FF', 'FF99FF', 'FF99CC', 'FF6666', 'FFB366', 'FFFF66', 'B3FF66', '66FF66', '66FFB3', '66FFFF', '66B2FF', '6666FF', 'B266FF', 'FF66FF', 'FF66B3', 'FF3333', 'FF9933', 'FFFF33', + '99FF33', '33FF33', '33FF99', '33FFFF', '3399FF', '3333FF', '9933FF', 'FF33FF', 'FF3399', 'FF0000', 'FF8000', 'FFFF00', '80FF00', '00FF00', '00FF80', '00FFFF', '007FFF', '0000FF', '7F00FF', 'FF00FF', 'FF0080', 'CC0000', 'CC6600', 'CCCC00', '66CC00', '00CC00', '00CC66', + '00CCCC', '0066CC', '0000CC', '6600CC', 'CC00CC', 'CC0066', '990000', '994C00', '999900', '4D9900', '009900', '00994D', '009999', '004C99', '000099', '4C0099', '990099', '99004D', '660000', '663300', '666600', '336600', '006600', '006633', '006666', '003366', '000066', + '330066', '660066', '660033', '330000', '331A00', '333300', '1A3300', '003300', '00331A', '003333', '001933', '000033', '190033', '330033', '33001A']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.createApplyFunction = function() +{ + return mxUtils.bind(this, function(color) + { + var graph = this.editorUi.editor.graph; + + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(this.currentColorKey, color); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [this.currentColorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); +}; + +/** + * + */ +ColorDialog.recentColors = []; + +/** + * Adds recent color for later use. + */ +ColorDialog.addRecentColor = function(color, max) +{ + if (color != null) + { + mxUtils.remove(color, ColorDialog.recentColors); + ColorDialog.recentColors.splice(0, 0, color); + + if (ColorDialog.recentColors.length >= max) + { + ColorDialog.recentColors.pop(); + } + } +}; + +/** + * Adds recent color for later use. + */ +ColorDialog.resetRecentColors = function() +{ + ColorDialog.recentColors = []; +}; + +/** + * Constructs a new about dialog. + */ +var AboutDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.setAttribute('align', 'center'); + var h3 = document.createElement('h3'); + mxUtils.write(h3, mxResources.get('about') + ' GraphEditor'); + div.appendChild(h3); + var img = document.createElement('img'); + img.style.border = '0px'; + img.setAttribute('width', '176'); + img.setAttribute('width', '151'); + img.setAttribute('src', IMAGE_PATH + '/logo.png'); + div.appendChild(img); + mxUtils.br(div); + mxUtils.write(div, 'Powered by mxGraph ' + mxClient.VERSION); + mxUtils.br(div); + var link = document.createElement('a'); + link.setAttribute('href', 'http://www.jgraph.com/'); + link.setAttribute('target', '_blank'); + mxUtils.write(link, 'www.jgraph.com'); + div.appendChild(link); + mxUtils.br(div); + mxUtils.br(div); + var closeBtn = mxUtils.button(mxResources.get('close'), function() + { + editorUi.hideDialog(); + }); + closeBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(closeBtn); + + this.container = div; +}; + +/** + * Constructs a new filename dialog. + */ +var FilenameDialog = function(editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn) +{ + closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.style.marginTop = '8px'; + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + td.style.fontSize = '10pt'; + td.style.width = '120px'; + mxUtils.write(td, (label || mxResources.get('filename')) + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', filename || ''); + nameInput.style.marginLeft = '4px'; + nameInput.style.width = '180px'; + + var genericBtn = mxUtils.button(buttonText, function() + { + if (validateFn == null || validateFn(nameInput.value)) + { + if (closeOnBtn) + { + editorUi.hideDialog(); + } + + fn(nameInput.value); + } + }); + genericBtn.className = 'geBtn gePrimaryBtn'; + + this.init = function() + { + if (label == null && content != null) + { + return; + } + + nameInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) + { + nameInput.select(); + } + else + { + document.execCommand('selectAll', false, null); + } + + // Installs drag and drop handler for links + if (Graph.fileSupport) + { + // Setup the dnd listeners + var dlg = table.parentNode; + var graph = editorUi.editor.graph; + var dropElt = null; + + mxEvent.addListener(dlg, 'dragleave', function(evt) + { + if (dropElt != null) + { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function(evt) + { + // IE 10 does not implement pointer-events so it can't have a drop highlight + if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10)) + { + dropElt = nameInput; + dropElt.style.backgroundColor = '#ebf2f9'; + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + + mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function(evt) + { + if (dropElt != null) + { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) + { + nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list')); + genericBtn.click(); + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + } + }; + + td = document.createElement('td'); + td.appendChild(nameInput); + row.appendChild(td); + + if (label != null || content == null) + { + tbody.appendChild(row); + } + + if (content != null) + { + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.appendChild(content); + row.appendChild(td); + tbody.appendChild(row); + } + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '20px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (helpLink != null) + { + var helpBtn = mxUtils.button(mxResources.get('help'), function() + { + editorUi.editor.graph.openLink(helpLink); + }); + + helpBtn.className = 'geBtn'; + td.appendChild(helpBtn); + } + + mxEvent.addListener(nameInput, 'keypress', function(e) + { + if (e.keyCode == 13) + { + genericBtn.click(); + } + }); + + td.appendChild(genericBtn); + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + + this.container = table; +}; + +/** + * Constructs a new textarea dialog. + */ +var TextareaDialog = function(editorUi, title, url, fn, cancelFn, cancelTitle, w, h, addButtons, noHide, noWrap, applyTitle) +{ + w = (w != null) ? w : 300; + h = (h != null) ? h : 120; + noHide = (noHide != null) ? noHide : false; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, title); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + + var nameInput = document.createElement('textarea'); + + if (noWrap) + { + nameInput.setAttribute('wrap', 'off'); + } + + nameInput.setAttribute('spellcheck', 'false'); + nameInput.setAttribute('autocorrect', 'off'); + nameInput.setAttribute('autocomplete', 'off'); + nameInput.setAttribute('autocapitalize', 'off'); + + mxUtils.write(nameInput, url || ''); + nameInput.style.resize = 'none'; + nameInput.style.width = w + 'px'; + nameInput.style.height = h + 'px'; + + this.textarea = nameInput; + + this.init = function() + { + nameInput.focus(); + nameInput.scrollTop = 0; + }; + + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.style.paddingTop = '14px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(cancelTitle || mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (addButtons != null) + { + addButtons(td); + } + + if (fn != null) + { + var genericBtn = mxUtils.button(applyTitle || mxResources.get('apply'), function() + { + if (!noHide) + { + editorUi.hideDialog(); + } + + fn(nameInput.value); + }); + + genericBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(genericBtn); + } + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Constructs a new edit file dialog. + */ +var EditDiagramDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.style.textAlign = 'right'; + var textarea = document.createElement('textarea'); + textarea.setAttribute('wrap', 'off'); + textarea.setAttribute('spellcheck', 'false'); + textarea.setAttribute('autocorrect', 'off'); + textarea.setAttribute('autocomplete', 'off'); + textarea.setAttribute('autocapitalize', 'off'); + textarea.style.overflow = 'auto'; + textarea.style.resize = 'none'; + textarea.style.width = '600px'; + textarea.style.height = '360px'; + textarea.style.marginBottom = '16px'; + + textarea.value = mxUtils.getPrettyXml(editorUi.editor.getGraphXml()); + div.appendChild(textarea); + + this.init = function() + { + textarea.focus(); + }; + + // Enables dropping files + if (Graph.fileSupport) + { + function handleDrop(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + + if (evt.dataTransfer.files.length > 0) + { + var file = evt.dataTransfer.files[0]; + var reader = new FileReader(); + + reader.onload = function(e) + { + textarea.value = e.target.result; + }; + + reader.readAsText(file); + } + else + { + textarea.value = editorUi.extractGraphModelFromEvent(evt); + } + }; + + function handleDragOver(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }; + + // Setup the dnd listeners. + textarea.addEventListener('dragover', handleDragOver, false); + textarea.addEventListener('drop', handleDrop, false); + } + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + var select = document.createElement('select'); + select.style.width = '180px'; + select.className = 'geBtn'; + + if (editorUi.editor.graph.isEnabled()) + { + var replaceOption = document.createElement('option'); + replaceOption.setAttribute('value', 'replace'); + mxUtils.write(replaceOption, mxResources.get('replaceExistingDrawing')); + select.appendChild(replaceOption); + } + + var newOption = document.createElement('option'); + newOption.setAttribute('value', 'new'); + mxUtils.write(newOption, mxResources.get('openInNewWindow')); + + if (EditDiagramDialog.showNewWindowOption) + { + select.appendChild(newOption); + } + + if (editorUi.editor.graph.isEnabled()) + { + var importOption = document.createElement('option'); + importOption.setAttribute('value', 'import'); + mxUtils.write(importOption, mxResources.get('addToExistingDrawing')); + select.appendChild(importOption); + } + + div.appendChild(select); + + var okBtn = mxUtils.button(mxResources.get('ok'), function() + { + // Removes all illegal control characters before parsing + var data = editorUi.editor.graph.zapGremlins(mxUtils.trim(textarea.value)); + var error = null; + + if (select.value == 'new') + { + window.openFile = new OpenFile(function() + { + editorUi.hideDialog(); + window.openFile = null; + }); + + window.openFile.setData(data, null); + editorUi.editor.graph.openLink(editorUi.getUrl()); + } + else if (select.value == 'replace') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + editorUi.editor.setGraphXml(mxUtils.parseXml(data).documentElement); + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + else if (select.value == 'import') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + var doc = mxUtils.parseXml(data); + var model = new mxGraphModel(); + var codec = new mxCodec(doc); + codec.decode(doc.documentElement, model); + + var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); + editorUi.editor.graph.setSelectionCells(editorUi.editor.graph.importCells(children)); + + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + + if (error != null) + { + mxUtils.alert(error.message); + } + }); + okBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(okBtn); + + if (!editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + this.container = div; +}; + +/** + * + */ +EditDiagramDialog.showNewWindowOption = true; + +/** + * Constructs a new export dialog. + */ +var ExportDialog = function(editorUi) +{ + var graph = editorUi.editor.graph; + var bounds = graph.getGraphBounds(); + var scale = graph.view.scale; + + var width = Math.ceil(bounds.width / scale); + var height = Math.ceil(bounds.height / scale); + + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.setAttribute('cellpadding', (mxClient.IS_SF) ? '0' : '2'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, mxResources.get('filename') + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', editorUi.editor.getOrCreateFilename()); + nameInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('format') + ':'); + + row.appendChild(td); + + var imageFormatSelect = document.createElement('select'); + imageFormatSelect.style.width = '180px'; + + var pngOption = document.createElement('option'); + pngOption.setAttribute('value', 'png'); + mxUtils.write(pngOption, mxResources.get('formatPng')); + imageFormatSelect.appendChild(pngOption); + + var gifOption = document.createElement('option'); + + if (ExportDialog.showGifOption) + { + gifOption.setAttribute('value', 'gif'); + mxUtils.write(gifOption, mxResources.get('formatGif')); + imageFormatSelect.appendChild(gifOption); + } + + var jpgOption = document.createElement('option'); + jpgOption.setAttribute('value', 'jpg'); + mxUtils.write(jpgOption, mxResources.get('formatJpg')); + imageFormatSelect.appendChild(jpgOption); + + var pdfOption = document.createElement('option'); + pdfOption.setAttribute('value', 'pdf'); + mxUtils.write(pdfOption, mxResources.get('formatPdf')); + imageFormatSelect.appendChild(pdfOption); + + var svgOption = document.createElement('option'); + svgOption.setAttribute('value', 'svg'); + mxUtils.write(svgOption, mxResources.get('formatSvg')); + imageFormatSelect.appendChild(svgOption); + + if (ExportDialog.showXmlOption) + { + var xmlOption = document.createElement('option'); + xmlOption.setAttribute('value', 'xml'); + mxUtils.write(xmlOption, mxResources.get('formatXml')); + imageFormatSelect.appendChild(xmlOption); + } + + td = document.createElement('td'); + td.appendChild(imageFormatSelect); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('zoom') + ' (%):'); + + row.appendChild(td); + + var zoomInput = document.createElement('input'); + zoomInput.setAttribute('type', 'number'); + zoomInput.setAttribute('value', '100'); + zoomInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(zoomInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('width') + ':'); + + row.appendChild(td); + + var widthInput = document.createElement('input'); + widthInput.setAttribute('value', width); + widthInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(widthInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('height') + ':'); + + row.appendChild(td); + + var heightInput = document.createElement('input'); + heightInput.setAttribute('value', height); + heightInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(heightInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('background') + ':'); + + row.appendChild(td); + + var transparentCheckbox = document.createElement('input'); + transparentCheckbox.setAttribute('type', 'checkbox'); + transparentCheckbox.checked = graph.background == null || graph.background == mxConstants.NONE; + + td = document.createElement('td'); + td.appendChild(transparentCheckbox); + mxUtils.write(td, mxResources.get('transparent')); + + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('borderWidth') + ':'); + + row.appendChild(td); + + var borderInput = document.createElement('input'); + borderInput.setAttribute('type', 'number'); + borderInput.setAttribute('value', ExportDialog.lastBorderValue); + borderInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(borderInput); + row.appendChild(td); + + tbody.appendChild(row); + table.appendChild(tbody); + + // Handles changes in the export format + function formatChanged() + { + var name = nameInput.value; + var dot = name.lastIndexOf('.'); + + if (dot > 0) + { + nameInput.value = name.substring(0, dot + 1) + imageFormatSelect.value; + } + else + { + nameInput.value = name + '.' + imageFormatSelect.value; + } + + if (imageFormatSelect.value === 'xml') + { + zoomInput.setAttribute('disabled', 'true'); + widthInput.setAttribute('disabled', 'true'); + heightInput.setAttribute('disabled', 'true'); + borderInput.setAttribute('disabled', 'true'); + } + else + { + zoomInput.removeAttribute('disabled'); + widthInput.removeAttribute('disabled'); + heightInput.removeAttribute('disabled'); + borderInput.removeAttribute('disabled'); + } + + if (imageFormatSelect.value === 'png' || imageFormatSelect.value === 'svg') + { + transparentCheckbox.removeAttribute('disabled'); + } + else + { + transparentCheckbox.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(imageFormatSelect, 'change', formatChanged); + formatChanged(); + + function checkValues() + { + if (widthInput.value * heightInput.value > MAX_AREA || widthInput.value <= 0) + { + widthInput.style.backgroundColor = 'red'; + } + else + { + widthInput.style.backgroundColor = ''; + } + + if (widthInput.value * heightInput.value > MAX_AREA || heightInput.value <= 0) + { + heightInput.style.backgroundColor = 'red'; + } + else + { + heightInput.style.backgroundColor = ''; + } + }; + + mxEvent.addListener(zoomInput, 'change', function() + { + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + zoomInput.value = parseFloat((s * 100).toFixed(2)); + + if (width > 0) + { + widthInput.value = Math.floor(width * s); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(widthInput, 'change', function() + { + var s = parseInt(widthInput.value) / width; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(heightInput, 'change', function() + { + var s = parseInt(heightInput.value) / height; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + widthInput.value = Math.floor(width * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.setAttribute('align', 'right'); + td.style.paddingTop = '22px'; + td.colSpan = 2; + + var saveBtn = mxUtils.button(mxResources.get('export'), mxUtils.bind(this, function() + { + if (parseInt(zoomInput.value) <= 0) + { + mxUtils.alert(mxResources.get('drawingEmpty')); + } + else + { + var name = nameInput.value; + var format = imageFormatSelect.value; + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + var b = Math.max(0, parseInt(borderInput.value)); + var bg = graph.background; + + if ((format == 'svg' || format == 'png') && transparentCheckbox.checked) + { + bg = null; + } + else if (bg == null || bg == mxConstants.NONE) + { + bg = '#ffffff'; + } + + ExportDialog.lastBorderValue = b; + ExportDialog.exportFile(editorUi, name, format, bg, s, b); + } + })); + saveBtn.className = 'geBtn gePrimaryBtn'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + td.appendChild(saveBtn); + } + else + { + td.appendChild(saveBtn); + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Remembers last value for border. + */ +ExportDialog.lastBorderValue = 0; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showGifOption = true; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showXmlOption = true; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.exportFile = function(editorUi, name, format, bg, s, b) +{ + var graph = editorUi.editor.graph; + + if (format == 'xml') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(editorUi.editor.getGraphXml()), name, format); + } + else if (format == 'svg') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(graph.getSvg(bg, s, b)), name, format); + } + else + { + var bounds = graph.getGraphBounds(); + + // New image export + var xmlDoc = mxUtils.createXmlDocument(); + var root = xmlDoc.createElement('output'); + xmlDoc.appendChild(root); + + // Renders graph. Offset will be multiplied with state's scale when painting state. + var xmlCanvas = new mxXmlCanvas2D(root); + xmlCanvas.translate(Math.floor((b / s - bounds.x) / graph.view.scale), + Math.floor((b / s - bounds.y) / graph.view.scale)); + xmlCanvas.scale(s / graph.view.scale); + + var imgExport = new mxImageExport() + imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); + + // Puts request data together + var param = 'xml=' + encodeURIComponent(mxUtils.getXml(root)); + var w = Math.ceil(bounds.width * s / graph.view.scale + 2 * b); + var h = Math.ceil(bounds.height * s / graph.view.scale + 2 * b); + + // Requests image if request is valid + if (param.length <= MAX_REQUEST_SIZE && w * h < MAX_AREA) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(EXPORT_URL, 'format=' + format + + '&filename=' + encodeURIComponent(name) + + '&bg=' + ((bg != null) ? bg : 'none') + + '&w=' + w + '&h=' + h + '&' + param); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + } + } +}; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.saveLocalFile = function(editorUi, data, filename, format) +{ + if (data.length < MAX_REQUEST_SIZE) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(SAVE_URL, 'xml=' + encodeURIComponent(data) + '&filename=' + + encodeURIComponent(filename) + '&format=' + format); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + mxUtils.popup(xml); + } +}; + +/** + * Constructs a new metadata dialog. + */ +var EditDataDialog = function(ui, cell) +{ + var div = document.createElement('div'); + var graph = ui.editor.graph; + + var value = graph.getModel().getValue(cell); + + // Converts the value to an XML node + if (!mxUtils.isNode(value)) + { + var doc = mxUtils.createXmlDocument(); + var obj = doc.createElement('object'); + obj.setAttribute('label', value || ''); + value = obj; + } + + // Creates the dialog contents + var form = new mxForm('properties'); + form.table.style.width = '100%'; + + var attrs = value.attributes; + var names = []; + var texts = []; + var count = 0; + + var id = EditDataDialog.getDisplayIdForCell(ui, cell); + + // FIXME: Fix remove button for quirks mode + var addRemoveButton = function(text, name) + { + var wrapper = document.createElement('div'); + wrapper.style.position = 'relative'; + wrapper.style.paddingRight = '20px'; + wrapper.style.boxSizing = 'border-box'; + wrapper.style.width = '100%'; + + var removeAttr = document.createElement('a'); + var img = mxUtils.createImage(Dialog.prototype.closeImage); + img.style.height = '9px'; + img.style.fontSize = '9px'; + img.style.marginBottom = (mxClient.IS_IE11) ? '-1px' : '5px'; + + removeAttr.className = 'geButton'; + removeAttr.setAttribute('title', mxResources.get('delete')); + removeAttr.style.position = 'absolute'; + removeAttr.style.top = '4px'; + removeAttr.style.right = '0px'; + removeAttr.style.margin = '0px'; + removeAttr.style.width = '9px'; + removeAttr.style.height = '9px'; + removeAttr.style.cursor = 'pointer'; + removeAttr.appendChild(img); + + var removeAttrFn = (function(name) + { + return function() + { + var count = 0; + + for (var j = 0; j < names.length; j++) + { + if (names[j] == name) + { + texts[j] = null; + form.table.deleteRow(count + ((id != null) ? 1 : 0)); + + break; + } + + if (texts[j] != null) + { + count++; + } + } + }; + })(name); + + mxEvent.addListener(removeAttr, 'click', removeAttrFn); + + var parent = text.parentNode; + wrapper.appendChild(text); + wrapper.appendChild(removeAttr); + parent.appendChild(wrapper); + }; + + var addTextArea = function(index, name, value) + { + names[index] = name; + texts[index] = form.addTextarea(names[count] + ':', value, 2); + texts[index].style.width = '100%'; + + addRemoveButton(texts[index], name); + }; + + var temp = []; + var isLayer = graph.getModel().getParent(cell) == graph.getModel().getRoot(); + + for (var i = 0; i < attrs.length; i++) + { + if ((isLayer || attrs[i].nodeName != 'label') && attrs[i].nodeName != 'placeholders') + { + temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); + } + } + + // Sorts by name + temp.sort(function(a, b) + { + if (a.name < b.name) + { + return -1; + } + else if (a.name > b.name) + { + return 1; + } + else + { + return 0; + } + }); + + if (id != null) + { + var text = document.createElement('input'); + text.style.width = '280px'; + text.style.textAlign = 'center'; + text.setAttribute('type', 'text'); + text.setAttribute('readOnly', 'true'); + text.setAttribute('value', id); + + form.addField(mxResources.get('id') + ':', text); + } + + for (var i = 0; i < temp.length; i++) + { + addTextArea(count, temp[i].name, temp[i].value); + count++; + } + + var top = document.createElement('div'); + top.style.cssText = 'position:absolute;left:30px;right:30px;overflow-y:auto;top:30px;bottom:80px;'; + top.appendChild(form.table); + + var newProp = document.createElement('div'); + newProp.style.whiteSpace = 'nowrap'; + newProp.style.marginTop = '6px'; + + var nameInput = document.createElement('input'); + nameInput.setAttribute('placeholder', mxResources.get('enterPropertyName')); + nameInput.setAttribute('type', 'text'); + nameInput.setAttribute('size', (mxClient.IS_IE || mxClient.IS_IE11) ? '18' : '22'); + nameInput.style.marginLeft = '2px'; + + newProp.appendChild(nameInput); + top.appendChild(newProp); + div.appendChild(top); + + var addBtn = mxUtils.button(mxResources.get('addProperty'), function() + { + var name = nameInput.value; + + // Avoid ':' in attribute names which seems to be valid in Chrome + if (name.length > 0 && name != 'label' && name != 'placeholders' && name.indexOf(':') < 0) + { + try + { + var idx = mxUtils.indexOf(names, name); + + if (idx >= 0 && texts[idx] != null) + { + texts[idx].focus(); + } + else + { + // Checks if the name is valid + var clone = value.cloneNode(false); + clone.setAttribute(name, ''); + + if (idx >= 0) + { + names.splice(idx, 1); + texts.splice(idx, 1); + } + + names.push(name); + var text = form.addTextarea(name + ':', '', 2); + text.style.width = '100%'; + texts.push(text); + addRemoveButton(text, name); + + text.focus(); + } + + nameInput.value = ''; + } + catch (e) + { + mxUtils.alert(e); + } + } + else + { + mxUtils.alert(mxResources.get('invalidName')); + } + }); + + this.init = function() + { + if (texts.length > 0) + { + texts[0].focus(); + } + else + { + nameInput.focus(); + } + }; + + addBtn.setAttribute('disabled', 'disabled'); + addBtn.style.marginLeft = '10px'; + addBtn.style.width = '144px'; + newProp.appendChild(addBtn); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + ui.hideDialog.apply(ui, arguments); + }); + + cancelBtn.className = 'geBtn'; + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + try + { + ui.hideDialog.apply(ui, arguments); + + // Clones and updates the value + value = value.cloneNode(true); + var removeLabel = false; + + for (var i = 0; i < names.length; i++) + { + if (texts[i] == null) + { + value.removeAttribute(names[i]); + } + else + { + value.setAttribute(names[i], texts[i].value); + removeLabel = removeLabel || (names[i] == 'placeholder' && + value.getAttribute('placeholders') == '1'); + } + } + + // Removes label if placeholder is assigned + if (removeLabel) + { + value.removeAttribute('label'); + } + + // Updates the value of the cell (undoable) + graph.getModel().setValue(cell, value); + } + catch (e) + { + mxUtils.alert(e); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + + function updateAddBtn() + { + if (nameInput.value.length > 0) + { + addBtn.removeAttribute('disabled'); + } + else + { + addBtn.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(nameInput, 'keyup', updateAddBtn); + + // Catches all changes that don't fire a keyup (such as paste via mouse) + mxEvent.addListener(nameInput, 'change', updateAddBtn); + + var buttons = document.createElement('div'); + buttons.style.cssText = 'position:absolute;left:30px;right:30px;text-align:right;bottom:30px;height:40px;' + + if (ui.editor.graph.getModel().isVertex(cell) || ui.editor.graph.getModel().isEdge(cell)) + { + var replace = document.createElement('span'); + replace.style.marginRight = '10px'; + var input = document.createElement('input'); + input.setAttribute('type', 'checkbox'); + input.style.marginRight = '6px'; + + if (value.getAttribute('placeholders') == '1') + { + input.setAttribute('checked', 'checked'); + input.defaultChecked = true; + } + + mxEvent.addListener(input, 'click', function() + { + if (value.getAttribute('placeholders') == '1') + { + value.removeAttribute('placeholders'); + } + else + { + value.setAttribute('placeholders', '1'); + } + }); + + replace.appendChild(input); + mxUtils.write(replace, mxResources.get('placeholders')); + + if (EditDataDialog.placeholderHelpLink != null) + { + var link = document.createElement('a'); + link.setAttribute('href', EditDataDialog.placeholderHelpLink); + link.setAttribute('title', mxResources.get('help')); + link.setAttribute('target', '_blank'); + link.style.marginLeft = '10px'; + link.style.cursor = 'help'; + + var icon = document.createElement('img'); + icon.setAttribute('border', '0'); + icon.setAttribute('valign', 'middle'); + icon.style.marginTop = (mxClient.IS_IE11) ? '0px' : '-4px'; + icon.setAttribute('src', Editor.helpImage); + link.appendChild(icon); + + replace.appendChild(link); + } + + buttons.appendChild(replace); + } + + if (ui.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + buttons.appendChild(applyBtn); + } + else + { + buttons.appendChild(applyBtn); + buttons.appendChild(cancelBtn); + } + + div.appendChild(buttons); + this.container = div; +}; + +/** + * Optional help link. + */ +EditDataDialog.getDisplayIdForCell = function(ui, cell) +{ + var id = null; + + if (ui.editor.graph.getModel().getParent(cell) != null) + { + id = cell.getId(); + } + + return id; +}; + +/** + * Optional help link. + */ +EditDataDialog.placeholderHelpLink = null; + +/** + * Constructs a new link dialog. + */ +var LinkDialog = function(editorUi, initialValue, btnLabel, fn) +{ + var div = document.createElement('div'); + mxUtils.write(div, mxResources.get('editLink') + ':'); + + var inner = document.createElement('div'); + inner.className = 'geTitle'; + inner.style.backgroundColor = 'transparent'; + inner.style.borderColor = 'transparent'; + inner.style.whiteSpace = 'nowrap'; + inner.style.textOverflow = 'clip'; + inner.style.cursor = 'default'; + + if (!mxClient.IS_VML) + { + inner.style.paddingRight = '20px'; + } + + var linkInput = document.createElement('input'); + linkInput.setAttribute('value', initialValue); + linkInput.setAttribute('placeholder', 'http://www.example.com/'); + linkInput.setAttribute('type', 'text'); + linkInput.style.marginTop = '6px'; + linkInput.style.width = '400px'; + linkInput.style.backgroundImage = 'url(\'' + Dialog.prototype.clearImage + '\')'; + linkInput.style.backgroundRepeat = 'no-repeat'; + linkInput.style.backgroundPosition = '100% 50%'; + linkInput.style.paddingRight = '14px'; + + var cross = document.createElement('div'); + cross.setAttribute('title', mxResources.get('reset')); + cross.style.position = 'relative'; + cross.style.left = '-16px'; + cross.style.width = '12px'; + cross.style.height = '14px'; + cross.style.cursor = 'pointer'; + + // Workaround for inline-block not supported in IE + cross.style.display = (mxClient.IS_VML) ? 'inline' : 'inline-block'; + cross.style.top = ((mxClient.IS_VML) ? 0 : 3) + 'px'; + + // Needed to block event transparency in IE + cross.style.background = 'url(' + IMAGE_PATH + '/transparent.gif)'; + + mxEvent.addListener(cross, 'click', function() + { + linkInput.value = ''; + linkInput.focus(); + }); + + inner.appendChild(linkInput); + inner.appendChild(cross); + div.appendChild(inner); + + this.init = function() + { + linkInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) + { + linkInput.select(); + } + else + { + document.execCommand('selectAll', false, null); + } + }; + + var btns = document.createElement('div'); + btns.style.marginTop = '18px'; + btns.style.textAlign = 'right'; + + mxEvent.addListener(linkInput, 'keypress', function(e) + { + if (e.keyCode == 13) + { + editorUi.hideDialog(); + fn(linkInput.value); + } + }); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + var mainBtn = mxUtils.button(btnLabel, function() + { + editorUi.hideDialog(); + fn(linkInput.value); + }); + mainBtn.className = 'geBtn gePrimaryBtn'; + btns.appendChild(mainBtn); + + if (!editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + div.appendChild(btns); + + this.container = div; +}; + +/** + * + */ +var OutlineWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.width = '100%'; + div.style.height = '100%'; + div.style.border = '1px solid whiteSmoke'; + div.style.overflow = 'hidden'; + + this.window = new mxWindow(mxResources.get('outline'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 80, 80); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + var outline = editorUi.createOutline(this.window); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + outline.destroy(); + } + + this.window.addListener(mxEvent.RESIZE, mxUtils.bind(this, function() + { + outline.update(false); + outline.outline.sizeDidChange(); + })); + + this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function() + { + outline.suspended = false; + outline.outline.refresh(); + outline.update(); + })); + + this.window.addListener(mxEvent.HIDE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + this.window.addListener(mxEvent.NORMALIZE, mxUtils.bind(this, function() + { + outline.suspended = false; + outline.update(); + })); + + this.window.addListener(mxEvent.MINIMIZE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + var outlineCreateGraph = outline.createGraph; + outline.createGraph = function(container) + { + var g = outlineCreateGraph.apply(this, arguments); + g.gridEnabled = false; + g.pageScale = graph.pageScale; + g.pageFormat = graph.pageFormat; + g.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; + g.pageVisible = graph.pageVisible; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + return g; + }; + + function update() + { + outline.outline.pageScale = graph.pageScale; + outline.outline.pageFormat = graph.pageFormat; + outline.outline.pageVisible = graph.pageVisible; + outline.outline.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background;; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + if (graph.view.backgroundPageShape != null && outline.outline.view.backgroundPageShape != null) + { + outline.outline.view.backgroundPageShape.fill = graph.view.backgroundPageShape.fill; + } + + outline.outline.refresh(); + }; + + outline.init(div); + + editorUi.editor.addListener('resetGraphView', update); + editorUi.addListener('pageFormatChanged', update); + editorUi.addListener('backgroundColorChanged', update); + editorUi.addListener('backgroundImageChanged', update); + editorUi.addListener('pageViewChanged', function() + { + update(); + outline.update(true); + }); + + if (outline.outline.dialect == mxConstants.DIALECT_SVG) + { + var zoomInAction = editorUi.actions.get('zoomIn'); + var zoomOutAction = editorUi.actions.get('zoomOut'); + + mxEvent.addMouseWheelListener(function(evt, up) + { + var outlineWheel = false; + var source = mxEvent.getSource(evt); + + while (source != null) + { + if (source == outline.outline.view.canvas.ownerSVGElement) + { + outlineWheel = true; + break; + } + + source = source.parentNode; + } + + if (outlineWheel) + { + if (up) + { + zoomInAction.funct(); + } + else + { + zoomOutAction.funct(); + } + + mxEvent.consume(evt); + } + }); + } +}; + +/** + * + */ +var LayersWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.userSelect = 'none'; + div.style.background = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + div.style.border = '1px solid whiteSmoke'; + div.style.height = '100%'; + div.style.marginBottom = '10px'; + div.style.overflow = 'auto'; + + var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px'; + + var listDiv = document.createElement('div') + listDiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? '#dcdcdc' : '#e5e5e5'; + listDiv.style.position = 'absolute'; + listDiv.style.overflow = 'auto'; + listDiv.style.left = '0px'; + listDiv.style.right = '0px'; + listDiv.style.top = '0px'; + listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px'; + div.appendChild(listDiv); + + var dragSource = null; + var dropIndex = null; + + mxEvent.addListener(div, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = 0; + evt.stopPropagation(); + evt.preventDefault(); + }); + + // Workaround for "no element found" error in FF + mxEvent.addListener(div, 'drop', function(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }); + + var layerCount = null; + var selectionLayer = null; + + var ldiv = document.createElement('div'); + + ldiv.className = 'geToolbarContainer'; + ldiv.style.position = 'absolute'; + ldiv.style.bottom = '0px'; + ldiv.style.left = '0px'; + ldiv.style.right = '0px'; + ldiv.style.height = tbarHeight; + ldiv.style.overflow = 'hidden'; + ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px'; + ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + ldiv.style.borderWidth = '1px 0px 0px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.display = 'block'; + ldiv.style.whiteSpace = 'nowrap'; + + if (mxClient.IS_QUIRKS) + { + ldiv.style.filter = 'none'; + } + + var link = document.createElement('a'); + link.className = 'geButton'; + + if (mxClient.IS_QUIRKS) + { + link.style.filter = 'none'; + } + + var removeLink = link.cloneNode(); + removeLink.innerHTML = '
'; + + mxEvent.addListener(removeLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + try + { + var index = graph.model.root.getIndex(selectionLayer); + graph.removeCells([selectionLayer], false); + + // Creates default layer if no layer exists + if (graph.model.getChildCount(graph.model.root) == 0) + { + graph.model.add(graph.model.root, new mxCell()); + graph.setDefaultParent(null); + } + else if (index > 0 && index <= graph.model.getChildCount(graph.model.root)) + { + graph.setDefaultParent(graph.model.getChildAt(graph.model.root, index - 1)); + } + else + { + graph.setDefaultParent(null); + } + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + removeLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(removeLink); + + var insertLink = link.cloneNode(); + insertLink.innerHTML = '
'; + + mxEvent.addListener(insertLink, 'click', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + graph.moveCells(graph.getSelectionCells(), 0, 0, false, selectionLayer); + } + }); + + ldiv.appendChild(insertLink); + + var dataLink = link.cloneNode(); + dataLink.innerHTML = '
'; + dataLink.setAttribute('title', mxResources.get('rename')); + + mxEvent.addListener(dataLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + editorUi.showDataDialog(selectionLayer); + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + dataLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(dataLink); + + function renameLayer(layer) + { + if (graph.isEnabled() && layer != null) + { + var label = graph.convertValueToString(layer); + var dlg = new FilenameDialog(editorUi, label || mxResources.get('background'), mxResources.get('rename'), mxUtils.bind(this, function(newValue) + { + if (newValue != null) + { + graph.cellLabelChanged(layer, newValue); + } + }), mxResources.get('enterName')); + editorUi.showDialog(dlg.container, 300, 100, true, true); + dlg.init(); + } + }; + + var duplicateLink = link.cloneNode(); + duplicateLink.innerHTML = '
'; + + mxEvent.addListener(duplicateLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + var newCell = null; + graph.model.beginUpdate(); + try + { + newCell = graph.cloneCells([selectionLayer])[0]; + graph.cellLabelChanged(newCell, mxResources.get('untitledLayer')); + newCell.setVisible(true); + newCell = graph.addCell(newCell, graph.model.root); + graph.setDefaultParent(newCell); + } + finally + { + graph.model.endUpdate(); + } + + if (newCell != null && !graph.isCellLocked(newCell)) + { + graph.selectAll(newCell); + } + } + }); + + if (!graph.isEnabled()) + { + duplicateLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(duplicateLink); + + var addLink = link.cloneNode(); + addLink.innerHTML = '
'; + addLink.setAttribute('title', mxResources.get('addLayer')); + + mxEvent.addListener(addLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + + try + { + var cell = graph.addCell(new mxCell(mxResources.get('untitledLayer')), graph.model.root); + graph.setDefaultParent(cell); + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + addLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(addLink); + + div.appendChild(ldiv); + + function refresh() + { + layerCount = graph.model.getChildCount(graph.model.root) + listDiv.innerHTML = ''; + + function addLayer(index, label, child, defaultParent) + { + var ldiv = document.createElement('div'); + ldiv.className = 'geToolbarContainer'; + + ldiv.style.overflow = 'hidden'; + ldiv.style.position = 'relative'; + ldiv.style.padding = '4px'; + ldiv.style.height = '22px'; + ldiv.style.display = 'block'; + ldiv.style.backgroundColor = 'whiteSmoke'; + ldiv.style.borderWidth = '0px 0px 1px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.whiteSpace = 'nowrap'; + ldiv.setAttribute('title', label); + + var left = document.createElement('div'); + left.style.display = 'inline-block'; + left.style.width = '100%'; + left.style.textOverflow = 'ellipsis'; + left.style.overflow = 'hidden'; + + mxEvent.addListener(ldiv, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = index; + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(ldiv, 'dragstart', function(evt) + { + dragSource = ldiv; + + // Workaround for no DnD on DIV in FF + if (mxClient.IS_FF) + { + // LATER: Check what triggers a parse as XML on this in FF after drop + evt.dataTransfer.setData('Text', ''); + } + }); + + mxEvent.addListener(ldiv, 'dragend', function(evt) + { + if (dragSource != null && dropIndex != null) + { + graph.addCell(child, graph.model.root, dropIndex); + } + + dragSource = null; + dropIndex = null; + evt.stopPropagation(); + evt.preventDefault(); + }); + + var btn = document.createElement('img'); + btn.setAttribute('draggable', 'false'); + btn.setAttribute('align', 'top'); + btn.setAttribute('border', '0'); + btn.style.padding = '4px'; + btn.setAttribute('title', mxResources.get('lockUnlock')); + + var state = graph.view.getState(child); + var style = (state != null) ? state.style : graph.getCellStyle(child); + + if (mxUtils.getValue(style, 'locked', '0') == '1') + { + btn.setAttribute('src', Dialog.prototype.lockedImage); + } + else + { + btn.setAttribute('src', Dialog.prototype.unlockedImage); + } + + if (graph.isEnabled()) + { + btn.style.cursor = 'pointer'; + } + + mxEvent.addListener(btn, 'click', function(evt) + { + if (graph.isEnabled()) + { + var value = null; + + graph.getModel().beginUpdate(); + try + { + value = (mxUtils.getValue(style, 'locked', '0') == '1') ? null : '1'; + graph.setCellStyles('locked', value, [child]); + } + finally + { + graph.getModel().endUpdate(); + } + + if (value == '1') + { + graph.removeSelectionCells(graph.getModel().getDescendants(child)); + } + + mxEvent.consume(evt); + } + }); + + left.appendChild(btn); + + var inp = document.createElement('input'); + inp.setAttribute('type', 'checkbox'); + inp.setAttribute('title', mxResources.get('hideIt', [child.value || mxResources.get('background')])); + inp.style.marginLeft = '4px'; + inp.style.marginRight = '6px'; + inp.style.marginTop = '4px'; + left.appendChild(inp); + + if (graph.model.isVisible(child)) + { + inp.setAttribute('checked', 'checked'); + inp.defaultChecked = true; + } + + mxEvent.addListener(inp, 'click', function(evt) + { + graph.model.setVisible(child, !graph.model.isVisible(child)); + mxEvent.consume(evt); + }); + + mxUtils.write(left, label); + ldiv.appendChild(left); + + if (graph.isEnabled()) + { + // Fallback if no drag and drop is available + if (mxClient.IS_TOUCH || mxClient.IS_POINTER || mxClient.IS_VML || + (mxClient.IS_IE && document.documentMode < 10)) + { + var right = document.createElement('div'); + right.style.display = 'block'; + right.style.textAlign = 'right'; + right.style.whiteSpace = 'nowrap'; + right.style.position = 'absolute'; + right.style.right = '6px'; + right.style.top = '6px'; + + // Poor man's change layer order + if (index > 0) + { + var img2 = document.createElement('a'); + + img2.setAttribute('title', mxResources.get('toBack')); + + img2.className = 'geButton'; + img2.style.cssFloat = 'none'; + img2.innerHTML = '▼'; + img2.style.width = '14px'; + img2.style.height = '14px'; + img2.style.fontSize = '14px'; + img2.style.margin = '0px'; + img2.style.marginTop = '-1px'; + right.appendChild(img2); + + mxEvent.addListener(img2, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index - 1); + } + + mxEvent.consume(evt); + }); + } + + if (index >= 0 && index < layerCount - 1) + { + var img1 = document.createElement('a'); + + img1.setAttribute('title', mxResources.get('toFront')); + + img1.className = 'geButton'; + img1.style.cssFloat = 'none'; + img1.innerHTML = '▲'; + img1.style.width = '14px'; + img1.style.height = '14px'; + img1.style.fontSize = '14px'; + img1.style.margin = '0px'; + img1.style.marginTop = '-1px'; + right.appendChild(img1); + + mxEvent.addListener(img1, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index + 1); + } + + mxEvent.consume(evt); + }); + } + + ldiv.appendChild(right); + } + + if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10)) + { + ldiv.setAttribute('draggable', 'true'); + ldiv.style.cursor = 'move'; + } + } + + mxEvent.addListener(ldiv, 'dblclick', function(evt) + { + var nodeName = mxEvent.getSource(evt).nodeName; + + if (nodeName != 'INPUT' && nodeName != 'IMG') + { + renameLayer(child); + mxEvent.consume(evt); + } + }); + + if (graph.getDefaultParent() == child) + { + ldiv.style.background = '#e6eff8'; + ldiv.style.fontWeight = (graph.isEnabled()) ? 'bold' : ''; + selectionLayer = child; + } + else + { + mxEvent.addListener(ldiv, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.setDefaultParent(defaultParent); + graph.view.setCurrentRoot(null); + refresh(); + } + }); + } + + listDiv.appendChild(ldiv); + }; + + // Cannot be moved or deleted + for (var i = layerCount - 1; i >= 0; i--) + { + (mxUtils.bind(this, function(child) + { + addLayer(i, graph.convertValueToString(child) || + mxResources.get('background'), child, child); + }))(graph.model.getChildAt(graph.model.root, i)); + } + + var label = graph.convertValueToString(selectionLayer) || mxResources.get('background'); + removeLink.setAttribute('title', mxResources.get('removeIt', [label])); + insertLink.setAttribute('title', mxResources.get('moveSelectionTo', [label])); + duplicateLink.setAttribute('title', mxResources.get('duplicateIt', [label])); + dataLink.setAttribute('title', mxResources.get('editData')); + + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + }; + + refresh(); + graph.model.addListener(mxEvent.CHANGE, function() + { + refresh(); + }); + + graph.selectionModel.addListener(mxEvent.CHANGE, function() + { + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + else + { + insertLink.className = 'geButton'; + } + }); + + this.window = new mxWindow(mxResources.get('layers'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 120, 120); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + // Make refresh available via instance + this.refreshLayers = refresh; + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Editor.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Editor.js new file mode 100644 index 00000000..39838cd2 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Editor.js @@ -0,0 +1,2252 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Editor constructor executed on page load. + */ +Editor = function(chromeless, themes, model, graph, editable) +{ + mxEventSource.call(this); + this.chromeless = (chromeless != null) ? chromeless : this.chromeless; + this.initStencilRegistry(); + this.graph = graph || this.createGraph(themes, model); + this.editable = (editable != null) ? editable : !chromeless; + this.undoManager = this.createUndoManager(); + this.status = ''; + + this.getOrCreateFilename = function() + { + return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml'; + }; + + this.getFilename = function() + { + return this.filename; + }; + + // Sets the status and fires a statusChanged event + this.setStatus = function(value) + { + this.status = value; + this.fireEvent(new mxEventObject('statusChanged')); + }; + + // Returns the current status + this.getStatus = function() + { + return this.status; + }; + + // Updates modified state if graph changes + this.graphChangeListener = function(sender, eventObject) + { + var edit = (eventObject != null) ? eventObject.getProperty('edit') : null; + + if (edit == null || !edit.ignoreEdit) + { + this.setModified(true); + } + }; + + this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + this.graphChangeListener.apply(this, arguments); + })); + + // Sets persistent graph state defaults + this.graph.resetViewOnRootChange = false; + this.init(); +}; + +/** + * Counts open editor tabs (must be global for cross-window access) + */ +Editor.pageCounter = 0; + +// Cross-domain window access is not allowed in FF, so if we +// were opened from another domain then this will fail. +(function() +{ + try + { + var op = window; + + while (op.opener != null && typeof op.opener.Editor !== 'undefined' && + !isNaN(op.opener.Editor.pageCounter) && + // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795 + op.opener != op) + { + op = op.opener; + } + + // Increments the counter in the first opener in the chain + if (op != null) + { + op.Editor.pageCounter++; + Editor.pageCounter = op.Editor.pageCounter; + } + } + catch (e) + { + // ignore + } +})(); + +/** + * Specifies if local storage should be used (eg. on the iPad which has no filesystem) + */ +Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS; + +/** + * Images below are for lightbox and embedding toolbars. + */ +Editor.helpImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAXVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5BxTwAAAAH3RSTlMAlUF8boNQIE0LBgOgkGlHNSwqFIx/dGVUOjApmV9ezNACSAAAAIVJREFUGNNtjNsOgzAMQ5NeoVcKDAZs+//PXLKI8YKlWvaRU7jXuFpb9qsbdK05XILUiE8JHQox1Pv3OgFUzf1AGqWqUg+QBwLF0YAeegBlCNgRWOpB5vUfTCmeoHQ/wNdy0jLH/cM+b+wLTw4n/7ACEmHVVy8h6qy8V7MNcGowWpsNbvUFcGUEdSi1s/oAAAAASUVORK5CYII=' : + IMAGE_PATH + '/help.png'; + +/** + * Sets the default font size. + */ +Editor.checkmarkImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhFQAVAMQfAGxsbHx8fIqKioaGhvb29nJycvr6+sDAwJqamltbW5OTk+np6YGBgeTk5Ly8vJiYmP39/fLy8qWlpa6ursjIyOLi4vj4+N/f3+3t7fT09LCwsHZ2dubm5r6+vmZmZv///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEY4NTZERTQ5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEY4NTZERTU5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Rjg1NkRFMjlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Rjg1NkRFMzlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAB8ALAAAAAAVABUAAAVI4CeOZGmeaKqubKtylktSgCOLRyLd3+QJEJnh4VHcMoOfYQXQLBcBD4PA6ngGlIInEHEhPOANRkaIFhq8SuHCE1Hb8Lh8LgsBADs=' : + IMAGE_PATH + '/checkmark.gif'; + +/** + * Images below are for lightbox and embedding toolbars. + */ +Editor.maximizeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABXRSTlMA758vX1Pw3BoAAABJSURBVAjXY8AJQkODGBhUQ0MhbAUGBiYY24CBgRnGFmZgMISwgwwDGRhEhVVBbAVmEQYGRwMmBjIAQi/CTIRd6G5AuA3dzYQBAHj0EFdHkvV4AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomOutImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCxxcXEhISFgYGChjTUxAAAAAXRSTlMAQObYZgAAAEdJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITgLJZXFxgbIQ4Qj3CHIT5ggoIe5kgNkM1KSDYKBKqxPkDAPo5BAZBE54hAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomInImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCwhISFxcXFgYGBavKaoAAAAAXRSTlMAQObYZgAAAElJREFUCNdjIAMwCQrB2YKCggJQJqMIA4sglK3owMzgqABVwsDMwCgAZTMbG8PYCHGEeoQ5CPMFFRD2MkFshmpSQLBRJFSJ8wcAEqcEM2uhl2MAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomFitImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAD1BMVEUAAAAAAAAwMDBwcHBgYGC1xl09AAAAAXRSTlMAQObYZgAAAEFJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITwMdGqEeYgzBfUAFhLxPEZqgmBQQbRUKFOH8AAK5OA3lA+FFOAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.layersImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAAaVBMVEUAAAAgICAICAgdHR0PDw8WFhYICAgLCwsXFxcvLy8ODg4uLi4iIiIqKiokJCQYGBgKCgonJycFBQUCAgIqKiocHBwcHBwODg4eHh4cHBwnJycJCQkUFBQqKiojIyMuLi4ZGRkgICAEBATOWYXAAAAAGnRSTlMAD7+fnz8/H7/ff18/77+vr5+fn39/b28fH2xSoKsAAACQSURBVBjTrYxJEsMgDARZZMAY73sgCcn/HxnhKtnk7j6oRq0psfuoyndZ/SuODkHPLzfVT6KeyPePnJ7KrnkRjWMXTn4SMnN8mXe2SSM3ts8L/ZUxxrbAULSYJJULE0Iw9pjpenoICcgcX61mGgTgtCv9Be99pzCoDhNQWQnchD1mup5++CYGcoQexajZbfwAj/0MD8ZOaUgAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.previousImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAh0lEQVQ4je3UsQnCUBCA4U8hpa1NsoEjpHQJS0dxADdwEMuMIJkgA1hYChbGQgMi+JC8q4L/AB/vDu7x74cWWEZhJU44RmA1zujR5GIbXF9YNrjD/Q0bDRY4fEBZ4P4LlgTnCbAf84pUM8/9hY08tMUtEoQ1LpEgrNBFglChFXR6Q6GfwwR6AGKJMF74Vtt3AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.nextImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAi0lEQVQ4jeXUIQ7CUAwA0MeGxWI2yylwnALJUdBcgYvM7QYLmjOQIAkIPmJZghiIvypoUtX0tfnJL38X5ZfaEgUeUcManFBHgS0SLlhHggk3bCPBhCf2keCQR8wjwYTDp6YiZxJmOU1jGw7vGALescuBxsArNlOwd/CM1VSM/ut1qCIw+uOwiMJ+OF4CQzBCXm3hyAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.editImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCwALAIABAFdXV////yH5BAEAAAEALAAAAAALAAsAAAIZjB8AiKuc4jvLOGqzrjX6zmkWyChXaUJBAQA7' : IMAGE_PATH + '/edit.gif'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomOutLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABJklEQVQ4y4WT2XaDMAxEvWD2nSSUNEnTJN3r//+9Sj7ILAY6L0ijC4ONYVZRpo6cByrz2YKSUGorGTpz71lPVHvT+avoB5wIkU/mxk8veceSuNoLg44IzziXjvpih72wKQnm8yc2UoiP/LAd8jQfe2Xf4Pq+2EyYIvv9wbzHHCgwxDdlBtWZOdqDfTCVgqpygQpsZaojVAVc9UjQxnAJDIBhiQv84tq3gMQCAVTxVoSibXJf8tMuc7e1TB/DCmejBNg/w1Y3c+AM5vv4w7xM59/oXamrHaLVqPQ+OTCnmMZxgz0SdL5zji0/ld6j88qGa5KIiBB6WeJGKfUKwSMKLuXgvl1TW0tm5R9UQL/efSDYsnzxD8CinhBsTTdugJatKpJwf8v+ADb8QmvW7AeAAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.zoomInLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABKElEQVQ4y4WT6WKCMBCENwkBwn2oFKvWqr3L+79es4EkQIDOH2d3Pxk2ABiJlB8JCXjqw4LikHVGLHTm3nM3UeVN5690GBBN0GwyV/3kkrUQR+WeKnREeKpzaXWd77CmJiXGfPIEI4V4yQ9TIW/ntlcMBe731Vts9w5TWG8F5j3mQI4hvrKpdGeYA7CX9qAcl650gVJartxRuhyHVghF8idQAIbFLvCLu28BsQEC6aKtCK6Pyb3JT7PmbmtNH8Ny56CotD/2qOs5cJbuffxgXmCib+xddVU5RNOhkvvkhTlFehzVWCOh3++MYElOhfdovaImnRYVmqDdsuhNp1QrBBE6uGC2+3ZNjGdg5B94oD+9uyVgWT79BwAxEBTWdOu3bWBVgsn/N/AHUD9IC01Oe40AAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.actualSizeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABIUlEQVQ4y4WT2XqDIBCFBxDc9yTWNEnTJN3r+79eGT4BEbXnaubMr8dBBaM450dCQp4LWFAascGIRd48eB4cNYE7f6XjgGiCFs5c+dml6CFN6j1V6IQIlHPpdV/usKcmJcV88gQTRXjLD9Mhb+fWq8YG9/uCmTCFjeeDeY85UGKIUGUuqzN42kv7oCouq9oHamlzVR1lVfpAIu1QVRiW+sAv7r4FpAYIZZVsRXB9TP5Dfpo1d1trCgzz1iiptH/sUbdz4CzN9+mLeXHn3+hdddd4RDegsrvzwZwSs2GLPRJidAqCLTlVwaMPqpYMWjTWBB2WRW86pVkhSKyDK2bdt2tmagZG4sBD/evdLQHLEvQfAOKRoLCmG1FAB6uKmby+gz+REDn7O5+EwQAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.printLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAXVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9RKvvlAAAAHnRSTlMAydnl77qbMLT093H7K4Nd4Ktn082+lYt5bkklEgP44nQSAAAApUlEQVQ4y73P2Q6DIBRF0cOgbRHHzhP//5m9mBAQKjG1cT0Yc7ITAMu1LNQgUZiQ2DYoNQ0sCQb6qgHAfRx48opq3J9AZ6xuF7uOew8Ik1OsCZRS2UAC9V+D9a+QZYxNA45YFQftPtSkATOhw7dAc0vPBwKWiIOjP0JZ0yMuQJ27g36DipOUsqRAM0dR8KD1/ILHaHSE/w8DIx09E3g/BTce6rHUB5sAPKvfF+JdAAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.layersLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAmVBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v7///+bnZkkAAAAMnRSTlMABPr8ByiD88KsTi/rvJb272mjeUA1CuPe1M/KjVxYHxMP6KZ0S9nYzGRGGRaznpGIbzaGUf0AAAHESURBVDjLbZLZYoIwEEVDgLCjbKIgAlqXqt3m/z+uNwu1rcyDhjl3ktnYL7OY254C0VX3yWFZfzDrOClbbgKxi0YDHjwl4jbnRkXxJS/C1YP3DbBhD1n7Ex4uaAqdVDb3yJ/4J/3nJD2to/ngQz/DfUvzMp4JJ5sSCaF5oXmemgQDfDxzbi+Kq4sU+vNcuAmx94JtyOP2DD4Epz2asWSCz4Z/4fECxyNj9zC9xNLHcdPEO+awDKeSaUu0W4twZQiO2hYVisTR3RCtK/c1X6t4xMEpiGqXqVntEBLolkZZsKY4QtwH6jzq67dEHlJysB1aNOD3XT7n1UkasQN59L4yC2RELMDSeCRtz3yV22Ub3ozIUTknYx8JWqDdQxbUes98cR2kZtUSveF/bAhcedwEWmlxIkpZUy4XOCb6VBjjxHvbwo/1lBAHHi2JCr0NI570QhyHq/DhJoE2lLgyA4RVe6KmZ47O/3b86MCP0HWa73A8/C3SUc5Qc1ajt6fgpXJ+RGpMvDSchepZDOOQRcZVIKcK90x2D7etqtI+56+u6n3sPriO6nfphitR4+O2m3EbM7lh3me1FM1o+LMI887rN+s3/wZdTFlpNVJiOAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.closeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAuvAIg/dDM/QlOeuFhj0S5s4vKgzjxJRQNiLSey0AAADNSURBVDjLfZLbEoMgDEQjRRRs1XqX///QNmOHJSnjPkHOGR7IEmeoGtJZstnwjqbRfIsmgEdtPCqe9Ynz7ZSc07rE2QiSc+qv8TvjRXA2PDUm3dpe82iJhOEUfxJJo3aCv+jKmRmH4lcCjCjeh9GWOdL/GZZkXH3PYYDrHBnfc4D/RVZf5sjoC1was+Y6HQxwaUxFvq/a0Pv343VCTxfBSRiB+ab3M3eiQZXmMNBJ3Y8pGRZtYQ7DgHMXJEdPLTaN/qBjzJOBc3nmNcbsA16bMR0oLqf+AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.editLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA+hzi3nRQWyXzkm0h2j3u54gzEgSXjlYoTBgJxL2loGpAOS3Jt7Wxm35Ga7gRAAAA6UlEQVQ4y63Q2XaCMBSF4Q0JBasoQ5DJqbXjfv8HbCK2BZNwo/8FXHx7rcMC7lQu0iX8qU/qtvAWCpoqH8dYzS0SwaV5eK/UAf8X9pd2CWKzuF5Jrftp1owXwnIGLUaL3PYndOHf4kNNXWrXK/m7CHunk7K8LE6YtBpcknwG9GKxnroY+ylBXcx4xKyx/u/EuXi509cP9V7OO1oyHnzrdFTcqLG/4ibBA5pIMr/4xvKzuQDkVy9wW8SgBFD6HDvuzMvrZcC9QlkfMzI7w64m+b4PqBMNHB05lH21PVxJo2/fBXxV4hB38PcD+5AkI4FuETsAAAAASUVORK5CYII='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.previousLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFZJREFUOMvdkskRgDAMA4lDwg2B7b9XOlge/KKvdsa25KFb5XlRvxXC/DNBEv8IFNjBgGdDgXtFgTyhwDXiQAUHCvwa4Uv6mR6UR+1led2mVonvl+tML45qCQNQLIx7AAAAAElFTkSuQmCC'; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.nextLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFRJREFUOMvd0skRgCAQBVEFwQ0V7fxzNQP6wI05v6pZ/kyj1b7FNgik2gQzzLcAwiUAigHOTwDHK4A1CmB5BJANJG1hQ9qafYcqFlZP3IFc9eVGrR+iIgkDQRUXIAAAAABJRU5ErkJggg=='; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.ctrlKey = (mxClient.IS_MAC) ? 'Cmd' : 'Ctrl'; + +/** + * Specifies if the diagram should be saved automatically if possible. Default + * is true. + */ +Editor.popupsAllowed = true; + +/** + * Editor inherits from mxEventSource + */ +mxUtils.extend(Editor, mxEventSource); + +/** + * Stores initial state of mxClient.NO_FO. + */ +Editor.prototype.originalNoForeignObject = mxClient.NO_FO; + +/** + * Specifies the image URL to be used for the transparent background. + */ +Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' : + IMAGE_PATH + '/transparent.gif'; + +/** + * Specifies if the canvas should be extended in all directions. Default is true. + */ +Editor.prototype.extendCanvas = true; + +/** + * Specifies if the app should run in chromeless mode. Default is false. + * This default is only used if the contructor argument is null. + */ +Editor.prototype.chromeless = false; + +/** + * Specifies the order of OK/Cancel buttons in dialogs. Default is true. + * Cancel first is used on Macs, Windows/Confluence uses cancel last. + */ +Editor.prototype.cancelFirst = true; + +/** + * Specifies if the editor is enabled. Default is true. + */ +Editor.prototype.enabled = true; + +/** + * Contains the name which was used for the last save. Default value is null. + */ +Editor.prototype.filename = null; + +/** + * Contains the current modified state of the diagram. This is false for + * new diagrams and after the diagram was saved. + */ +Editor.prototype.modified = false; + +/** + * Specifies if the diagram should be saved automatically if possible. Default + * is true. + */ +Editor.prototype.autosave = true; + +/** + * Specifies the top spacing for the initial page view. Default is 0. + */ +Editor.prototype.initialTopSpacing = 0; + +/** + * Specifies the app name. Default is document.title. + */ +Editor.prototype.appName = document.title; + +/** + * + */ +Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/'; + +/** + * Initializes the environment. + */ +Editor.prototype.init = function() { }; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.isChromelessView = function() +{ + return this.chromeless; +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.setAutosave = function(value) +{ + this.autosave = value; + this.fireEvent(new mxEventObject('autosaveChanged')); +}; + +/** + * + */ +Editor.prototype.getEditBlankUrl = function(params) +{ + return this.editBlankUrl + params; +} + +/** + * + */ +Editor.prototype.editAsNew = function(xml, title) +{ + var p = (title != null) ? '?title=' + encodeURIComponent(title) : ''; + + if (urlParams['ui'] != null) + { + p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui']; + } + + if (this.editorWindow != null && !this.editorWindow.closed) + { + this.editorWindow.focus(); + } + else + { + if (typeof window.postMessage !== 'undefined' && (document.documentMode == null || document.documentMode >= 10)) + { + if (this.editorWindow == null) + { + mxEvent.addListener(window, 'message', mxUtils.bind(this, function(evt) + { + if (evt.data == 'ready' && evt.source == this.editorWindow) + { + this.editorWindow.postMessage(xml, '*'); + } + })); + } + + this.editorWindow = this.graph.openLink(this.getEditBlankUrl(p + + ((p.length > 0) ? '&' : '?') + 'client=1')); + } + else + { + this.editorWindow = this.graph.openLink(this.getEditBlankUrl(p) + + '#R' + encodeURIComponent(xml)); + } + } +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.createGraph = function(themes, model) +{ + var graph = new Graph(null, model, null, null, themes); + graph.transparentBackground = false; + + // Opens all links in a new window while editing + if (!this.chromeless) + { + graph.isBlankLink = function(href) + { + return !this.isExternalProtocol(href); + }; + } + + return graph; +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.resetGraph = function() +{ + this.graph.gridEnabled = !this.isChromelessView() || urlParams['grid'] == '1'; + this.graph.graphHandler.guidesEnabled = true; + this.graph.setTooltips(true); + this.graph.setConnectable(true); + this.graph.foldingEnabled = true; + this.graph.scrollbars = this.graph.defaultScrollbars; + this.graph.pageVisible = this.graph.defaultPageVisible; + this.graph.pageBreaksVisible = this.graph.pageVisible; + this.graph.preferPageSize = this.graph.pageBreaksVisible; + this.graph.background = this.graph.defaultGraphBackground; + this.graph.pageScale = mxGraph.prototype.pageScale; + this.graph.pageFormat = mxGraph.prototype.pageFormat; + this.graph.currentScale = 1; + this.graph.currentTranslate.x = 0; + this.graph.currentTranslate.y = 0; + this.updateGraphComponents(); + this.graph.view.setScale(1); +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.readGraphState = function(node) +{ + this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.isChromelessView() || urlParams['grid'] == '1'); + this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize; + this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0'; + this.graph.setTooltips(node.getAttribute('tooltips') != '0'); + this.graph.setConnectable(node.getAttribute('connect') != '0'); + this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0'; + this.graph.foldingEnabled = node.getAttribute('fold') != '0'; + + if (this.isChromelessView() && this.graph.foldingEnabled) + { + this.graph.foldingEnabled = urlParams['nav'] == '1'; + this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled; + } + + var ps = node.getAttribute('pageScale'); + + if (ps != null) + { + this.graph.pageScale = ps; + } + else + { + this.graph.pageScale = mxGraph.prototype.pageScale; + } + + if (!this.graph.isLightboxView()) + { + var pv = node.getAttribute('page'); + + if (pv != null) + { + this.graph.pageVisible = (pv != '0'); + } + else + { + this.graph.pageVisible = this.graph.defaultPageVisible; + } + } + else + { + this.graph.pageVisible = false; + } + + this.graph.pageBreaksVisible = this.graph.pageVisible; + this.graph.preferPageSize = this.graph.pageBreaksVisible; + + var pw = node.getAttribute('pageWidth'); + var ph = node.getAttribute('pageHeight'); + + if (pw != null && ph != null) + { + this.graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph)); + } + + // Loads the persistent state settings + var bg = node.getAttribute('background'); + + if (bg != null && bg.length > 0) + { + this.graph.background = bg; + } + else + { + this.graph.background = this.graph.defaultGraphBackground; + } +}; + +/** + * Sets the XML node for the current diagram. + */ +Editor.prototype.setGraphXml = function(node) +{ + if (node != null) + { + var dec = new mxCodec(node.ownerDocument); + + if (node.nodeName == 'mxGraphModel') + { + this.graph.model.beginUpdate(); + + try + { + this.graph.model.clear(); + this.graph.view.scale = 1; + this.readGraphState(node); + this.updateGraphComponents(); + dec.decode(node, this.graph.getModel()); + } + finally + { + this.graph.model.endUpdate(); + } + + this.fireEvent(new mxEventObject('resetGraphView')); + } + else if (node.nodeName == 'root') + { + this.resetGraph(); + + // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml + var wrapper = dec.document.createElement('mxGraphModel'); + wrapper.appendChild(node); + + dec.decode(wrapper, this.graph.getModel()); + this.updateGraphComponents(); + this.fireEvent(new mxEventObject('resetGraphView')); + } + else + { + throw { + message: mxResources.get('cannotOpenFile'), + node: node, + toString: function() { return this.message; } + }; + } + } + else + { + this.resetGraph(); + this.graph.model.clear(); + this.fireEvent(new mxEventObject('resetGraphView')); + } +}; + +/** + * Returns the XML node that represents the current diagram. + */ +Editor.prototype.getGraphXml = function(ignoreSelection) +{ + ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; + var node = null; + + if (ignoreSelection) + { + var enc = new mxCodec(mxUtils.createXmlDocument()); + node = enc.encode(this.graph.getModel()); + } + else + { + node = this.graph.encodeCells(mxUtils.sortCells(this.graph.model.getTopmostCells( + this.graph.getSelectionCells()))); + } + + if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0) + { + node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100); + node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100); + } + + node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0'); + node.setAttribute('gridSize', this.graph.gridSize); + node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0'); + node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0'); + node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0'); + node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0'); + node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0'); + node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0'); + node.setAttribute('pageScale', this.graph.pageScale); + node.setAttribute('pageWidth', this.graph.pageFormat.width); + node.setAttribute('pageHeight', this.graph.pageFormat.height); + + if (this.graph.background != null) + { + node.setAttribute('background', this.graph.background); + } + + return node; +}; + +/** + * Keeps the graph container in sync with the persistent graph state + */ +Editor.prototype.updateGraphComponents = function() +{ + var graph = this.graph; + + if (graph.container != null) + { + graph.view.validateBackground(); + graph.container.style.overflow = (graph.scrollbars) ? 'auto' : 'hidden'; + + this.fireEvent(new mxEventObject('updateGraphComponents')); + } +}; + +/** + * Sets the modified flag. + */ +Editor.prototype.setModified = function(value) +{ + this.modified = value; +}; + +/** + * Sets the filename. + */ +Editor.prototype.setFilename = function(value) +{ + this.filename = value; +}; + +/** + * Creates and returns a new undo manager. + */ +Editor.prototype.createUndoManager = function() +{ + var graph = this.graph; + var undoMgr = new mxUndoManager(); + + this.undoListener = function(sender, evt) + { + undoMgr.undoableEditHappened(evt.getProperty('edit')); + }; + + // Installs the command history + var listener = mxUtils.bind(this, function(sender, evt) + { + this.undoListener.apply(this, arguments); + }); + + graph.getModel().addListener(mxEvent.UNDO, listener); + graph.getView().addListener(mxEvent.UNDO, listener); + + // Keeps the selection in sync with the history + var undoHandler = function(sender, evt) + { + var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes); + var model = graph.getModel(); + var cells = []; + + for (var i = 0; i < cand.length; i++) + { + if ((model.isVertex(cand[i]) || model.isEdge(cand[i])) && graph.view.getState(cand[i]) != null) + { + cells.push(cand[i]); + } + } + + graph.setSelectionCells(cells); + }; + + undoMgr.addListener(mxEvent.UNDO, undoHandler); + undoMgr.addListener(mxEvent.REDO, undoHandler); + + return undoMgr; +}; + +/** + * Adds basic stencil set (no namespace). + */ +Editor.prototype.initStencilRegistry = function() { }; + +/** + * Creates and returns a new undo manager. + */ +Editor.prototype.destroy = function() +{ + if (this.graph != null) + { + this.graph.destroy(); + this.graph = null; + } +}; + +/** + * Class for asynchronously opening a new window and loading a file at the same + * time. This acts as a bridge between the open dialog and the new editor. + */ +OpenFile = function(done) +{ + this.producer = null; + this.consumer = null; + this.done = done; + this.args = null; +}; + +/** + * Registers the editor from the new window. + */ +OpenFile.prototype.setConsumer = function(value) +{ + this.consumer = value; + this.execute(); +}; + +/** + * Sets the data from the loaded file. + */ +OpenFile.prototype.setData = function() +{ + this.args = arguments; + this.execute(); +}; + +/** + * Displays an error message. + */ +OpenFile.prototype.error = function(msg) +{ + this.cancel(true); + mxUtils.alert(msg); +}; + +/** + * Consumes the data. + */ +OpenFile.prototype.execute = function() +{ + if (this.consumer != null && this.args != null) + { + this.cancel(false); + this.consumer.apply(this, this.args); + } +}; + +/** + * Cancels the operation. + */ +OpenFile.prototype.cancel = function(cancel) +{ + if (this.done != null) + { + this.done((cancel != null) ? cancel : true); + } +}; + +/** + * Basic dialogs that are available in the viewer (print dialog). + */ +function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll) +{ + var dx = 0; + + if (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) + { + // Adds padding as a workaround for box model in older IE versions + // This needs to match the total padding of geDialog in CSS + dx = 80; + } + + w += dx; + h += dx; + + var w0 = w; + var h0 = h; + + // clientHeight check is attempted fix for print dialog offset in viewer lightbox + var dh = (document.documentElement.clientHeight > 0) ? document.documentElement.clientHeight : + Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight); + var left = Math.max(1, Math.round((document.body.clientWidth - w - 64) / 2)); + var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); + + // Keeps window size inside available space + if (!mxClient.IS_QUIRKS) + { + elt.style.maxHeight = '100%'; + } + + w = Math.min(w, document.body.scrollWidth - 64); + h = Math.min(h, dh - 64); + + // Increments zIndex to put subdialogs and background over existing dialogs and background + if (editorUi.dialogs.length > 0) + { + this.zIndex += editorUi.dialogs.length * 2; + } + + if (this.bg == null) + { + this.bg = editorUi.createDiv('background'); + this.bg.style.position = 'absolute'; + this.bg.style.background = Dialog.backdropColor; + this.bg.style.height = dh + 'px'; + this.bg.style.right = '0px'; + this.bg.style.zIndex = this.zIndex - 2; + + mxUtils.setOpacity(this.bg, this.bgOpacity); + + if (mxClient.IS_QUIRKS) + { + new mxDivResizer(this.bg); + } + } + + var origin = mxUtils.getDocumentScrollOrigin(document); + this.bg.style.left = origin.x + 'px'; + this.bg.style.top = origin.y + 'px'; + left += origin.x; + top += origin.y; + + if (modal) + { + document.body.appendChild(this.bg); + } + + var div = editorUi.createDiv('geDialog'); + var pos = this.getPosition(left, top, w, h); + left = pos.x; + top = pos.y; + + div.style.width = w + 'px'; + div.style.height = h + 'px'; + div.style.left = left + 'px'; + div.style.top = top + 'px'; + div.style.zIndex = this.zIndex; + + div.appendChild(elt); + document.body.appendChild(div); + + // Adds vertical scrollbars if needed + if (!noScroll && elt.clientHeight > div.clientHeight - 64) + { + elt.style.overflowY = 'auto'; + } + + if (closable) + { + var img = document.createElement('img'); + + img.setAttribute('src', Dialog.prototype.closeImage); + img.setAttribute('title', mxResources.get('close')); + img.className = 'geDialogClose'; + img.style.top = (top + 14) + 'px'; + img.style.left = (left + w + 38 - dx) + 'px'; + img.style.zIndex = this.zIndex; + + mxEvent.addListener(img, 'click', mxUtils.bind(this, function() + { + editorUi.hideDialog(true); + })); + + document.body.appendChild(img); + this.dialogImg = img; + + mxEvent.addGestureListeners(this.bg, null, null, mxUtils.bind(this, function(evt) + { + editorUi.hideDialog(true); + })); + } + + this.resizeListener = mxUtils.bind(this, function() + { + dh = Math.max(document.body.clientHeight, document.documentElement.clientHeight); + this.bg.style.height = dh + 'px'; + + left = Math.max(1, Math.round((document.body.clientWidth - w - 64) / 2)); + top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); + w = Math.min(w0, document.body.scrollWidth - 64); + h = Math.min(h0, dh - 64); + + var pos = this.getPosition(left, top, w, h); + left = pos.x; + top = pos.y; + + div.style.left = left + 'px'; + div.style.top = top + 'px'; + div.style.width = w + 'px'; + div.style.height = h + 'px'; + + // Adds vertical scrollbars if needed + if (!noScroll && elt.clientHeight > div.clientHeight - 64) + { + elt.style.overflowY = 'auto'; + } + + if (this.dialogImg != null) + { + this.dialogImg.style.top = (top + 14) + 'px'; + this.dialogImg.style.left = (left + w + 38 - dx) + 'px'; + } + }); + + mxEvent.addListener(window, 'resize', this.resizeListener); + + this.onDialogClose = onClose; + this.container = div; + + editorUi.editor.fireEvent(new mxEventObject('showDialog')); +}; + +/** + * + */ +Dialog.backdropColor = 'white'; + +/** + * + */ +Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 1; + +/** + * + */ +Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC'; + +/** + * + */ +Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg=='; + +/** + * + */ +Dialog.prototype.clearImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/clear.gif' : 'data:image/gif;base64,R0lGODlhDQAKAIABAMDAwP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUIzOEM1NzI4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUIzOEM1NzM4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QjM4QzU3MDg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QjM4QzU3MTg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAEALAAAAAANAAoAAAIXTGCJebD9jEOTqRlttXdrB32PJ2ncyRQAOw=='; + +/** + * + */ +Dialog.prototype.lockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/locked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCODExNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCODIxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3RjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI4MDE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvqMCFYAAAAVUExURZmZmb+/v7KysqysrMzMzLGxsf///4g8N1cAAAAHdFJOU////////wAaSwNGAAAAPElEQVR42lTMQQ4AIQgEwUa0//9kTQirOweYOgDqAMbZUr10AGlAwx4/BJ2QJ4U0L5brYjovvpv32xZgAHZaATFtMbu4AAAAAElFTkSuQmCC'; + +/** + * + */ +Dialog.prototype.unlockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/unlocked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCN0QxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCN0UxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3QjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI3QzE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkKMpVwAAAAYUExURZmZmbKysr+/v6ysrOXl5czMzLGxsf///zHN5lwAAAAIdFJOU/////////8A3oO9WQAAADxJREFUeNpUzFESACAEBNBVsfe/cZJU+8Mzs8CIABCidtfGOndnYsT40HDSiCcbPdoJo10o9aI677cpwACRoAF3dFNlswAAAABJRU5ErkJggg=='; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.bgOpacity = 80; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.getPosition = function(left, top) +{ + return new mxPoint(left, top); +}; + +/** + * Removes the dialog from the DOM. + */ +Dialog.prototype.close = function(cancel) +{ + if (this.onDialogClose != null) + { + this.onDialogClose(cancel); + this.onDialogClose = null; + } + + if (this.dialogImg != null) + { + this.dialogImg.parentNode.removeChild(this.dialogImg); + this.dialogImg = null; + } + + if (this.bg != null && this.bg.parentNode != null) + { + this.bg.parentNode.removeChild(this.bg); + } + + mxEvent.removeListener(window, 'resize', this.resizeListener); + this.container.parentNode.removeChild(this.container); +}; + +/** + * Constructs a new print dialog. + */ +var PrintDialog = function(editorUi, title) +{ + this.create(editorUi, title); +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.prototype.create = function(editorUi) +{ + var graph = editorUi.editor.graph; + var row, td; + + var table = document.createElement('table'); + table.style.width = '100%'; + table.style.height = '100%'; + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + var onePageCheckBox = document.createElement('input'); + onePageCheckBox.setAttribute('type', 'checkbox'); + td = document.createElement('td'); + td.setAttribute('colspan', '2'); + td.style.fontSize = '10pt'; + td.appendChild(onePageCheckBox); + + var span = document.createElement('span'); + mxUtils.write(span, ' ' + mxResources.get('fitPage')); + td.appendChild(span); + + mxEvent.addListener(span, 'click', function(evt) + { + onePageCheckBox.checked = !onePageCheckBox.checked; + pageCountCheckBox.checked = !onePageCheckBox.checked; + mxEvent.consume(evt); + }); + + mxEvent.addListener(onePageCheckBox, 'change', function() + { + pageCountCheckBox.checked = !onePageCheckBox.checked; + }); + + row.appendChild(td); + tbody.appendChild(row); + + row = row.cloneNode(false); + + var pageCountCheckBox = document.createElement('input'); + pageCountCheckBox.setAttribute('type', 'checkbox'); + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.appendChild(pageCountCheckBox); + + var span = document.createElement('span'); + mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':'); + td.appendChild(span); + + mxEvent.addListener(span, 'click', function(evt) + { + pageCountCheckBox.checked = !pageCountCheckBox.checked; + onePageCheckBox.checked = !pageCountCheckBox.checked; + mxEvent.consume(evt); + }); + + row.appendChild(td); + + var pageCountInput = document.createElement('input'); + pageCountInput.setAttribute('value', '1'); + pageCountInput.setAttribute('type', 'number'); + pageCountInput.setAttribute('min', '1'); + pageCountInput.setAttribute('size', '4'); + pageCountInput.setAttribute('disabled', 'disabled'); + pageCountInput.style.width = '50px'; + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.appendChild(pageCountInput); + mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)'); + row.appendChild(td); + tbody.appendChild(row); + + mxEvent.addListener(pageCountCheckBox, 'change', function() + { + if (pageCountCheckBox.checked) + { + pageCountInput.removeAttribute('disabled'); + } + else + { + pageCountInput.setAttribute('disabled', 'disabled'); + } + + onePageCheckBox.checked = !pageCountCheckBox.checked; + }); + + row = row.cloneNode(false); + + td = document.createElement('td'); + mxUtils.write(td, mxResources.get('pageScale') + ':'); + row.appendChild(td); + + td = document.createElement('td'); + var pageScaleInput = document.createElement('input'); + pageScaleInput.setAttribute('value', '100 %'); + pageScaleInput.setAttribute('size', '5'); + pageScaleInput.style.width = '50px'; + + td.appendChild(pageScaleInput); + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '20px'; + td.setAttribute('align', 'right'); + + // Overall scale for print-out to account for print borders in dialogs etc + function preview(print) + { + var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked; + var printScale = parseInt(pageScaleInput.value) / 100; + + if (isNaN(printScale)) + { + printScale = 1; + pageScaleInput.value = '100%'; + } + + // Workaround to match available paper size in actual print output + printScale *= 0.75; + + var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT; + var scale = 1 / graph.pageScale; + + if (autoOrigin) + { + var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value); + + if (!isNaN(pageCount)) + { + scale = mxUtils.getScaleForPageCount(pageCount, graph, pf); + } + } + + // Negative coordinates are cropped or shifted if page visible + var gb = graph.getGraphBounds(); + var border = 0; + var x0 = 0; + var y0 = 0; + + // Applies print scale + pf = mxRectangle.fromRectangle(pf); + pf.width = Math.ceil(pf.width * printScale); + pf.height = Math.ceil(pf.height * printScale); + scale *= printScale; + + // Starts at first visible page + if (!autoOrigin && graph.pageVisible) + { + var layout = graph.getPageLayout(); + x0 -= layout.x * pf.width; + y0 -= layout.y * pf.height; + } + else + { + autoOrigin = true; + } + + var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin); + preview.open(); + + if (print) + { + PrintDialog.printPreview(preview); + } + }; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (PrintDialog.previewEnabled) + { + var previewBtn = mxUtils.button(mxResources.get('preview'), function() + { + editorUi.hideDialog(); + preview(false); + }); + previewBtn.className = 'geBtn'; + td.appendChild(previewBtn); + } + + var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function() + { + editorUi.hideDialog(); + preview(true); + }); + printBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(printBtn); + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + + table.appendChild(tbody); + this.container = table; +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.printPreview = function(preview) +{ + if (preview.wnd != null) + { + var printFn = function() + { + preview.wnd.focus(); + preview.wnd.print(); + preview.wnd.close(); + }; + + // Workaround for Google Chrome which needs a bit of a + // delay in order to render the SVG contents + // Needs testing in production + if (mxClient.IS_GC) + { + window.setTimeout(printFn, 500); + } + else + { + printFn(); + } + } +}; + +/** + * Constructs a new print dialog. + */ +PrintDialog.createPrintPreview = function(graph, scale, pf, border, x0, y0, autoOrigin) +{ + var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0); + preview.title = mxResources.get('preview'); + preview.printBackgroundImage = true; + preview.autoOrigin = autoOrigin; + var bg = graph.background; + + if (bg == null || bg == '' || bg == mxConstants.NONE) + { + bg = '#ffffff'; + } + + preview.backgroundColor = bg; + + var writeHead = preview.writeHead; + + // Adds a border in the preview + preview.writeHead = function(doc) + { + writeHead.apply(this, arguments); + + doc.writeln(''); + }; + + return preview; +}; + +/** + * Specifies if the preview button should be enabled. Default is true. + */ +PrintDialog.previewEnabled = true; + +/** + * Constructs a new page setup dialog. + */ +var PageSetupDialog = function(editorUi) +{ + var graph = editorUi.editor.graph; + var row, td; + + var table = document.createElement('table'); + table.style.width = '100%'; + table.style.height = '100%'; + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.verticalAlign = 'top'; + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('paperSize') + ':'); + + row.appendChild(td); + + td = document.createElement('td'); + td.style.verticalAlign = 'top'; + td.style.fontSize = '10pt'; + + var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + mxUtils.write(td, mxResources.get('background') + ':'); + + row.appendChild(td); + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + + var backgroundInput = document.createElement('input'); + backgroundInput.setAttribute('type', 'text'); + var backgroundButton = document.createElement('button'); + + backgroundButton.style.width = '18px'; + backgroundButton.style.height = '18px'; + backgroundButton.style.marginRight = '20px'; + backgroundButton.style.backgroundPosition = 'center center'; + backgroundButton.style.backgroundRepeat = 'no-repeat'; + + var newBackgroundColor = graph.background; + + function updateBackgroundColor() + { + if (newBackgroundColor == null || newBackgroundColor == mxConstants.NONE) + { + backgroundButton.style.backgroundColor = ''; + backgroundButton.style.backgroundImage = 'url(\'' + Dialog.prototype.noColorImage + '\')'; + } + else + { + backgroundButton.style.backgroundColor = newBackgroundColor; + backgroundButton.style.backgroundImage = ''; + } + }; + + updateBackgroundColor(); + + mxEvent.addListener(backgroundButton, 'click', function(evt) + { + editorUi.pickColor(newBackgroundColor || 'none', function(color) + { + newBackgroundColor = color; + updateBackgroundColor(); + }); + mxEvent.consume(evt); + }); + + td.appendChild(backgroundButton); + + mxUtils.write(td, mxResources.get('gridSize') + ':'); + + var gridSizeInput = document.createElement('input'); + gridSizeInput.setAttribute('type', 'number'); + gridSizeInput.setAttribute('min', '0'); + gridSizeInput.style.width = '40px'; + gridSizeInput.style.marginLeft = '6px'; + + gridSizeInput.value = graph.getGridSize(); + td.appendChild(gridSizeInput); + + mxEvent.addListener(gridSizeInput, 'change', function() + { + var value = parseInt(gridSizeInput.value); + gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value); + }); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + + mxUtils.write(td, mxResources.get('image') + ':'); + + row.appendChild(td); + td = document.createElement('td'); + + var changeImageLink = document.createElement('a'); + changeImageLink.style.textDecoration = 'underline'; + changeImageLink.style.cursor = 'pointer'; + changeImageLink.style.color = '#a0a0a0'; + + var newBackgroundImage = graph.backgroundImage; + + function updateBackgroundImage() + { + if (newBackgroundImage == null) + { + changeImageLink.removeAttribute('title'); + changeImageLink.style.fontSize = ''; + changeImageLink.innerHTML = mxResources.get('change') + '...'; + } + else + { + changeImageLink.setAttribute('title', newBackgroundImage.src); + changeImageLink.style.fontSize = '11px'; + changeImageLink.innerHTML = newBackgroundImage.src.substring(0, 42) + '...'; + } + }; + + mxEvent.addListener(changeImageLink, 'click', function(evt) + { + editorUi.showBackgroundImageDialog(function(image) + { + newBackgroundImage = image; + updateBackgroundImage(); + }); + + mxEvent.consume(evt); + }); + + updateBackgroundImage(); + + td.appendChild(changeImageLink); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '16px'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + editorUi.hideDialog(); + + if (graph.gridSize !== gridSizeInput.value) + { + graph.setGridSize(parseInt(gridSizeInput.value)); + } + + var change = new ChangePageSetup(editorUi, newBackgroundColor, + newBackgroundImage, accessor.get()); + change.ignoreColor = graph.background == newBackgroundColor; + + var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null; + var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null; + + change.ignoreImage = oldSrc === newSrc; + + if (graph.pageFormat.width != change.previousFormat.width || + graph.pageFormat.height != change.previousFormat.height || + !change.ignoreColor || !change.ignoreImage) + { + graph.model.execute(change); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(applyBtn); + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + + table.appendChild(tbody); + this.container = table; +}; + +/** + * + */ +PageSetupDialog.addPageFormatPanel = function(div, namePostfix, pageFormat, pageFormatListener) +{ + var formatName = 'format-' + namePostfix; + + var portraitCheckBox = document.createElement('input'); + portraitCheckBox.setAttribute('name', formatName); + portraitCheckBox.setAttribute('type', 'radio'); + portraitCheckBox.setAttribute('value', 'portrait'); + + var landscapeCheckBox = document.createElement('input'); + landscapeCheckBox.setAttribute('name', formatName); + landscapeCheckBox.setAttribute('type', 'radio'); + landscapeCheckBox.setAttribute('value', 'landscape'); + + var paperSizeSelect = document.createElement('select'); + paperSizeSelect.style.marginBottom = '8px'; + paperSizeSelect.style.width = '202px'; + + var formatDiv = document.createElement('div'); + formatDiv.style.marginLeft = '4px'; + formatDiv.style.width = '210px'; + formatDiv.style.height = '24px'; + + portraitCheckBox.style.marginRight = '6px'; + formatDiv.appendChild(portraitCheckBox); + + var portraitSpan = document.createElement('span'); + portraitSpan.style.maxWidth = '100px'; + mxUtils.write(portraitSpan, mxResources.get('portrait')); + formatDiv.appendChild(portraitSpan); + + landscapeCheckBox.style.marginLeft = '10px'; + landscapeCheckBox.style.marginRight = '6px'; + formatDiv.appendChild(landscapeCheckBox); + + var landscapeSpan = document.createElement('span'); + landscapeSpan.style.width = '100px'; + mxUtils.write(landscapeSpan, mxResources.get('landscape')); + formatDiv.appendChild(landscapeSpan) + + var customDiv = document.createElement('div'); + customDiv.style.marginLeft = '4px'; + customDiv.style.width = '210px'; + customDiv.style.height = '24px'; + + var widthInput = document.createElement('input'); + widthInput.setAttribute('size', '7'); + widthInput.style.textAlign = 'right'; + customDiv.appendChild(widthInput); + mxUtils.write(customDiv, ' in x '); + + var heightInput = document.createElement('input'); + heightInput.setAttribute('size', '7'); + heightInput.style.textAlign = 'right'; + customDiv.appendChild(heightInput); + mxUtils.write(customDiv, ' in'); + + formatDiv.style.display = 'none'; + customDiv.style.display = 'none'; + + var pf = new Object(); + var formats = PageSetupDialog.getFormats(); + + for (var i = 0; i < formats.length; i++) + { + var f = formats[i]; + pf[f.key] = f; + + var paperSizeOption = document.createElement('option'); + paperSizeOption.setAttribute('value', f.key); + mxUtils.write(paperSizeOption, f.title); + paperSizeSelect.appendChild(paperSizeOption); + } + + var customSize = false; + + function listener(sender, evt, force) + { + if (force || (widthInput != document.activeElement && heightInput != document.activeElement)) + { + var detected = false; + + for (var i = 0; i < formats.length; i++) + { + var f = formats[i]; + + // Special case where custom was chosen + if (customSize) + { + if (f.key == 'custom') + { + paperSizeSelect.value = f.key; + customSize = false; + } + } + else if (f.format != null) + { + // Fixes wrong values for previous A4 and A5 page sizes + if (f.key == 'a4') + { + if (pageFormat.width == 826) + { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.width = 827; + } + else if (pageFormat.height == 826) + { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.height = 827; + } + } + else if (f.key == 'a5') + { + if (pageFormat.width == 584) + { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.width = 583; + } + else if (pageFormat.height == 584) + { + pageFormat = mxRectangle.fromRectangle(pageFormat); + pageFormat.height = 583; + } + } + + if (pageFormat.width == f.format.width && pageFormat.height == f.format.height) + { + paperSizeSelect.value = f.key; + portraitCheckBox.setAttribute('checked', 'checked'); + portraitCheckBox.defaultChecked = true; + portraitCheckBox.checked = true; + landscapeCheckBox.removeAttribute('checked'); + landscapeCheckBox.defaultChecked = false; + landscapeCheckBox.checked = false; + detected = true; + } + else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width) + { + paperSizeSelect.value = f.key; + portraitCheckBox.removeAttribute('checked'); + portraitCheckBox.defaultChecked = false; + portraitCheckBox.checked = false; + landscapeCheckBox.setAttribute('checked', 'checked'); + landscapeCheckBox.defaultChecked = true; + landscapeCheckBox.checked = true; + detected = true; + } + } + } + + // Selects custom format which is last in list + if (!detected) + { + widthInput.value = pageFormat.width / 100; + heightInput.value = pageFormat.height / 100; + portraitCheckBox.setAttribute('checked', 'checked'); + paperSizeSelect.value = 'custom'; + formatDiv.style.display = 'none'; + customDiv.style.display = ''; + } + else + { + formatDiv.style.display = ''; + customDiv.style.display = 'none'; + } + } + }; + + listener(); + + div.appendChild(paperSizeSelect); + mxUtils.br(div); + + div.appendChild(formatDiv); + div.appendChild(customDiv); + + var currentPageFormat = pageFormat; + + var update = function(evt, selectChanged) + { + var f = pf[paperSizeSelect.value]; + + if (f.format != null) + { + widthInput.value = f.format.width / 100; + heightInput.value = f.format.height / 100; + customDiv.style.display = 'none'; + formatDiv.style.display = ''; + } + else + { + formatDiv.style.display = 'none'; + customDiv.style.display = ''; + } + + if (isNaN(parseFloat(widthInput.value))) + { + widthInput.value = pageFormat.width / 100; + } + + if (isNaN(parseFloat(heightInput.value))) + { + heightInput.value = pageFormat.height / 100; + } + + var newPageFormat = new mxRectangle(0, 0, + Math.floor(parseFloat(widthInput.value) * 100), + Math.floor(parseFloat(heightInput.value) * 100)); + + if (paperSizeSelect.value != 'custom' && landscapeCheckBox.checked) + { + newPageFormat = new mxRectangle(0, 0, newPageFormat.height, newPageFormat.width); + } + + // Initial select of custom should not update page format to avoid update of combo + if ((!selectChanged || !customSize) && (newPageFormat.width != currentPageFormat.width || + newPageFormat.height != currentPageFormat.height)) + { + currentPageFormat = newPageFormat; + + // Updates page format and reloads format panel + if (pageFormatListener != null) + { + pageFormatListener(currentPageFormat); + } + } + }; + + mxEvent.addListener(portraitSpan, 'click', function(evt) + { + portraitCheckBox.checked = true; + update(evt); + mxEvent.consume(evt); + }); + + mxEvent.addListener(landscapeSpan, 'click', function(evt) + { + landscapeCheckBox.checked = true; + update(evt); + mxEvent.consume(evt); + }); + + mxEvent.addListener(widthInput, 'blur', update); + mxEvent.addListener(widthInput, 'click', update); + mxEvent.addListener(heightInput, 'blur', update); + mxEvent.addListener(heightInput, 'click', update); + mxEvent.addListener(landscapeCheckBox, 'change', update); + mxEvent.addListener(portraitCheckBox, 'change', update); + mxEvent.addListener(paperSizeSelect, 'change', function(evt) + { + // Handles special case where custom was chosen + customSize = paperSizeSelect.value == 'custom'; + update(evt, true); + }); + + update(); + + return {set: function(value) + { + pageFormat = value; + listener(null, null, true); + },get: function() + { + return currentPageFormat; + }, widthInput: widthInput, + heightInput: heightInput}; +}; + +/** + * + */ +PageSetupDialog.getFormats = function() +{ + return [{key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT}, + {key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400)}, + {key: 'tabloid', title: 'US-Tabloid (279 mm x 432 mm)', format: new mxRectangle(0, 0, 1100, 1700)}, + {key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681)}, + {key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300)}, + {key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336)}, + {key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654)}, + {key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT}, + {key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827)}, + {key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583)}, + {key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413)}, + {key: 'custom', title: mxResources.get('custom'), format: null}]; +}; + +/** + * Static overrides + */ +(function() +{ + // Uses HTML for background pages (to support grid background image) + mxGraphView.prototype.validateBackgroundPage = function() + { + var graph = this.graph; + + if (graph.container != null && !graph.transparentBackground) + { + if (graph.pageVisible) + { + var bounds = this.getBackgroundPageBounds(); + + if (this.backgroundPageShape == null) + { + // Finds first element in graph container + var firstChild = graph.container.firstChild; + + while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT) + { + firstChild = firstChild.nextSibling; + } + + if (firstChild != null) + { + this.backgroundPageShape = this.createBackgroundPageShape(bounds); + this.backgroundPageShape.scale = 1; + + // Shadow filter causes problems in outline window in quirks mode. IE8 standards + // also has known rendering issues inside mxWindow but not using shadow is worse. + this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS; + this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML; + this.backgroundPageShape.init(graph.container); + + // Required for the browser to render the background page in correct order + firstChild.style.position = 'absolute'; + graph.container.insertBefore(this.backgroundPageShape.node, firstChild); + this.backgroundPageShape.redraw(); + + this.backgroundPageShape.node.className = 'geBackgroundPage'; + + // Adds listener for double click handling on background + mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', + mxUtils.bind(this, function(evt) + { + graph.dblClick(evt); + }) + ); + + // Adds basic listeners for graph event dispatching outside of the + // container and finishing the handling of a single gesture + mxEvent.addGestureListeners(this.backgroundPageShape.node, + mxUtils.bind(this, function(evt) + { + graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt)); + }), + mxUtils.bind(this, function(evt) + { + // Hides the tooltip if mouse is outside container + if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) + { + graph.tooltipHandler.hide(); + } + + if (graph.isMouseDown && !mxEvent.isConsumed(evt)) + { + graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt)); + } + }), + mxUtils.bind(this, function(evt) + { + graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt)); + }) + ); + } + } + else + { + this.backgroundPageShape.scale = 1; + this.backgroundPageShape.bounds = bounds; + this.backgroundPageShape.redraw(); + } + } + else if (this.backgroundPageShape != null) + { + this.backgroundPageShape.destroy(); + this.backgroundPageShape = null; + } + + this.validateBackgroundStyles(); + } + }; + + // Updates the CSS of the background to draw the grid + mxGraphView.prototype.validateBackgroundStyles = function() + { + var graph = this.graph; + var color = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; + var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff'; + var image = 'none'; + var position = ''; + + if (graph.isGridEnabled()) + { + var phase = 10; + + if (mxClient.IS_SVG) + { + // Generates the SVG required for drawing the dynamic grid + image = unescape(encodeURIComponent(this.createSvgGrid(gridColor))); + image = (window.btoa) ? btoa(image) : Base64.encode(image, true); + image = 'url(' + 'data:image/svg+xml;base64,' + image + ')' + phase = graph.gridSize * this.scale * this.gridSteps; + } + else + { + // Fallback to grid wallpaper with fixed size + image = 'url(' + this.gridImage + ')'; + } + + var x0 = 0; + var y0 = 0; + + if (graph.view.backgroundPageShape != null) + { + var bds = this.getBackgroundPageBounds(); + + x0 = 1 + bds.x; + y0 = 1 + bds.y; + } + + // Computes the offset to maintain origin for grid + position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' + + -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px'; + } + + var canvas = graph.view.canvas; + + if (canvas.ownerSVGElement != null) + { + canvas = canvas.ownerSVGElement; + } + + if (graph.view.backgroundPageShape != null) + { + graph.view.backgroundPageShape.node.style.backgroundPosition = position; + graph.view.backgroundPageShape.node.style.backgroundImage = image; + graph.view.backgroundPageShape.node.style.backgroundColor = color; + graph.container.className = 'geDiagramContainer geDiagramBackdrop'; + canvas.style.backgroundImage = 'none'; + canvas.style.backgroundColor = ''; + } + else + { + graph.container.className = 'geDiagramContainer'; + canvas.style.backgroundPosition = position; + canvas.style.backgroundColor = color; + canvas.style.backgroundImage = image; + } + }; + + // Returns the SVG required for painting the background grid. + mxGraphView.prototype.createSvgGrid = function(color) + { + var tmp = this.graph.gridSize * this.scale; + + while (tmp < this.minGridSize) + { + tmp *= 2; + } + + var tmp2 = this.gridSteps * tmp; + + // Small grid lines + var d = []; + + for (var i = 1; i < this.gridSteps; i++) + { + var tmp3 = i * tmp; + d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2); + } + + // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround + // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams. + var size = tmp2; + var svg = '' + + '' + + '' + + '' + + ''; + + return svg; + }; + + // Adds panning for the grid with no page view and disabled scrollbars + var mxGraphPanGraph = mxGraph.prototype.panGraph; + mxGraph.prototype.panGraph = function(dx, dy) + { + mxGraphPanGraph.apply(this, arguments); + + if (this.shiftPreview1 != null) + { + var canvas = this.view.canvas; + + if (canvas.ownerSVGElement != null) + { + canvas = canvas.ownerSVGElement; + } + + var phase = this.gridSize * this.view.scale * this.view.gridSteps; + var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' + + -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px'; + canvas.style.backgroundPosition = position; + } + }; + + // Draws page breaks only within the page + mxGraph.prototype.updatePageBreaks = function(visible, width, height) + { + var scale = this.view.scale; + var tr = this.view.translate; + var fmt = this.pageFormat; + var ps = scale * this.pageScale; + + var bounds2 = this.view.getBackgroundPageBounds(); + + width = bounds2.width; + height = bounds2.height; + var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps); + + // Does not show page breaks if the scale is too small + visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist; + + var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0; + var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0; + var right = bounds2.x + width; + var bottom = bounds2.y + height; + + if (this.horizontalPageBreaks == null && horizontalCount > 0) + { + this.horizontalPageBreaks = []; + } + + if (this.verticalPageBreaks == null && verticalCount > 0) + { + this.verticalPageBreaks = []; + } + + var drawPageBreaks = mxUtils.bind(this, function(breaks) + { + if (breaks != null) + { + var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount; + + for (var i = 0; i <= count; i++) + { + var pts = (breaks == this.horizontalPageBreaks) ? + [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)), + new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] : + [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)), + new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))]; + + if (breaks[i] != null) + { + breaks[i].points = pts; + breaks[i].redraw(); + } + else + { + var pageBreak = new mxPolyline(pts, this.pageBreakColor); + pageBreak.dialect = this.dialect; + pageBreak.isDashed = this.pageBreakDashed; + pageBreak.pointerEvents = false; + pageBreak.init(this.view.backgroundPane); + pageBreak.redraw(); + + breaks[i] = pageBreak; + } + } + + for (var i = count; i < breaks.length; i++) + { + breaks[i].destroy(); + } + + breaks.splice(count, breaks.length - count); + } + }); + + drawPageBreaks(this.horizontalPageBreaks); + drawPageBreaks(this.verticalPageBreaks); + }; + + // Disables removing relative children from parents + var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent; + mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt) + { + for (var i = 0; i < cells.length; i++) + { + if (this.graph.getModel().isVertex(cells[i])) + { + var geo = this.graph.getCellGeometry(cells[i]); + + if (geo != null && geo.relative) + { + return false; + } + } + } + + return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments); + }; + + // Overrides to ignore hotspot only for target terminal + var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; + mxConnectionHandler.prototype.createMarker = function() + { + var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); + + marker.intersects = mxUtils.bind(this, function(state, evt) + { + if (this.isConnecting()) + { + return true; + } + + return mxCellMarker.prototype.intersects.apply(marker, arguments); + }); + + return marker; + }; + + // Creates background page shape + mxGraphView.prototype.createBackgroundPageShape = function(bounds) + { + return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor); + }; + + // Fits the number of background pages to the graph + mxGraphView.prototype.getBackgroundPageBounds = function() + { + var gb = this.getGraphBounds(); + + // Computes unscaled, untranslated graph bounds + var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0; + var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0; + var w = gb.width / this.scale; + var h = gb.height / this.scale; + + var fmt = this.graph.pageFormat; + var ps = this.graph.pageScale; + + var pw = fmt.width * ps; + var ph = fmt.height * ps; + + var x0 = Math.floor(Math.min(0, x) / pw); + var y0 = Math.floor(Math.min(0, y) / ph); + var xe = Math.ceil(Math.max(1, x + w) / pw); + var ye = Math.ceil(Math.max(1, y + h) / ph); + + var rows = xe - x0; + var cols = ye - y0; + + var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale * + (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph); + + return bounds; + }; + + // Add panning for background page in VML + var graphPanGraph = mxGraph.prototype.panGraph; + mxGraph.prototype.panGraph = function(dx, dy) + { + graphPanGraph.apply(this, arguments); + + if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) && + (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container))) + { + this.view.backgroundPageShape.node.style.marginLeft = dx + 'px'; + this.view.backgroundPageShape.node.style.marginTop = dy + 'px'; + } + }; + + /** + * Consumes click events for disabled menu items. + */ + var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem; + mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled) + { + var result = mxPopupMenuAddItem.apply(this, arguments); + + if (enabled != null && !enabled) + { + mxEvent.addListener(result, 'mousedown', function(evt) + { + mxEvent.consume(evt); + }); + } + + return result; + }; + + // Selects ancestors before descendants + var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent; + mxGraphHandler.prototype.getInitialCellForEvent = function(me) + { + var model = this.graph.getModel(); + var psel = model.getParent(this.graph.getSelectionCell()); + var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments); + var parent = model.getParent(cell); + + if (psel == null || (psel != cell && psel != parent)) + { + while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) && + model.isVertex(parent) && !this.graph.isContainer(parent)) + { + cell = parent; + parent = this.graph.getModel().getParent(cell); + } + } + + return cell; + }; + + // Selection is delayed to mouseup if ancestor is selected + var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection; + mxGraphHandler.prototype.isDelayedSelection = function(cell, me) + { + var result = graphHandlerIsDelayedSelection.apply(this, arguments); + + if (!result) + { + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (parent != null) + { + // Inconsistency for unselected parent swimlane is intended for easier moving + // of stack layouts where the container title section is too far away + if (this.graph.isCellSelected(parent) && model.isVertex(parent)) + { + result = true; + break; + } + + parent = model.getParent(parent); + } + } + + return result; + }; + + // Delayed selection of parent group + mxGraphHandler.prototype.selectDelayed = function(me) + { + if (!this.graph.popupMenuHandler.isPopupTrigger(me)) + { + var cell = me.getCell(); + + if (cell == null) + { + cell = this.cell; + } + + // Selects folded cell for hit on folding icon + var state = this.graph.view.getState(cell) + + if (state != null && me.isSource(state.control)) + { + this.graph.selectCellForEvent(cell, me.getEvent()); + } + else + { + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (!this.graph.isCellSelected(parent) && model.isVertex(parent)) + { + cell = parent; + parent = model.getParent(cell); + } + + this.graph.selectCellForEvent(cell, me.getEvent()); + } + } + }; + + // Returns last selected ancestor + mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me) + { + var cell = me.getCell(); + var model = this.graph.getModel(); + var parent = model.getParent(cell); + + while (model.isVertex(parent) && !this.graph.isContainer(parent)) + { + if (this.graph.isCellSelected(parent)) + { + cell = parent; + } + + parent = model.getParent(parent); + } + + return cell; + }; + +})(); diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/EditorUi.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/EditorUi.js new file mode 100644 index 00000000..aed65272 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/EditorUi.js @@ -0,0 +1,4234 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new graph editor + */ +EditorUi = function(editor, container, lightbox) +{ + mxEventSource.call(this); + + this.destroyFunctions = []; + this.editor = editor || new Editor(); + this.container = container || document.body; + + var graph = this.editor.graph; + graph.lightbox = lightbox; + graph.useCssTransforms = + this.editor.isChromelessView() && + graph.isCssTransformsSupported(); + + // Faster scrollwheel zoom is possible with CSS transforms + if (graph.useCssTransforms) + { + this.lazyZoomDelay = 0; + } + + // Pre-fetches submenu image or replaces with embedded image if supported + if (mxClient.IS_SVG) + { + mxPopupMenu.prototype.submenuImage = 'data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs='; + } + else + { + new Image().src = mxPopupMenu.prototype.submenuImage; + } + + // Pre-fetches connect image + if (!mxClient.IS_SVG && mxConnectionHandler.prototype.connectImage != null) + { + new Image().src = mxConnectionHandler.prototype.connectImage.src; + } + + // Disables graph and forced panning in chromeless mode + if (this.editor.chromeless && !this.editor.editable) + { + this.footerHeight = 0; + graph.isEnabled = function() { return false; }; + graph.panningHandler.isForcePanningEvent = function(me) + { + return !mxEvent.isPopupTrigger(me.getEvent()); + }; + } + + // Creates the user interface + this.actions = new Actions(this); + this.menus = this.createMenus(); + this.createDivs(); + this.createUi(); + this.refresh(); + + // Disables HTML and text selection + var textEditing = mxUtils.bind(this, function(evt) + { + if (evt == null) + { + evt = window.event; + } + return (this.isSelectionAllowed(evt) || graph.isEditing()); + }); + + //begin add by wsp, 直接返回 true + //作者本意是想禁止用户选择界面文字,但会带来一个问题,当自定义 format 面板中需要动态产生一个 textarea 时, + //因为这个函数将导致生成的 textarea 无法获得焦点。 + var textEditing2 = mxUtils.bind(this, function(evt) + { + if (evt == null) + { + evt = window.event; + } + return true; + }); + //end add by wsp + + // Disables text selection while not editing and no dialog visible + if (this.container == document.body) + { + this.menubarContainer.onselectstart = textEditing; + this.menubarContainer.onmousedown = textEditing; + this.toolbarContainer.onselectstart = textEditing; + this.toolbarContainer.onmousedown = textEditing; + this.diagramContainer.onselectstart = textEditing; + this.diagramContainer.onmousedown = textEditing; + this.sidebarContainer.onselectstart = textEditing; + this.sidebarContainer.onmousedown = textEditing; + //begin modify by wsp,让格式面板中的 textarea 可获得焦点 + this.formatContainer.onselectstart = textEditing2; + this.formatContainer.onmousedown = textEditing2; + //end modify by wsp + this.footerContainer.onselectstart = textEditing; + this.footerContainer.onmousedown = textEditing; + + if (this.tabContainer != null) + { + // Mouse down is needed for drag and drop + this.tabContainer.onselectstart = textEditing; + } + } + + // And uses built-in context menu while editing + if (!this.editor.chromeless || this.editor.editable) + { + // Allows context menu for links in hints + var linkHandler = function(evt) + { + var source = mxEvent.getSource(evt); + + if (source.nodeName == 'A') + { + while (source != null) + { + if (source.className == 'geHint') + { + return true; + } + + source = source.parentNode; + } + } + + return textEditing(evt); + }; + + if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) + { + mxEvent.addListener(this.diagramContainer, 'contextmenu', linkHandler); + } + else + { + // Allows browser context menu outside of diagram and sidebar + this.diagramContainer.oncontextmenu = linkHandler; + } + } + else + { + graph.panningHandler.usePopupTrigger = false; + } + + // Contains the main graph instance inside the given panel + graph.init(this.diagramContainer); + + // Improves line wrapping for in-place editor + if (mxClient.IS_SVG && graph.view.getDrawPane() != null) + { + var root = graph.view.getDrawPane().ownerSVGElement; + + if (root != null) + { + root.style.position = 'absolute'; + } + } + + // Creates hover icons + this.hoverIcons = this.createHoverIcons(); + + // Adds tooltip when mouse is over scrollbars to show space-drag panning option + mxEvent.addListener(this.diagramContainer, 'mousemove', mxUtils.bind(this, function(evt) + { + var off = mxUtils.getOffset(this.diagramContainer); + + if (mxEvent.getClientX(evt) - off.x - this.diagramContainer.clientWidth > 0 || + mxEvent.getClientY(evt) - off.y - this.diagramContainer.clientHeight > 0) + { + this.diagramContainer.setAttribute('title', mxResources.get('panTooltip')); + } + else + { + this.diagramContainer.removeAttribute('title'); + } + })); + + // Escape key hides dialogs, adds space+drag panning + var spaceKeyPressed = false; + + // Overrides hovericons to disable while space key is pressed + var hoverIconsIsResetEvent = this.hoverIcons.isResetEvent; + + this.hoverIcons.isResetEvent = function(evt, allowShift) + { + return spaceKeyPressed || hoverIconsIsResetEvent.apply(this, arguments); + }; + + this.keydownHandler = mxUtils.bind(this, function(evt) + { + if (evt.which == 32 /* Space */) + { + spaceKeyPressed = true; + this.hoverIcons.reset(); + graph.container.style.cursor = 'move'; + + // Disables scroll after space keystroke with scrollbars + if (!graph.isEditing() && mxEvent.getSource(evt) == graph.container) + { + mxEvent.consume(evt); + } + } + else if (!mxEvent.isConsumed(evt) && evt.keyCode == 27 /* Escape */) + { + this.hideDialog(); + } + }); + + mxEvent.addListener(document, 'keydown', this.keydownHandler); + + this.keyupHandler = mxUtils.bind(this, function(evt) + { + graph.container.style.cursor = ''; + spaceKeyPressed = false; + }); + + mxEvent.addListener(document, 'keyup', this.keyupHandler); + + // Forces panning for middle and right mouse buttons + var panningHandlerIsForcePanningEvent = graph.panningHandler.isForcePanningEvent; + graph.panningHandler.isForcePanningEvent = function(me) + { + // Ctrl+left button is reported as right button in FF on Mac + return panningHandlerIsForcePanningEvent.apply(this, arguments) || + spaceKeyPressed || (mxEvent.isMouseEvent(me.getEvent()) && + (this.usePopupTrigger || !mxEvent.isPopupTrigger(me.getEvent())) && + ((!mxEvent.isControlDown(me.getEvent()) && + mxEvent.isRightMouseButton(me.getEvent())) || + mxEvent.isMiddleMouseButton(me.getEvent()))); + }; + + // Ctrl/Cmd+Enter applies editing value except in Safari where Ctrl+Enter creates + // a new line (while Enter creates a new paragraph and Shift+Enter stops) + var cellEditorIsStopEditingEvent = graph.cellEditor.isStopEditingEvent; + graph.cellEditor.isStopEditingEvent = function(evt) + { + return cellEditorIsStopEditingEvent.apply(this, arguments) || + (evt.keyCode == 13 && ((!mxClient.IS_SF && mxEvent.isControlDown(evt)) || + (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || + (mxClient.IS_SF && mxEvent.isShiftDown(evt)))); + }; + + // Switches toolbar for text editing + var textMode = false; + var fontMenu = null; + var sizeMenu = null; + var nodes = null; + + var updateToolbar = mxUtils.bind(this, function() + { + if (this.toolbar != null && textMode != graph.cellEditor.isContentEditing()) + { + var node = this.toolbar.container.firstChild; + var newNodes = []; + + while (node != null) + { + var tmp = node.nextSibling; + + if (mxUtils.indexOf(this.toolbar.staticElements, node) < 0) + { + node.parentNode.removeChild(node); + newNodes.push(node); + } + + node = tmp; + } + + // Saves references to special items + var tmp1 = this.toolbar.fontMenu; + var tmp2 = this.toolbar.sizeMenu; + + if (nodes == null) + { + this.toolbar.createTextToolbar(); + } + else + { + for (var i = 0; i < nodes.length; i++) + { + this.toolbar.container.appendChild(nodes[i]); + } + + // Restores references to special items + this.toolbar.fontMenu = fontMenu; + this.toolbar.sizeMenu = sizeMenu; + } + + textMode = graph.cellEditor.isContentEditing(); + fontMenu = tmp1; + sizeMenu = tmp2; + nodes = newNodes; + } + }); + + var ui = this; + + // Overrides cell editor to update toolbar + var cellEditorStartEditing = graph.cellEditor.startEditing; + graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + updateToolbar(); + + if (graph.cellEditor.isContentEditing()) + { + var updating = false; + + var updateCssHandler = function() + { + if (!updating) + { + updating = true; + + window.setTimeout(function() + { + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null) + { + var css = mxUtils.getCurrentStyle(node); + + if (css != null && ui.toolbar != null) + { + // Strips leading and trailing quotes + var ff = css.fontFamily; + + if (ff.charAt(0) == '\'') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '\'') + { + ff = ff.substring(0, ff.length - 1); + } + + ui.toolbar.setFontName(ff); + ui.toolbar.setFontSize(parseInt(css.fontSize)); + } + } + + updating = false; + }, 0); + } + }; + + mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) + mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); + updateCssHandler(); + } + }; + + var cellEditorStopEditing = graph.cellEditor.stopEditing; + graph.cellEditor.stopEditing = function(cell, trigger) + { + cellEditorStopEditing.apply(this, arguments); + updateToolbar(); + }; + + // Enables scrollbars and sets cursor style for the container + graph.container.setAttribute('tabindex', '0'); + graph.container.style.cursor = 'default'; + + // Workaround for page scroll if embedded via iframe + if (window.self === window.top && graph.container.parentNode != null) + { + try + { + graph.container.focus(); + } + catch (e) + { + // ignores error in old versions of IE + } + } + + // Keeps graph container focused on mouse down + var graphFireMouseEvent = graph.fireMouseEvent; + graph.fireMouseEvent = function(evtName, me, sender) + { + if (evtName == mxEvent.MOUSE_DOWN) + { + this.container.focus(); + } + + graphFireMouseEvent.apply(this, arguments); + }; + + // Configures automatic expand on mouseover + graph.popupMenuHandler.autoExpand = true; + + // Installs context menu + if (this.menus != null) + { + graph.popupMenuHandler.factoryMethod = mxUtils.bind(this, function(menu, cell, evt) + { + this.menus.createPopupMenu(menu, cell, evt); + }); + } + + // Hides context menu + mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt) + { + graph.popupMenuHandler.hideMenu(); + })); + + // Create handler for key events + this.keyHandler = this.createKeyHandler(editor); + + // Getter for key handler + this.getKeyHandler = function() + { + return keyHandler; + }; + + // Stores the current style and assigns it to new cells + var styles = ['rounded', 'shadow', 'glass', 'dashed', 'dashPattern', 'comic', 'labelBackgroundColor']; + var connectStyles = ['shape', 'edgeStyle', 'curved', 'rounded', 'elbow', 'comic', 'jumpStyle', 'jumpSize']; + + // Note: Everything that is not in styles is ignored (styles is augmented below) + this.setDefaultStyle = function(cell) + { + var state = graph.view.getState(cell); + + if (state != null) + { + // Ignores default styles + var clone = cell.clone(); + clone.style = '' + var defaultStyle = graph.getCellStyle(clone); + var values = []; + var keys = []; + + for (var key in state.style) + { + if (defaultStyle[key] != state.style[key]) + { + values.push(state.style[key]); + keys.push(key); + } + } + + // Handles special case for value "none" + var cellStyle = graph.getModel().getStyle(state.cell); + var tokens = (cellStyle != null) ? cellStyle.split(';') : []; + + for (var i = 0; i < tokens.length; i++) + { + var tmp = tokens[i]; + var pos = tmp.indexOf('='); + + if (pos >= 0) + { + var key = tmp.substring(0, pos); + var value = tmp.substring(pos + 1); + + if (defaultStyle[key] != null && value == 'none') + { + values.push(value); + keys.push(key); + } + } + } + + // Resets current style + if (graph.getModel().isEdge(state.cell)) + { + graph.currentEdgeStyle = {}; + } + else + { + graph.currentVertexStyle = {} + } + + this.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values, 'cells', [state.cell])); + } + }; + + this.clearDefaultStyle = function() + { + graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle); + graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle); + + // Updates UI + this.fireEvent(new mxEventObject('styleChanged', 'keys', [], 'values', [], 'cells', [])); + }; + + // Keys that should be ignored if the cell has a value (known: new default for all cells is html=1 so + // for the html key this effecticely only works for edges inserted via the connection handler) + var valueStyles = ['fontFamily', 'fontSize', 'fontColor']; + + // Keys that always update the current edge style regardless of selection + var alwaysEdgeStyles = ['edgeStyle', 'startArrow', 'startFill', 'startSize', 'endArrow', + 'endFill', 'endSize', 'jettySize', 'orthogonalLoop']; + + // Keys that are ignored together (if one appears all are ignored) + var keyGroups = [['startArrow', 'startFill', 'startSize', 'endArrow', 'endFill', 'endSize', 'jettySize', 'orthogonalLoop'], + ['strokeColor', 'strokeWidth'], + ['fillColor', 'gradientColor'], + valueStyles, + ['opacity'], + ['align'], + ['html']]; + + // Adds all keys used above to the styles array + for (var i = 0; i < keyGroups.length; i++) + { + for (var j = 0; j < keyGroups[i].length; j++) + { + styles.push(keyGroups[i][j]); + } + } + + for (var i = 0; i < connectStyles.length; i++) + { + if (mxUtils.indexOf(styles, connectStyles[i]) < 0) + { + styles.push(connectStyles[i]); + } + } + + // Implements a global current style for edges and vertices that is applied to new cells + var insertHandler = function(cells, asText) + { + var model = graph.getModel(); + + model.beginUpdate(); + try + { + // Applies only basic text styles + if (asText) + { + var edge = model.isEdge(cell); + var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle; + var textStyles = ['fontSize', 'fontFamily', 'fontColor']; + + for (var j = 0; j < textStyles.length; j++) + { + var value = current[textStyles[j]]; + + if (value != null) + { + graph.setCellStyles(textStyles[j], value, cells); + } + } + } + else + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + // Removes styles defined in the cell style from the styles to be applied + var cellStyle = model.getStyle(cell); + var tokens = (cellStyle != null) ? cellStyle.split(';') : []; + var appliedStyles = styles.slice(); + + for (var j = 0; j < tokens.length; j++) + { + var tmp = tokens[j]; + var pos = tmp.indexOf('='); + + if (pos >= 0) + { + var key = tmp.substring(0, pos); + var index = mxUtils.indexOf(appliedStyles, key); + + if (index >= 0) + { + appliedStyles.splice(index, 1); + } + + // Handles special cases where one defined style ignores other styles + for (var k = 0; k < keyGroups.length; k++) + { + var group = keyGroups[k]; + + if (mxUtils.indexOf(group, key) >= 0) + { + for (var l = 0; l < group.length; l++) + { + var index2 = mxUtils.indexOf(appliedStyles, group[l]); + + if (index2 >= 0) + { + appliedStyles.splice(index2, 1); + } + } + } + } + } + } + + // Applies the current style to the cell + var edge = model.isEdge(cell); + var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle; + var newStyle = model.getStyle(cell); + + for (var j = 0; j < appliedStyles.length; j++) + { + var key = appliedStyles[j]; + var styleValue = current[key]; + + if (styleValue != null && (key != 'shape' || edge)) + { + // Special case: Connect styles are not applied here but in the connection handler + if (!edge || mxUtils.indexOf(connectStyles, key) < 0) + { + newStyle = mxUtils.setStyle(newStyle, key, styleValue); + } + } + } + + model.setStyle(cell, newStyle); + } + } + } + finally + { + model.endUpdate(); + } + }; + + graph.addListener('cellsInserted', function(sender, evt) + { + insertHandler(evt.getProperty('cells')); + }); + + graph.addListener('textInserted', function(sender, evt) + { + insertHandler(evt.getProperty('cells'), true); + }); + + graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt) + { + var cells = [evt.getProperty('cell')]; + + if (evt.getProperty('terminalInserted')) + { + cells.push(evt.getProperty('terminal')); + } + + insertHandler(cells); + }); + + this.addListener('styleChanged', mxUtils.bind(this, function(sender, evt) + { + // Checks if edges and/or vertices were modified + var cells = evt.getProperty('cells'); + var vertex = false; + var edge = false; + + if (cells.length > 0) + { + for (var i = 0; i < cells.length; i++) + { + vertex = graph.getModel().isVertex(cells[i]) || vertex; + edge = graph.getModel().isEdge(cells[i]) || edge; + + if (edge && vertex) + { + break; + } + } + } + else + { + vertex = true; + edge = true; + } + + var keys = evt.getProperty('keys'); + var values = evt.getProperty('values'); + + for (var i = 0; i < keys.length; i++) + { + var common = mxUtils.indexOf(valueStyles, keys[i]) >= 0; + + // Ignores transparent stroke colors + if (keys[i] != 'strokeColor' || (values[i] != null && values[i] != 'none')) + { + // Special case: Edge style and shape + if (mxUtils.indexOf(connectStyles, keys[i]) >= 0) + { + if (edge || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentEdgeStyle[keys[i]]; + } + else + { + graph.currentEdgeStyle[keys[i]] = values[i]; + } + } + // Uses style for vertex if defined in styles + else if (vertex && mxUtils.indexOf(styles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentVertexStyle[keys[i]]; + } + else + { + graph.currentVertexStyle[keys[i]] = values[i]; + } + } + } + else if (mxUtils.indexOf(styles, keys[i]) >= 0) + { + if (vertex || common) + { + if (values[i] == null) + { + delete graph.currentVertexStyle[keys[i]]; + } + else + { + graph.currentVertexStyle[keys[i]] = values[i]; + } + } + + if (edge || common || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) + { + if (values[i] == null) + { + delete graph.currentEdgeStyle[keys[i]]; + } + else + { + graph.currentEdgeStyle[keys[i]] = values[i]; + } + } + } + } + } + + if (this.toolbar != null) + { + this.toolbar.setFontName(graph.currentVertexStyle['fontFamily'] || Menus.prototype.defaultFont); + this.toolbar.setFontSize(graph.currentVertexStyle['fontSize'] || Menus.prototype.defaultFontSize); + + if (this.toolbar.edgeStyleMenu != null) + { + // Updates toolbar icon for edge style + var edgeStyleDiv = this.toolbar.edgeStyleMenu.getElementsByTagName('div')[0]; + + if (graph.currentEdgeStyle['edgeStyle'] == 'orthogonalEdgeStyle' && graph.currentEdgeStyle['curved'] == '1') + { + edgeStyleDiv.className = 'geSprite geSprite-curved'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'straight' || graph.currentEdgeStyle['edgeStyle'] == 'none' || + graph.currentEdgeStyle['edgeStyle'] == null) + { + edgeStyleDiv.className = 'geSprite geSprite-straight'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'entityRelationEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-entity'; + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? + 'verticalelbow' : 'horizontalelbow'); + } + else if (graph.currentEdgeStyle['edgeStyle'] == 'isometricEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? + 'verticalisometric' : 'horizontalisometric'); + } + else + { + edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; + } + } + + if (this.toolbar.edgeShapeMenu != null) + { + // Updates icon for edge shape + var edgeShapeDiv = this.toolbar.edgeShapeMenu.getElementsByTagName('div')[0]; + + if (graph.currentEdgeStyle['shape'] == 'link') + { + edgeShapeDiv.className = 'geSprite geSprite-linkedge'; + } + else if (graph.currentEdgeStyle['shape'] == 'flexArrow') + { + edgeShapeDiv.className = 'geSprite geSprite-arrow'; + } + else if (graph.currentEdgeStyle['shape'] == 'arrow') + { + edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; + } + else + { + edgeShapeDiv.className = 'geSprite geSprite-connection'; + } + } + + // Updates icon for optinal line start shape + if (this.toolbar.lineStartMenu != null) + { + var lineStartDiv = this.toolbar.lineStartMenu.getElementsByTagName('div')[0]; + + lineStartDiv.className = this.getCssClassForMarker('start', + graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_STARTARROW], + mxUtils.getValue(graph.currentEdgeStyle, 'startFill', '1')); + } + + // Updates icon for optinal line end shape + if (this.toolbar.lineEndMenu != null) + { + var lineEndDiv = this.toolbar.lineEndMenu.getElementsByTagName('div')[0]; + + lineEndDiv.className = this.getCssClassForMarker('end', + graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_ENDARROW], + mxUtils.getValue(graph.currentEdgeStyle, 'endFill', '1')); + } + } + })); + + // Update font size and font family labels + if (this.toolbar != null) + { + var update = mxUtils.bind(this, function() + { + var ff = graph.currentVertexStyle['fontFamily'] || 'Helvetica'; + var fs = String(graph.currentVertexStyle['fontSize'] || '12'); + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + ff = state.style[mxConstants.STYLE_FONTFAMILY] || ff; + fs = state.style[mxConstants.STYLE_FONTSIZE] || fs; + + if (ff.length > 10) + { + ff = ff.substring(0, 8) + '...'; + } + } + + this.toolbar.setFontName(ff); + this.toolbar.setFontSize(fs); + }); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, update); + graph.getModel().addListener(mxEvent.CHANGE, update); + } + + // Makes sure the current layer is visible when cells are added + graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) + { + var cells = evt.getProperty('cells'); + var parent = evt.getProperty('parent'); + + if (graph.getModel().isLayer(parent) && !graph.isCellVisible(parent) && cells != null && cells.length > 0) + { + graph.getModel().setVisible(parent, true); + } + }); + + // Global handler to hide the current menu + this.gestureHandler = mxUtils.bind(this, function(evt) + { + if (this.currentMenu != null && mxEvent.getSource(evt) != this.currentMenu.div) + { + this.hideCurrentMenu(); + } + }); + + mxEvent.addGestureListeners(document, this.gestureHandler); + + // Updates the editor UI after the window has been resized or the orientation changes + // Timeout is workaround for old IE versions which have a delay for DOM client sizes. + // Should not use delay > 0 to avoid handle multiple repaints during window resize + this.resizeHandler = mxUtils.bind(this, function() + { + window.setTimeout(mxUtils.bind(this, function() + { + if (this.editor.graph != null) + { + this.refresh(); + } + }), 0); + }); + + mxEvent.addListener(window, 'resize', this.resizeHandler); + + this.orientationChangeHandler = mxUtils.bind(this, function() + { + this.refresh(); + }); + + mxEvent.addListener(window, 'orientationchange', this.orientationChangeHandler); + + // Workaround for bug on iOS see + // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue + if (mxClient.IS_IOS && !window.navigator.standalone) + { + this.scrollHandler = mxUtils.bind(this, function() + { + window.scrollTo(0, 0); + }); + + mxEvent.addListener(window, 'scroll', this.scrollHandler); + } + + /** + * Sets the initial scrollbar locations after a file was loaded. + */ + this.editor.addListener('resetGraphView', mxUtils.bind(this, function() + { + this.resetScrollbars(); + })); + + /** + * Repaints the grid. + */ + this.addListener('gridEnabledChanged', mxUtils.bind(this, function() + { + graph.view.validateBackground(); + })); + + this.addListener('backgroundColorChanged', mxUtils.bind(this, function() + { + graph.view.validateBackground(); + })); + + /** + * Repaints the grid. + */ + graph.addListener('gridSizeChanged', mxUtils.bind(this, function() + { + if (graph.isGridEnabled()) + { + graph.view.validateBackground(); + } + })); + + // Resets UI, updates action and menu states + this.editor.resetGraph(); + this.init(); + this.open(); +}; + +// Extends mxEventSource +mxUtils.extend(EditorUi, mxEventSource); + +/** + * Global config that specifies if the compact UI elements should be used. + */ +EditorUi.compactUi = true; + +/** + * Specifies the size of the split bar. + */ +EditorUi.prototype.splitSize = (mxClient.IS_TOUCH || mxClient.IS_POINTER) ? 12 : 8; + +/** + * Specifies the height of the menubar. Default is 34. + */ +//begin modify by wsp, 将菜单栏的高度设置为0,即隐藏菜单栏 +EditorUi.prototype.menubarHeight = 30; +//EditorUi.prototype.menubarHeight = 0; + +/** + * Specifies the width of the format panel should be enabled. Default is true. + */ +EditorUi.prototype.formatEnabled = true; + +/** + * Specifies the width of the format panel. Default is 240. + */ +EditorUi.prototype.formatWidth = 240; + +/** + * Specifies the height of the toolbar. Default is 36. + */ +EditorUi.prototype.toolbarHeight = 34; + +/** + * Specifies the height of the footer. Default is 28. + */ +EditorUi.prototype.footerHeight = 28; + +/** + * Specifies the height of the optional sidebarFooterContainer. Default is 34. + */ +EditorUi.prototype.sidebarFooterHeight = 34; + +/** + * Specifies the position of the horizontal split bar. Default is 208 or 118 for + * screen widths <= 640px. + */ +EditorUi.prototype.hsplitPosition = (screen.width <= 640) ? 118 : 208; + +/** + * Specifies if animations are allowed in . Default is true. + */ +EditorUi.prototype.allowAnimation = true; + +/** + * Specifies if animations are allowed in . Default is true. + */ +EditorUi.prototype.lightboxMaxFitScale = 2; + +/** + * Specifies if animations are allowed in . Default is true. + */ +EditorUi.prototype.lightboxVerticalDivider = 4; + +/** + * Specifies if single click on horizontal split should collapse sidebar. Default is false. + */ +EditorUi.prototype.hsplitClickEnabled = false; + +/** + * Installs the listeners to update the action states. + */ +EditorUi.prototype.init = function() +{ + /** + * Keypress starts immediate editing on selection cell + */ + var graph = this.editor.graph; + + mxEvent.addListener(graph.container, 'keydown', mxUtils.bind(this, function(evt) + { + this.onKeyDown(evt); + })); + mxEvent.addListener(graph.container, 'keypress', mxUtils.bind(this, function(evt) + { + this.onKeyPress(evt); + })); + + // Updates action states + this.addUndoListener(); + this.addBeforeUnloadListener(); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + this.updateActionStates(); + })); + + graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + this.updateActionStates(); + })); + + // Changes action states after change of default parent + var graphSetDefaultParent = graph.setDefaultParent; + var ui = this; + + this.editor.graph.setDefaultParent = function() + { + graphSetDefaultParent.apply(this, arguments); + ui.updateActionStates(); + }; + + // Hack to make editLink available in vertex handler + graph.editLink = ui.actions.get('editLink').funct; + + this.updateActionStates(); + this.initClipboard(); + this.initCanvas(); + + if (this.format != null) + { + this.format.init(); + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.onKeyDown = function(evt) +{ + var graph = this.editor.graph; + + // Tab selects next cell + if (evt.which == 9 && graph.isEnabled() && !mxEvent.isAltDown(evt)) + { + if (graph.isEditing()) + { + graph.stopEditing(false); + } + else + { + graph.selectCell(!mxEvent.isShiftDown(evt)); + } + + mxEvent.consume(evt); + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.onKeyPress = function(evt) +{ + var graph = this.editor.graph; + + // KNOWN: Focus does not work if label is empty in quirks mode + if (this.isImmediateEditingEvent(evt) && !graph.isEditing() && !graph.isSelectionEmpty() && evt.which !== 0 && + !mxEvent.isAltDown(evt) && !mxEvent.isControlDown(evt) && !mxEvent.isMetaDown(evt)) + { + graph.escape(); + graph.startEditing(); + + // Workaround for FF where char is lost if cursor is placed before char + if (mxClient.IS_FF) + { + var ce = graph.cellEditor; + ce.textarea.innerHTML = String.fromCharCode(evt.which); + + // Moves cursor to end of textarea + var range = document.createRange(); + range.selectNodeContents(ce.textarea); + range.collapse(false); + var sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + } + } +}; + +/** + * Returns true if the given event should start editing. This implementation returns true. + */ +EditorUi.prototype.isImmediateEditingEvent = function(evt) +{ + return true; +}; + +/** + * Private helper method. + */ +EditorUi.prototype.getCssClassForMarker = function(prefix, shape, marker, fill) +{ + var result = ''; + + if (shape == 'flexArrow') + { + result = (marker != null && marker != mxConstants.NONE) ? + 'geSprite geSprite-' + prefix + 'blocktrans' : 'geSprite geSprite-noarrow'; + } + else + { + if (marker == mxConstants.ARROW_CLASSIC) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classic' : 'geSprite geSprite-' + prefix + 'classictrans'; + } + else if (marker == mxConstants.ARROW_CLASSIC_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classicthin' : 'geSprite geSprite-' + prefix + 'classicthintrans'; + } + else if (marker == mxConstants.ARROW_OPEN) + { + result = 'geSprite geSprite-' + prefix + 'open'; + } + else if (marker == mxConstants.ARROW_OPEN_THIN) + { + result = 'geSprite geSprite-' + prefix + 'openthin'; + } + else if (marker == mxConstants.ARROW_BLOCK) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'block' : 'geSprite geSprite-' + prefix + 'blocktrans'; + } + else if (marker == mxConstants.ARROW_BLOCK_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'blockthin' : 'geSprite geSprite-' + prefix + 'blockthintrans'; + } + else if (marker == mxConstants.ARROW_OVAL) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'oval' : 'geSprite geSprite-' + prefix + 'ovaltrans'; + } + else if (marker == mxConstants.ARROW_DIAMOND) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'diamond' : 'geSprite geSprite-' + prefix + 'diamondtrans'; + } + else if (marker == mxConstants.ARROW_DIAMOND_THIN) + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'thindiamond' : 'geSprite geSprite-' + prefix + 'thindiamondtrans'; + } + else if (marker == 'openAsync') + { + result = 'geSprite geSprite-' + prefix + 'openasync'; + } + else if (marker == 'dash') + { + result = 'geSprite geSprite-' + prefix + 'dash'; + } + else if (marker == 'cross') + { + result = 'geSprite geSprite-' + prefix + 'cross'; + } + else if (marker == 'async') + { + result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'async' : 'geSprite geSprite-' + prefix + 'asynctrans'; + } + else if (marker == 'circle' || marker == 'circlePlus') + { + result = (fill == '1' || marker == 'circle') ? 'geSprite geSprite-' + prefix + 'circle' : 'geSprite geSprite-' + prefix + 'circleplus'; + } + else if (marker == 'ERone') + { + result = 'geSprite geSprite-' + prefix + 'erone'; + } + else if (marker == 'ERmandOne') + { + result = 'geSprite geSprite-' + prefix + 'eronetoone'; + } + else if (marker == 'ERmany') + { + result = 'geSprite geSprite-' + prefix + 'ermany'; + } + else if (marker == 'ERoneToMany') + { + result = 'geSprite geSprite-' + prefix + 'eronetomany'; + } + else if (marker == 'ERzeroToOne') + { + result = 'geSprite geSprite-' + prefix + 'eroneopt'; + } + else if (marker == 'ERzeroToMany') + { + result = 'geSprite geSprite-' + prefix + 'ermanyopt'; + } + else + { + result = 'geSprite geSprite-noarrow'; + } + } + + return result; +}; + +/** + * Overridden in Menus.js + */ +EditorUi.prototype.createMenus = function() +{ + return null; +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.updatePasteActionStates = function() +{ + var graph = this.editor.graph; + var paste = this.actions.get('paste'); + var pasteHere = this.actions.get('pasteHere'); + + paste.setEnabled(this.editor.graph.cellEditor.isContentEditing() || (!mxClipboard.isEmpty() && + graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))); + pasteHere.setEnabled(paste.isEnabled()); +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.initClipboard = function() +{ + var ui = this; + + var mxClipboardCut = mxClipboard.cut; + mxClipboard.cut = function(graph) + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('cut', false, null); + } + else + { + mxClipboardCut.apply(this, arguments); + } + + ui.updatePasteActionStates(); + }; + + var mxClipboardCopy = mxClipboard.copy; + mxClipboard.copy = function(graph) + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('copy', false, null); + } + else + { + mxClipboardCopy.apply(this, arguments); + } + + ui.updatePasteActionStates(); + }; + + var mxClipboardPaste = mxClipboard.paste; + mxClipboard.paste = function(graph) + { + var result = null; + + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('paste', false, null); + } + else + { + result = mxClipboardPaste.apply(this, arguments); + } + + ui.updatePasteActionStates(); + + return result; + }; + + // Overrides cell editor to update paste action state + var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; + + this.editor.graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + ui.updatePasteActionStates(); + }; + + var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; + + this.editor.graph.cellEditor.stopEditing = function(cell, trigger) + { + cellEditorStopEditing.apply(this, arguments); + ui.updatePasteActionStates(); + }; + + this.updatePasteActionStates(); +}; + +/** + * Initializes the infinite canvas. + */ +EditorUi.prototype.lazyZoomDelay = 20; + +/** + * Initializes the infinite canvas. + */ +EditorUi.prototype.initCanvas = function() +{ + // Initial page layout view, scrollBuffer and timer-based scrolling + var graph = this.editor.graph; + graph.timerAutoScroll = true; + + /** + * Returns the padding for pages in page view with scrollbars. + */ + graph.getPagePadding = function() + { + return new mxPoint(Math.max(0, Math.round((graph.container.offsetWidth - 34) / graph.view.scale)), + Math.max(0, Math.round((graph.container.offsetHeight - 34) / graph.view.scale))); + }; + + // Fits the number of background pages to the graph + graph.view.getBackgroundPageBounds = function() + { + var layout = this.graph.getPageLayout(); + var page = this.graph.getPageSize(); + + return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width), + this.scale * (this.translate.y + layout.y * page.height), + this.scale * layout.width * page.width, + this.scale * layout.height * page.height); + }; + + graph.getPreferredPageSize = function(bounds, width, height) + { + var pages = this.getPageLayout(); + var size = this.getPageSize(); + + return new mxRectangle(0, 0, pages.width * size.width, pages.height * size.height); + }; + + // Scales pages/graph to fit available size + var resize = null; + var ui = this; + + if (this.editor.isChromelessView()) + { + resize = mxUtils.bind(this, function(autoscale, maxScale, cx, cy) + { + if (graph.container != null) + { + cx = (cx != null) ? cx : 0; + cy = (cy != null) ? cy : 0; + + var bds = (graph.pageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds(); + var scroll = mxUtils.hasScrollbars(graph.container); + var tr = graph.view.translate; + var s = graph.view.scale; + + // Normalizes the bounds + var b = mxRectangle.fromRectangle(bds); + b.x = b.x / s - tr.x; + b.y = b.y / s - tr.y; + b.width /= s; + b.height /= s; + + var st = graph.container.scrollTop; + var sl = graph.container.scrollLeft; + var sb = (mxClient.IS_QUIRKS || document.documentMode >= 8) ? 20 : 14; + + if (document.documentMode == 8 || document.documentMode == 9) + { + sb += 3; + } + + var cw = graph.container.offsetWidth - sb; + var ch = graph.container.offsetHeight - sb; + + var ns = (autoscale) ? Math.max(0.3, Math.min(maxScale || 1, cw / b.width)) : s; + var dx = ((cw - ns * b.width) / 2) / ns; + var dy = (this.lightboxVerticalDivider == 0) ? 0 : ((ch - ns * b.height) / this.lightboxVerticalDivider) / ns; + + if (scroll) + { + dx = Math.max(dx, 0); + dy = Math.max(dy, 0); + } + + if (scroll || bds.width < cw || bds.height < ch) + { + graph.view.scaleAndTranslate(ns, Math.floor(dx - b.x), Math.floor(dy - b.y)); + graph.container.scrollTop = st * ns / s; + graph.container.scrollLeft = sl * ns / s; + } + else if (cx != 0 || cy != 0) + { + var t = graph.view.translate; + graph.view.setTranslate(Math.floor(t.x + cx / s), Math.floor(t.y + cy / s)); + } + } + }); + + // Hack to make function available to subclassers + this.chromelessResize = resize; + + // Hook for subclassers for override + this.chromelessWindowResize = mxUtils.bind(this, function() + { + this.chromelessResize(false); + }); + + // Removable resize listener + var autoscaleResize = mxUtils.bind(this, function() + { + this.chromelessWindowResize(false); + }); + + mxEvent.addListener(window, 'resize', autoscaleResize); + + this.destroyFunctions.push(function() + { + mxEvent.removeListener(window, 'resize', autoscaleResize); + }); + + this.editor.addListener('resetGraphView', mxUtils.bind(this, function() + { + this.chromelessResize(true); + })); + + this.actions.get('zoomIn').funct = mxUtils.bind(this, function(evt) + { + graph.zoomIn(); + this.chromelessResize(false); + }); + this.actions.get('zoomOut').funct = mxUtils.bind(this, function(evt) + { + graph.zoomOut(); + this.chromelessResize(false); + }); + + // Creates toolbar for viewer - do not use CSS here + // as this may be used in a viewer that has no CSS + if (urlParams['toolbar'] != '0') + { + this.chromelessToolbar = document.createElement('div'); + this.chromelessToolbar.style.position = 'fixed'; + this.chromelessToolbar.style.overflow = 'hidden'; + this.chromelessToolbar.style.boxSizing = 'border-box'; + this.chromelessToolbar.style.whiteSpace = 'nowrap'; + this.chromelessToolbar.style.backgroundColor = '#000000'; + this.chromelessToolbar.style.padding = '10px 10px 8px 10px'; + this.chromelessToolbar.style.left = '50%'; + + if (!mxClient.IS_VML) + { + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'borderRadius', '20px'); + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transition', 'opacity 600ms ease-in-out'); + } + + var updateChromelessToolbarPosition = mxUtils.bind(this, function() + { + var css = mxUtils.getCurrentStyle(graph.container); + this.chromelessToolbar.style.bottom = ((css != null) ? parseInt(css['margin-bottom'] || 0) : 0) + + ((this.tabContainer != null) ? (20 + parseInt(this.tabContainer.style.height)) : 20) + 'px'; + }); + + this.editor.addListener('resetGraphView', updateChromelessToolbarPosition); + updateChromelessToolbarPosition(); + + var btnCount = 0; + + var addButton = mxUtils.bind(this, function(fn, imgSrc, tip) + { + btnCount++; + + var a = document.createElement('span'); + a.style.paddingLeft = '8px'; + a.style.paddingRight = '8px'; + a.style.cursor = 'pointer'; + mxEvent.addListener(a, 'click', fn); + + if (tip != null) + { + a.setAttribute('title', tip); + } + + var img = document.createElement('img'); + img.setAttribute('border', '0'); + img.setAttribute('src', imgSrc); + + a.appendChild(img); + this.chromelessToolbar.appendChild(a); + + return a; + }); + + var prevButton = addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('previousPage').funct(); + mxEvent.consume(evt); + }), Editor.previousLargeImage, mxResources.get('previousPage')); + + var pageInfo = document.createElement('div'); + pageInfo.style.display = 'inline-block'; + pageInfo.style.verticalAlign = 'top'; + pageInfo.style.fontFamily = 'Helvetica,Arial'; + pageInfo.style.marginTop = '8px'; + pageInfo.style.color = '#ffffff'; + this.chromelessToolbar.appendChild(pageInfo); + + var nextButton = addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('nextPage').funct(); + mxEvent.consume(evt); + }), Editor.nextLargeImage, mxResources.get('nextPage')); + + var updatePageInfo = mxUtils.bind(this, function() + { + if (this.pages != null && this.pages.length > 1 && this.currentPage != null) + { + pageInfo.innerHTML = ''; + mxUtils.write(pageInfo, (mxUtils.indexOf(this.pages, this.currentPage) + 1) + ' / ' + this.pages.length); + } + }); + + prevButton.style.paddingLeft = '0px'; + prevButton.style.paddingRight = '4px'; + nextButton.style.paddingLeft = '4px'; + nextButton.style.paddingRight = '0px'; + + var updatePageButtons = mxUtils.bind(this, function() + { + if (this.pages != null && this.pages.length > 1 && this.currentPage != null) + { + nextButton.style.display = ''; + prevButton.style.display = ''; + pageInfo.style.display = 'inline-block'; + } + else + { + nextButton.style.display = 'none'; + prevButton.style.display = 'none'; + pageInfo.style.display = 'none'; + } + + updatePageInfo(); + }); + + this.editor.addListener('resetGraphView', updatePageButtons); + this.editor.addListener('pageSelected', updatePageInfo); + + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('zoomOut').funct(); + mxEvent.consume(evt); + }), Editor.zoomOutLargeImage, mxResources.get('zoomOut') + ' (Alt+Mousewheel)'); + + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('zoomIn').funct(); + mxEvent.consume(evt); + }), Editor.zoomInLargeImage, mxResources.get('zoomIn') + ' (Alt+Mousewheel)'); + + addButton(mxUtils.bind(this, function(evt) + { + if (graph.isLightboxView()) + { + if (graph.view.scale == 1) + { + this.lightboxFit(); + } + else + { + graph.zoomTo(1); + } + + this.chromelessResize(false); + } + else + { + this.chromelessResize(true); + } + + mxEvent.consume(evt); + }), Editor.actualSizeLargeImage, mxResources.get('fit')); + + // Changes toolbar opacity on hover + var fadeThread = null; + var fadeThread2 = null; + + var fadeOut = mxUtils.bind(this, function(delay) + { + if (fadeThread != null) + { + window.clearTimeout(fadeThread); + fadeThead = null; + } + + if (fadeThread2 != null) + { + window.clearTimeout(fadeThread2); + fadeThead2 = null; + } + + fadeThread = window.setTimeout(mxUtils.bind(this, function() + { + mxUtils.setOpacity(this.chromelessToolbar, 0); + fadeThread = null; + + fadeThread2 = window.setTimeout(mxUtils.bind(this, function() + { + this.chromelessToolbar.style.display = 'none'; + fadeThread2 = null; + }), 600); + }), delay || 200); + }); + + var fadeIn = mxUtils.bind(this, function(opacity) + { + if (fadeThread != null) + { + window.clearTimeout(fadeThread); + fadeThead = null; + } + + if (fadeThread2 != null) + { + window.clearTimeout(fadeThread2); + fadeThead2 = null; + } + + this.chromelessToolbar.style.display = ''; + mxUtils.setOpacity(this.chromelessToolbar, opacity || 30); + }); + + if (urlParams['layers'] == '1') + { + this.layersDialog = null; + + var layersButton = addButton(mxUtils.bind(this, function(evt) + { + if (this.layersDialog != null) + { + this.layersDialog.parentNode.removeChild(this.layersDialog); + this.layersDialog = null; + } + else + { + this.layersDialog = graph.createLayersDialog(); + + mxEvent.addListener(this.layersDialog, 'mouseleave', mxUtils.bind(this, function() + { + this.layersDialog.parentNode.removeChild(this.layersDialog); + this.layersDialog = null; + })); + + var r = layersButton.getBoundingClientRect(); + + mxUtils.setPrefixedStyle(this.layersDialog.style, 'borderRadius', '5px'); + this.layersDialog.style.position = 'fixed'; + this.layersDialog.style.fontFamily = 'Helvetica,Arial'; + this.layersDialog.style.backgroundColor = '#000000'; + this.layersDialog.style.width = '160px'; + this.layersDialog.style.padding = '4px 2px 4px 2px'; + this.layersDialog.style.color = '#ffffff'; + mxUtils.setOpacity(this.layersDialog, 70); + this.layersDialog.style.left = r.left + 'px'; + this.layersDialog.style.bottom = parseInt(this.chromelessToolbar.style.bottom) + + this.chromelessToolbar.offsetHeight + 4 + 'px'; + + // Puts the dialog on top of the container z-index + var style = mxUtils.getCurrentStyle(this.editor.graph.container); + this.layersDialog.style.zIndex = style.zIndex; + + document.body.appendChild(this.layersDialog); + } + + mxEvent.consume(evt); + }), Editor.layersLargeImage, mxResources.get('layers')); + + // Shows/hides layers button depending on content + var model = graph.getModel(); + + model.addListener(mxEvent.CHANGE, function() + { + layersButton.style.display = (model.getChildCount(model.root) > 1) ? '' : 'none'; + }); + } + + this.addChromelessToolbarItems(addButton); + + if (this.editor.editButtonLink != null || this.editor.editButtonFunc != null) + { + addButton(mxUtils.bind(this, function(evt) + { + if (this.editor.editButtonFunc != null) + { + this.editor.editButtonFunc(); + } + else if (this.editor.editButtonLink == '_blank') + { + this.editor.editAsNew(this.getEditBlankXml()); + } + else + { + graph.openLink(this.editor.editButtonLink, 'editWindow'); + } + + mxEvent.consume(evt); + }), Editor.editLargeImage, mxResources.get('edit')); + } + + if (graph.lightbox && (urlParams['close'] == '1' || this.container != document.body)) + { + addButton(mxUtils.bind(this, function(evt) + { + if (urlParams['close'] == '1') + { + window.close(); + } + else + { + this.destroy(); + mxEvent.consume(evt); + } + }), Editor.closeLargeImage, mxResources.get('close') + ' (Escape)'); + } + + // Initial state invisible + this.chromelessToolbar.style.display = 'none'; + mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transform', 'translate(-50%,0)'); + graph.container.appendChild(this.chromelessToolbar); + + mxEvent.addListener(graph.container, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isTouchEvent(evt)) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(30); + } + + fadeOut(); + } + })); + + mxEvent.addListener(this.chromelessToolbar, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', function(evt) + { + mxEvent.consume(evt); + }); + + mxEvent.addListener(this.chromelessToolbar, 'mouseenter', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(100); + } + else + { + fadeOut(); + } + })); + + mxEvent.addListener(this.chromelessToolbar, 'mousemove', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isShiftDown(evt)) + { + fadeIn(100); + } + else + { + fadeOut(); + } + + mxEvent.consume(evt); + })); + + mxEvent.addListener(this.chromelessToolbar, 'mouseleave', mxUtils.bind(this, function(evt) + { + if (!mxEvent.isTouchEvent(evt)) + { + fadeIn(30); + } + })); + + // Shows/hides toolbar for touch devices + var tol = graph.getTolerance(); + + graph.addMouseListener( + { + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0, + mouseDown: function(sender, me) + { + this.startX = me.getGraphX(); + this.startY = me.getGraphY(); + this.scrollLeft = graph.container.scrollLeft; + this.scrollTop = graph.container.scrollTop; + }, + mouseMove: function(sender, me) {}, + mouseUp: function(sender, me) + { + if (mxEvent.isTouchEvent(me.getEvent())) + { + if ((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (Math.abs(this.startX - me.getGraphX()) < tol && + Math.abs(this.startY - me.getGraphY()) < tol)) + { + if (parseFloat(ui.chromelessToolbar.style.opacity || 0) > 0) + { + fadeOut(); + } + else + { + fadeIn(30); + } + } + } + } + }); + } // end if toolbar + + // Installs handling of highlight and handling links to relative links and anchors + if (!this.editor.editable) + { + this.addChromelessClickHandler(); + } + } + else if (this.editor.extendCanvas) + { + /** + * Guesses autoTranslate to avoid another repaint (see below). + * Works if only the scale of the graph changes or if pages + * are visible and the visible pages do not change. + */ + var graphViewValidate = graph.view.validate; + graph.view.validate = function() + { + if (this.graph.container != null && mxUtils.hasScrollbars(this.graph.container)) + { + var pad = this.graph.getPagePadding(); + var size = this.graph.getPageSize(); + + // Updating scrollbars here causes flickering in quirks and is not needed + // if zoom method is always used to set the current scale on the graph. + var tx = this.translate.x; + var ty = this.translate.y; + this.translate.x = pad.x - (this.x0 || 0) * size.width; + this.translate.y = pad.y - (this.y0 || 0) * size.height; + } + + graphViewValidate.apply(this, arguments); + }; + + var graphSizeDidChange = graph.sizeDidChange; + graph.sizeDidChange = function() + { + if (this.container != null && mxUtils.hasScrollbars(this.container)) + { + var pages = this.getPageLayout(); + var pad = this.getPagePadding(); + var size = this.getPageSize(); + + // Updates the minimum graph size + var minw = Math.ceil(2 * pad.x + pages.width * size.width); + var minh = Math.ceil(2 * pad.y + pages.height * size.height); + + var min = graph.minimumGraphSize; + + // LATER: Fix flicker of scrollbar size in IE quirks mode + // after delayed call in window.resize event handler + if (min == null || min.width != minw || min.height != minh) + { + graph.minimumGraphSize = new mxRectangle(0, 0, minw, minh); + } + + // Updates auto-translate to include padding and graph size + var dx = pad.x - pages.x * size.width; + var dy = pad.y - pages.y * size.height; + + if (!this.autoTranslate && (this.view.translate.x != dx || this.view.translate.y != dy)) + { + this.autoTranslate = true; + this.view.x0 = pages.x; + this.view.y0 = pages.y; + + // NOTE: THIS INVOKES THIS METHOD AGAIN. UNFORTUNATELY THERE IS NO WAY AROUND THIS SINCE THE + // BOUNDS ARE KNOWN AFTER THE VALIDATION AND SETTING THE TRANSLATE TRIGGERS A REVALIDATION. + // SHOULD MOVE TRANSLATE/SCALE TO VIEW. + var tx = graph.view.translate.x; + var ty = graph.view.translate.y; + graph.view.setTranslate(dx, dy); + + // LATER: Fix rounding errors for small zoom + graph.container.scrollLeft += Math.round((dx - tx) * graph.view.scale); + graph.container.scrollTop += Math.round((dy - ty) * graph.view.scale); + + this.autoTranslate = false; + + return; + } + + graphSizeDidChange.apply(this, arguments); + } + }; + } + + // Accumulates the zoom factor while the rendering is taking place + // so that not the complete sequence of zoom steps must be painted + graph.updateZoomTimeout = null; + graph.cumulativeZoomFactor = 1; + + var cursorPosition = null; + + graph.lazyZoom = function(zoomIn) + { + if (this.updateZoomTimeout != null) + { + window.clearTimeout(this.updateZoomTimeout); + } + + // Switches to 1% zoom steps below 15% + // Lower bound depdends on rounding below + if (zoomIn) + { + if (this.view.scale * this.cumulativeZoomFactor < 0.15) + { + this.cumulativeZoomFactor = (this.view.scale + 0.01) / this.view.scale; + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.cumulativeZoomFactor *= this.zoomFactor; + this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; + } + } + else + { + if (this.view.scale * this.cumulativeZoomFactor <= 0.15) + { + this.cumulativeZoomFactor = (this.view.scale - 0.01) / this.view.scale; + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.cumulativeZoomFactor /= this.zoomFactor; + this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; + } + } + + this.cumulativeZoomFactor = Math.max(0.01, Math.min(this.view.scale * this.cumulativeZoomFactor, 160) / this.view.scale); + + this.updateZoomTimeout = window.setTimeout(mxUtils.bind(this, function() + { + var offset = mxUtils.getOffset(graph.container); + var dx = 0; + var dy = 0; + + if (cursorPosition != null) + { + dx = graph.container.offsetWidth / 2 - cursorPosition.x + offset.x; + dy = graph.container.offsetHeight / 2 - cursorPosition.y + offset.y; + } + + var prev = this.view.scale; + this.zoom(this.cumulativeZoomFactor); + var s = this.view.scale; + + if (s != prev) + { + if (resize != null) + { + ui.chromelessResize(false, null, dx * (this.cumulativeZoomFactor - 1), + dy * (this.cumulativeZoomFactor - 1)); + } + + if (mxUtils.hasScrollbars(graph.container) && (dx != 0 || dy != 0)) + { + graph.container.scrollLeft -= dx * (this.cumulativeZoomFactor - 1); + graph.container.scrollTop -= dy * (this.cumulativeZoomFactor - 1); + } + } + + this.cumulativeZoomFactor = 1; + this.updateZoomTimeout = null; + }), this.lazyZoomDelay); + }; + + mxEvent.addMouseWheelListener(mxUtils.bind(this, function(evt, up) + { + // Ctrl+wheel (or pinch on touchpad) is a native browser zoom event is OS X + // LATER: Add support for zoom via pinch on trackpad for Chrome in OS X + if ((this.dialogs == null || this.dialogs.length == 0) && graph.isZoomWheelEvent(evt)) + { + var source = mxEvent.getSource(evt); + + while (source != null) + { + if (source == graph.container) + { + cursorPosition = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + graph.lazyZoom(up); + mxEvent.consume(evt); + + return; + } + + source = source.parentNode; + } + } + })); +}; + +/** + * Creates a temporary graph instance for rendering off-screen content. + */ +EditorUi.prototype.addChromelessToolbarItems = function(addButton) +{ + addButton(mxUtils.bind(this, function(evt) + { + this.actions.get('print').funct(); + mxEvent.consume(evt); + }), Editor.printLargeImage, mxResources.get('print')); +}; + +/** + * Creates a temporary graph instance for rendering off-screen content. + */ +EditorUi.prototype.createTemporaryGraph = function(stylesheet) +{ + var graph = new Graph(document.createElement('div'), null, null, stylesheet); + graph.resetViewOnRootChange = false; + graph.setConnectable(false); + graph.gridEnabled = false; + graph.autoScroll = false; + graph.setTooltips(false); + graph.setEnabled(false); + + // Container must be in the DOM for correct HTML rendering + graph.container.style.visibility = 'hidden'; + graph.container.style.position = 'absolute'; + graph.container.style.overflow = 'hidden'; + graph.container.style.height = '1px'; + graph.container.style.width = '1px'; + + return graph; +}; + +/** + * + */ +EditorUi.prototype.addChromelessClickHandler = function() +{ + var hl = urlParams['highlight']; + + // Adds leading # for highlight color code + if (hl != null && hl.length > 0) + { + hl = '#' + hl; + } + + this.editor.graph.addClickHandler(hl); +}; + +/** + * + */ +EditorUi.prototype.toggleFormatPanel = function(forceHide) +{ + this.formatWidth = (forceHide || this.formatWidth > 0) ? 0 : 240; + this.formatContainer.style.display = (forceHide || this.formatWidth > 0) ? '' : 'none'; + this.refresh(); + this.format.refresh(); + this.fireEvent(new mxEventObject('formatWidthChanged')); +}; + +/** + * Adds support for placeholders in labels. + */ +EditorUi.prototype.lightboxFit = function(maxHeight) +{ + if (this.isDiagramEmpty()) + { + this.editor.graph.view.setScale(1); + } + else + { + var p = urlParams['border']; + var border = 60; + + if (p != null) + { + border = parseInt(p); + } + + // LATER: Use initial graph bounds to avoid rounding errors + this.editor.graph.maxFitScale = this.lightboxMaxFitScale; + this.editor.graph.fit(border, null, null, null, null, null, maxHeight); + this.editor.graph.maxFitScale = null; + } +}; + +/** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ +EditorUi.prototype.isDiagramEmpty = function() +{ + var model = this.editor.graph.getModel(); + + return model.getChildCount(model.root) == 1 && model.getChildCount(model.getChildAt(model.root, 0)) == 0; +}; + +/** + * Hook for allowing selection and context menu for certain events. + */ +EditorUi.prototype.isSelectionAllowed = function(evt) +{ + return mxEvent.getSource(evt).nodeName == 'SELECT' || (mxEvent.getSource(evt).nodeName == 'INPUT' && + mxUtils.isAncestorNode(this.formatContainer, mxEvent.getSource(evt))); +}; + +/** + * Installs dialog if browser window is closed without saving + * This must be disabled during save and image export. + */ +EditorUi.prototype.addBeforeUnloadListener = function() +{ + // Installs dialog if browser window is closed without saving + // This must be disabled during save and image export + window.onbeforeunload = mxUtils.bind(this, function() + { + if (!this.editor.isChromelessView()) + { + return this.onBeforeUnload(); + } + }); +}; + +/** + * Sets the onbeforeunload for the application + */ +EditorUi.prototype.onBeforeUnload = function() +{ + if (this.editor.modified) + { + return mxResources.get('allChangesLost'); + } +}; + +/** + * Opens the current diagram via the window.opener if one exists. + */ +EditorUi.prototype.open = function() +{ + // Cross-domain window access is not allowed in FF, so if we + // were opened from another domain then this will fail. + try + { + if (window.opener != null && window.opener.openFile != null) + { + window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) + { + try + { + var doc = mxUtils.parseXml(xml); + this.editor.setGraphXml(doc.documentElement); + this.editor.setModified(false); + this.editor.undoManager.clear(); + + if (filename != null) + { + this.editor.setFilename(filename); + this.updateDocumentTitle(); + } + + return; + } + catch (e) + { + mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); + } + })); + } + } + catch(e) + { + // ignore + } + + // Fires as the last step if no file was loaded + this.editor.graph.view.validate(); + + // Required only in special cases where an initial file is opened + // and the minimumGraphSize changes and CSS must be updated. + this.editor.graph.sizeDidChange(); + this.editor.fireEvent(new mxEventObject('resetGraphView')); +}; + +/** + * Sets the current menu and element. + */ +EditorUi.prototype.setCurrentMenu = function(menu, elt) +{ + this.currentMenuElt = elt; + this.currentMenu = menu; +}; + +/** + * Resets the current menu and element. + */ +EditorUi.prototype.resetCurrentMenu = function() +{ + this.currentMenuElt = null; + this.currentMenu = null; +}; + +/** + * Hides and destroys the current menu. + */ +EditorUi.prototype.hideCurrentMenu = function() +{ + if (this.currentMenu != null) + { + this.currentMenu.hideMenu(); + this.resetCurrentMenu(); + } +}; + +/** + * Updates the document title. + */ +EditorUi.prototype.updateDocumentTitle = function() +{ + var title = this.editor.getOrCreateFilename(); + + if (this.editor.appName != null) + { + title += ' - ' + this.editor.appName; + } + + document.title = title; +}; + +/** + * Updates the document title. + */ +EditorUi.prototype.createHoverIcons = function() +{ + return new HoverIcons(this.editor.graph); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.redo = function() +{ + try + { + var graph = this.editor.graph; + + if (graph.isEditing()) + { + document.execCommand('redo', false, null); + } + else + { + this.editor.undoManager.redo(); + } + } + catch (e) + { + // ignore all errors + } +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.undo = function() +{ + try + { + var graph = this.editor.graph; + + if (graph.isEditing()) + { + // Stops editing and executes undo on graph if native undo + // does not affect current editing value + var value = graph.cellEditor.textarea.innerHTML; + document.execCommand('undo', false, null); + + if (value == graph.cellEditor.textarea.innerHTML) + { + graph.stopEditing(true); + this.editor.undoManager.undo(); + } + } + else + { + this.editor.undoManager.undo(); + } + } + catch (e) + { + // ignore all errors + } +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.canRedo = function() +{ + return this.editor.graph.isEditing() || this.editor.undoManager.canRedo(); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.canUndo = function() +{ + return this.editor.graph.isEditing() || this.editor.undoManager.canUndo(); +}; + +/** + * + */ +EditorUi.prototype.getEditBlankXml = function() +{ + return mxUtils.getXml(this.editor.getGraphXml()); +}; + +/** + * Returns the URL for a copy of this editor with no state. + */ +EditorUi.prototype.getUrl = function(pathname) +{ + var href = (pathname != null) ? pathname : window.location.pathname; + var parms = (href.indexOf('?') > 0) ? 1 : 0; + + // Removes template URL parameter for new blank diagram + for (var key in urlParams) + { + if (parms == 0) + { + href += '?'; + } + else + { + href += '&'; + } + + href += key + '=' + urlParams[key]; + parms++; + } + + return href; +}; + +/** + * Specifies if the graph has scrollbars. + */ +EditorUi.prototype.setScrollbars = function(value) +{ + var graph = this.editor.graph; + var prev = graph.container.style.overflow; + graph.scrollbars = value; + this.editor.updateGraphComponents(); + + if (prev != graph.container.style.overflow) + { + if (graph.container.style.overflow == 'hidden') + { + var t = graph.view.translate; + graph.view.setTranslate(t.x - graph.container.scrollLeft / graph.view.scale, t.y - graph.container.scrollTop / graph.view.scale); + graph.container.scrollLeft = 0; + graph.container.scrollTop = 0; + graph.minimumGraphSize = null; + graph.sizeDidChange(); + } + else + { + var dx = graph.view.translate.x; + var dy = graph.view.translate.y; + + graph.view.translate.x = 0; + graph.view.translate.y = 0; + graph.sizeDidChange(); + graph.container.scrollLeft -= Math.round(dx * graph.view.scale); + graph.container.scrollTop -= Math.round(dy * graph.view.scale); + } + } + + this.fireEvent(new mxEventObject('scrollbarsChanged')); +}; + +/** + * Returns true if the graph has scrollbars. + */ +EditorUi.prototype.hasScrollbars = function() +{ + return this.editor.graph.scrollbars; +}; + +/** + * Resets the state of the scrollbars. + */ +EditorUi.prototype.resetScrollbars = function() +{ + var graph = this.editor.graph; + + if (!this.editor.extendCanvas) + { + graph.container.scrollTop = 0; + graph.container.scrollLeft = 0; + + if (!mxUtils.hasScrollbars(graph.container)) + { + graph.view.setTranslate(0, 0); + } + } + else if (!this.editor.isChromelessView()) + { + if (mxUtils.hasScrollbars(graph.container)) + { + if (graph.pageVisible) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = Math.floor(pad.y - this.editor.initialTopSpacing) - 1; + graph.container.scrollLeft = Math.floor(Math.min(pad.x, + (graph.container.scrollWidth - graph.container.clientWidth) / 2)) - 1; + + // Scrolls graph to visible area + var bounds = graph.getGraphBounds(); + + if (bounds.width > 0 && bounds.height > 0) + { + if (bounds.x > graph.container.scrollLeft + graph.container.clientWidth * 0.9) + { + graph.container.scrollLeft = Math.min(bounds.x + bounds.width - graph.container.clientWidth, bounds.x - 10); + } + + if (bounds.y > graph.container.scrollTop + graph.container.clientHeight * 0.9) + { + graph.container.scrollTop = Math.min(bounds.y + bounds.height - graph.container.clientHeight, bounds.y - 10); + } + } + } + else + { + var bounds = graph.getGraphBounds(); + var width = Math.max(bounds.width, graph.scrollTileSize.width * graph.view.scale); + var height = Math.max(bounds.height, graph.scrollTileSize.height * graph.view.scale); + graph.container.scrollTop = Math.floor(Math.max(0, bounds.y - Math.max(20, (graph.container.clientHeight - height) / 4))); + graph.container.scrollLeft = Math.floor(Math.max(0, bounds.x - Math.max(0, (graph.container.clientWidth - width) / 2))); + } + } + else + { + // This code is not actively used since the default for scrollbars is always true + if (graph.pageVisible) + { + var b = graph.view.getBackgroundPageBounds(); + graph.view.setTranslate(Math.floor(Math.max(0, (graph.container.clientWidth - b.width) / 2) - b.x), + Math.floor(Math.max(0, (graph.container.clientHeight - b.height) / 2) - b.y)); + } + else + { + var bounds = graph.getGraphBounds(); + graph.view.setTranslate(Math.floor(Math.max(0, Math.max(0, (graph.container.clientWidth - bounds.width) / 2) - bounds.x)), + Math.floor(Math.max(0, Math.max(20, (graph.container.clientHeight - bounds.height) / 4)) - bounds.y)); + } + } + } +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageVisible = function(value) +{ + var graph = this.editor.graph; + var hasScrollbars = mxUtils.hasScrollbars(graph.container); + var tx = 0; + var ty = 0; + + if (hasScrollbars) + { + tx = graph.view.translate.x * graph.view.scale - graph.container.scrollLeft; + ty = graph.view.translate.y * graph.view.scale - graph.container.scrollTop; + } + + graph.pageVisible = value; + graph.pageBreaksVisible = value; + graph.preferPageSize = value; + graph.view.validateBackground(); + + // Workaround for possible handle offset + if (hasScrollbars) + { + var cells = graph.getSelectionCells(); + graph.clearSelection(); + graph.setSelectionCells(cells); + } + + // Calls updatePageBreaks + graph.sizeDidChange(); + + if (hasScrollbars) + { + graph.container.scrollLeft = graph.view.translate.x * graph.view.scale - tx; + graph.container.scrollTop = graph.view.translate.y * graph.view.scale - ty; + } + + this.fireEvent(new mxEventObject('pageViewChanged')); +}; + +/** + * Change types + */ +function ChangePageSetup(ui, color, image, format) +{ + this.ui = ui; + this.color = color; + this.previousColor = color; + this.image = image; + this.previousImage = image; + this.format = format; + this.previousFormat = format; + + // Needed since null are valid values for color and image + this.ignoreColor = false; + this.ignoreImage = false; +} + +/** + * Implementation of the undoable page rename. + */ +ChangePageSetup.prototype.execute = function() +{ + var graph = this.ui.editor.graph; + + if (!this.ignoreColor) + { + this.color = this.previousColor; + var tmp = graph.background; + this.ui.setBackgroundColor(this.previousColor); + this.previousColor = tmp; + } + + if (!this.ignoreImage) + { + this.image = this.previousImage; + var tmp = graph.backgroundImage; + this.ui.setBackgroundImage(this.previousImage); + this.previousImage = tmp; + } + + if (this.previousFormat != null) + { + this.format = this.previousFormat; + var tmp = graph.pageFormat; + + if (this.previousFormat.width != tmp.width || + this.previousFormat.height != tmp.height) + { + this.ui.setPageFormat(this.previousFormat); + this.previousFormat = tmp; + } + } + + if (this.foldingEnabled != null && this.foldingEnabled != this.ui.editor.graph.foldingEnabled) + { + this.ui.setFoldingEnabled(this.foldingEnabled); + this.foldingEnabled = !this.foldingEnabled; + } +}; + +// Registers codec for ChangePageSetup +(function() +{ + var codec = new mxObjectCodec(new ChangePageSetup(), ['ui', 'previousColor', 'previousImage', 'previousFormat']); + + codec.afterDecode = function(dec, node, obj) + { + obj.previousColor = obj.color; + obj.previousImage = obj.image; + obj.previousFormat = obj.format; + + if (obj.foldingEnabled != null) + { + obj.foldingEnabled = !obj.foldingEnabled; + } + + return obj; + }; + + mxCodecRegistry.register(codec); +})(); + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setBackgroundColor = function(value) +{ + this.editor.graph.background = value; + this.editor.graph.view.validateBackground(); + + this.fireEvent(new mxEventObject('backgroundColorChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setFoldingEnabled = function(value) +{ + this.editor.graph.foldingEnabled = value; + this.editor.graph.view.revalidate(); + + this.fireEvent(new mxEventObject('foldingEnabledChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageFormat = function(value) +{ + this.editor.graph.pageFormat = value; + + if (!this.editor.graph.pageVisible) + { + this.actions.get('pageView').funct(); + } + else + { + this.editor.graph.view.validateBackground(); + this.editor.graph.sizeDidChange(); + } + + this.fireEvent(new mxEventObject('pageFormatChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setPageScale = function(value) +{ + this.editor.graph.pageScale = value; + + if (!this.editor.graph.pageVisible) + { + this.actions.get('pageView').funct(); + } + else + { + this.editor.graph.view.validateBackground(); + this.editor.graph.sizeDidChange(); + } + + this.fireEvent(new mxEventObject('pageScaleChanged')); +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setGridColor = function(value) +{ + this.editor.graph.view.gridColor = value; + this.editor.graph.view.validateBackground(); + this.fireEvent(new mxEventObject('gridColorChanged')); +}; + +/** + * Updates the states of the given undo/redo items. + */ +EditorUi.prototype.addUndoListener = function() +{ + var undo = this.actions.get('undo'); + var redo = this.actions.get('redo'); + + var undoMgr = this.editor.undoManager; + + var undoListener = mxUtils.bind(this, function() + { + undo.setEnabled(this.canUndo()); + redo.setEnabled(this.canRedo()); + }); + + undoMgr.addListener(mxEvent.ADD, undoListener); + undoMgr.addListener(mxEvent.UNDO, undoListener); + undoMgr.addListener(mxEvent.REDO, undoListener); + undoMgr.addListener(mxEvent.CLEAR, undoListener); + + // Overrides cell editor to update action states + var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; + + this.editor.graph.cellEditor.startEditing = function() + { + cellEditorStartEditing.apply(this, arguments); + undoListener(); + }; + + var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; + + this.editor.graph.cellEditor.stopEditing = function(cell, trigger) + { + cellEditorStopEditing.apply(this, arguments); + undoListener(); + }; + + // Updates the button states once + undoListener(); +}; + +/** +* Updates the states of the given toolbar items based on the selection. +*/ +EditorUi.prototype.updateActionStates = function() +{ + var graph = this.editor.graph; + var selected = !graph.isSelectionEmpty(); + var vertexSelected = false; + var edgeSelected = false; + + var cells = graph.getSelectionCells(); + + if (cells != null) + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + edgeSelected = true; + } + + if (graph.getModel().isVertex(cell)) + { + vertexSelected = true; + } + + if (edgeSelected && vertexSelected) + { + break; + } + } + } + + // Updates action states + var actions = ['cut', 'copy', 'bold', 'italic', 'underline', 'delete', 'duplicate', + 'editStyle', 'editTooltip', 'editLink', 'backgroundColor', 'borderColor', + 'edit', 'toFront', 'toBack', 'lockUnlock', 'solid', 'dashed', 'pasteSize', + 'dotted', 'fillColor', 'gradientColor', 'shadow', 'fontColor', + 'formattedText', 'rounded', 'toggleRounded', 'sharp', 'strokeColor']; + + for (var i = 0; i < actions.length; i++) + { + this.actions.get(actions[i]).setEnabled(selected); + } + + this.actions.get('setAsDefaultStyle').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('clearWaypoints').setEnabled(!graph.isSelectionEmpty()); + this.actions.get('copySize').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('turn').setEnabled(!graph.isSelectionEmpty()); + this.actions.get('curved').setEnabled(edgeSelected); + this.actions.get('rotation').setEnabled(vertexSelected); + this.actions.get('wordWrap').setEnabled(vertexSelected); + this.actions.get('autosize').setEnabled(vertexSelected); + var oneVertexSelected = vertexSelected && graph.getSelectionCount() == 1; + this.actions.get('group').setEnabled(graph.getSelectionCount() > 1 || + (oneVertexSelected && !graph.isContainer(graph.getSelectionCell()))); + this.actions.get('ungroup').setEnabled(graph.getSelectionCount() == 1 && + (graph.getModel().getChildCount(graph.getSelectionCell()) > 0 || + (oneVertexSelected && graph.isContainer(graph.getSelectionCell())))); + this.actions.get('removeFromGroup').setEnabled(oneVertexSelected && + graph.getModel().isVertex(graph.getModel().getParent(graph.getSelectionCell()))); + + // Updates menu states + var state = graph.view.getState(graph.getSelectionCell()); + this.menus.get('navigation').setEnabled(selected || graph.view.currentRoot != null); + this.actions.get('collapsible').setEnabled(vertexSelected && + (graph.isContainer(graph.getSelectionCell()) || graph.model.getChildCount(graph.getSelectionCell()) > 0)); + this.actions.get('home').setEnabled(graph.view.currentRoot != null); + this.actions.get('exitGroup').setEnabled(graph.view.currentRoot != null); + this.actions.get('enterGroup').setEnabled(graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell())); + var foldable = graph.getSelectionCount() == 1 && graph.isCellFoldable(graph.getSelectionCell()); + this.actions.get('expand').setEnabled(foldable); + this.actions.get('collapse').setEnabled(foldable); + this.actions.get('editLink').setEnabled(graph.getSelectionCount() == 1); + this.actions.get('openLink').setEnabled(graph.getSelectionCount() == 1 && + graph.getLinkForCell(graph.getSelectionCell()) != null); + this.actions.get('guides').setEnabled(graph.isEnabled()); + this.actions.get('grid').setEnabled(!this.editor.chromeless || this.editor.editable); + + var unlocked = graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()); + this.menus.get('layout').setEnabled(unlocked); + this.menus.get('insert').setEnabled(unlocked); + this.menus.get('direction').setEnabled(unlocked && vertexSelected); + this.menus.get('align').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); + this.menus.get('distribute').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); + this.actions.get('selectVertices').setEnabled(unlocked); + this.actions.get('selectEdges').setEnabled(unlocked); + this.actions.get('selectAll').setEnabled(unlocked); + this.actions.get('selectNone').setEnabled(unlocked); + + this.updatePasteActionStates(); +}; + +/** + * Refreshes the viewport. + */ +EditorUi.prototype.refresh = function(sizeDidChange) +{ + sizeDidChange = (sizeDidChange != null) ? sizeDidChange : true; + + var quirks = mxClient.IS_IE && (document.documentMode == null || document.documentMode == 5); + var w = this.container.clientWidth; + var h = this.container.clientHeight; + + if (this.container == document.body) + { + w = document.body.clientWidth || document.documentElement.clientWidth; + h = (quirks) ? document.body.clientHeight || document.documentElement.clientHeight : document.documentElement.clientHeight; + } + + // Workaround for bug on iOS see + // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue + // FIXME: Fix if footer visible + var off = 0; + + if (mxClient.IS_IOS && !window.navigator.standalone) + { + if (window.innerHeight != document.documentElement.clientHeight) + { + off = document.documentElement.clientHeight - window.innerHeight; + window.scrollTo(0, 0); + } + } + + var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize - 20)); + + var tmp = 0; + + if (this.menubar != null) + { + this.menubarContainer.style.height = this.menubarHeight + 'px'; + tmp += this.menubarHeight; + } + + if (this.toolbar != null) + { + this.toolbarContainer.style.top = this.menubarHeight + 'px'; + this.toolbarContainer.style.height = this.toolbarHeight + 'px'; + tmp += this.toolbarHeight; + } + + if (tmp > 0 && !mxClient.IS_QUIRKS) + { + tmp += 1; + } + + var sidebarFooterHeight = 0; + + if (this.sidebarFooterContainer != null) + { + var bottom = this.footerHeight + off; + sidebarFooterHeight = Math.max(0, Math.min(h - tmp - bottom, this.sidebarFooterHeight)); + this.sidebarFooterContainer.style.width = effHsplitPosition + 'px'; + this.sidebarFooterContainer.style.height = sidebarFooterHeight + 'px'; + this.sidebarFooterContainer.style.bottom = bottom + 'px'; + } + + var fw = (this.format != null) ? this.formatWidth : 0; + this.sidebarContainer.style.top = tmp + 'px'; + this.sidebarContainer.style.width = effHsplitPosition + 'px'; + this.formatContainer.style.top = tmp + 'px'; + this.formatContainer.style.width = fw + 'px'; + this.formatContainer.style.display = (this.format != null) ? '' : 'none'; + + this.diagramContainer.style.left = (this.hsplit.parentNode != null) ? (effHsplitPosition + this.splitSize) + 'px' : '0px'; + this.diagramContainer.style.top = this.sidebarContainer.style.top; + this.footerContainer.style.height = this.footerHeight + 'px'; + this.hsplit.style.top = this.sidebarContainer.style.top; + this.hsplit.style.bottom = (this.footerHeight + off) + 'px'; + this.hsplit.style.left = effHsplitPosition + 'px'; + + if (this.tabContainer != null) + { + this.tabContainer.style.left = this.diagramContainer.style.left; + } + + if (quirks) + { + this.menubarContainer.style.width = w + 'px'; + this.toolbarContainer.style.width = this.menubarContainer.style.width; + var sidebarHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); + this.sidebarContainer.style.height = (sidebarHeight - sidebarFooterHeight) + 'px'; + this.formatContainer.style.height = sidebarHeight + 'px'; + this.diagramContainer.style.width = (this.hsplit.parentNode != null) ? Math.max(0, w - effHsplitPosition - this.splitSize - fw) + 'px' : w + 'px'; + this.footerContainer.style.width = this.menubarContainer.style.width; + var diagramHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); + + if (this.tabContainer != null) + { + this.tabContainer.style.width = this.diagramContainer.style.width; + this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; + diagramHeight -= this.tabContainer.clientHeight; + } + + this.diagramContainer.style.height = diagramHeight + 'px'; + this.hsplit.style.height = diagramHeight + 'px'; + } + else + { + if (this.footerHeight > 0) + { + this.footerContainer.style.bottom = off + 'px'; + } + + this.diagramContainer.style.right = fw + 'px'; + var th = 0; + + if (this.tabContainer != null) + { + this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; + this.tabContainer.style.right = this.diagramContainer.style.right; + th = this.tabContainer.clientHeight; + } + + this.sidebarContainer.style.bottom = (this.footerHeight + sidebarFooterHeight + off) + 'px'; + this.formatContainer.style.bottom = (this.footerHeight + off) + 'px'; + this.diagramContainer.style.bottom = (this.footerHeight + off + th) + 'px'; + } + + if (sizeDidChange) + { + this.editor.graph.sizeDidChange(); + } +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createTabContainer = function() +{ + return null; +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createDivs = function() +{ + this.menubarContainer = this.createDiv('geMenubarContainer'); + this.toolbarContainer = this.createDiv('geToolbarContainer'); + this.sidebarContainer = this.createDiv('geSidebarContainer'); + this.formatContainer = this.createDiv('geSidebarContainer geFormatContainer'); + this.diagramContainer = this.createDiv('geDiagramContainer'); + this.footerContainer = this.createDiv('geFooterContainer'); + this.hsplit = this.createDiv('geHsplit'); + this.hsplit.setAttribute('title', mxResources.get('collapseExpand')); + + // Sets static style for containers + this.menubarContainer.style.top = '0px'; + this.menubarContainer.style.left = '0px'; + this.menubarContainer.style.right = '0px'; + this.toolbarContainer.style.left = '0px'; + this.toolbarContainer.style.right = '0px'; + this.sidebarContainer.style.left = '0px'; + this.formatContainer.style.right = '0px'; + this.formatContainer.style.zIndex = '1'; + this.diagramContainer.style.right = ((this.format != null) ? this.formatWidth : 0) + 'px'; + this.footerContainer.style.left = '0px'; + this.footerContainer.style.right = '0px'; + this.footerContainer.style.bottom = '0px'; + this.footerContainer.style.zIndex = mxPopupMenu.prototype.zIndex - 2; + this.hsplit.style.width = this.splitSize + 'px'; + this.sidebarFooterContainer = this.createSidebarFooterContainer(); + + if (this.sidebarFooterContainer) + { + this.sidebarFooterContainer.style.left = '0px'; + } + + if (!this.editor.chromeless) + { + this.tabContainer = this.createTabContainer(); + } + else + { + this.diagramContainer.style.border = 'none'; + } +}; + +/** + * Hook for sidebar footer container. This implementation returns null. + */ +EditorUi.prototype.createSidebarFooterContainer = function() +{ + return null; +}; + +/** + * Creates the required containers. + */ +EditorUi.prototype.createUi = function() +{ + // Creates menubar + this.menubar = (this.editor.chromeless) ? null : this.menus.createMenubar(this.createDiv('geMenubar')); + + if (this.menubar != null) + { + this.menubarContainer.appendChild(this.menubar.container); + } + + // Adds status bar in menubar + if (this.menubar != null) + { + this.statusContainer = this.createStatusContainer(); + + // Connects the status bar to the editor status + this.editor.addListener('statusChanged', mxUtils.bind(this, function() + { + this.setStatusText(this.editor.getStatus()); + })); + + this.setStatusText(this.editor.getStatus()); + this.menubar.container.appendChild(this.statusContainer); + + // Inserts into DOM + this.container.appendChild(this.menubarContainer); + } + + // Creates the sidebar + this.sidebar = (this.editor.chromeless) ? null : this.createSidebar(this.sidebarContainer); + + if (this.sidebar != null) + { + this.container.appendChild(this.sidebarContainer); + } + + // Creates the format sidebar + this.format = (this.editor.chromeless || !this.formatEnabled) ? null : this.createFormat(this.formatContainer); + + if (this.format != null) + { + this.container.appendChild(this.formatContainer); + } + + // Creates the footer + var footer = (this.editor.chromeless) ? null : this.createFooter(); + + if (footer != null) + { + this.footerContainer.appendChild(footer); + this.container.appendChild(this.footerContainer); + } + + if (this.sidebar != null && this.sidebarFooterContainer) + { + this.container.appendChild(this.sidebarFooterContainer); + } + + this.container.appendChild(this.diagramContainer); + + if (this.container != null && this.tabContainer != null) + { + this.container.appendChild(this.tabContainer); + } + + // Creates toolbar + this.toolbar = (this.editor.chromeless) ? null : this.createToolbar(this.createDiv('geToolbar')); + + if (this.toolbar != null) + { + this.toolbarContainer.appendChild(this.toolbar.container); + this.container.appendChild(this.toolbarContainer); + } + + // HSplit + if (this.sidebar != null) + { + this.container.appendChild(this.hsplit); + + this.addSplitHandler(this.hsplit, true, 0, mxUtils.bind(this, function(value) + { + this.hsplitPosition = value; + this.refresh(); + })); + } +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.createStatusContainer = function() +{ + var container = document.createElement('a'); + container.className = 'geItem geStatus'; + + if (screen.width < 420) + { + container.style.maxWidth = Math.max(20, screen.width - 320) + 'px'; + container.style.overflow = 'hidden'; + } + + return container; +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.setStatusText = function(value) +{ + this.statusContainer.innerHTML = value; +}; + +/** + * Creates a new toolbar for the given container. + */ +EditorUi.prototype.createToolbar = function(container) +{ + return new Toolbar(this, container); +}; + +/** + * Creates a new sidebar for the given container. + */ +EditorUi.prototype.createSidebar = function(container) +{ + return new Sidebar(this, container); +}; + +/** + * Creates a new sidebar for the given container. + */ +EditorUi.prototype.createFormat = function(container) +{ + return new Format(this, container); +}; + +/** + * Creates and returns a new footer. + */ +EditorUi.prototype.createFooter = function() +{ + return this.createDiv('geFooter'); +}; + +/** + * Creates the actual toolbar for the toolbar container. + */ +EditorUi.prototype.createDiv = function(classname) +{ + var elt = document.createElement('div'); + elt.className = classname; + + return elt; +}; + +/** + * Updates the states of the given undo/redo items. + */ +EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange) +{ + var start = null; + var initial = null; + var ignoreClick = true; + var last = null; + + // Disables built-in pan and zoom in IE10 and later + if (mxClient.IS_POINTER) + { + elt.style.touchAction = 'none'; + } + + var getValue = mxUtils.bind(this, function() + { + var result = parseInt(((horizontal) ? elt.style.left : elt.style.bottom)); + + // Takes into account hidden footer + if (!horizontal) + { + result = result + dx - this.footerHeight; + } + + return result; + }); + + function moveHandler(evt) + { + if (start != null) + { + var pt = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + onChange(Math.max(0, initial + ((horizontal) ? (pt.x - start.x) : (start.y - pt.y)) - dx)); + mxEvent.consume(evt); + + if (initial != getValue()) + { + ignoreClick = true; + last = null; + } + } + }; + + function dropHandler(evt) + { + moveHandler(evt); + initial = null; + start = null; + }; + + mxEvent.addGestureListeners(elt, function(evt) + { + start = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + initial = getValue(); + ignoreClick = false; + mxEvent.consume(evt); + }); + + mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) + { + if (!ignoreClick && this.hsplitClickEnabled) + { + var next = (last != null) ? last - dx : 0; + last = getValue(); + onChange(next); + mxEvent.consume(evt); + } + })); + + mxEvent.addGestureListeners(document, null, moveHandler, dropHandler); + + this.destroyFunctions.push(function() + { + mxEvent.removeGestureListeners(document, null, moveHandler, dropHandler); + }); +}; + +/** + * Displays a print dialog. + */ +EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, noScroll) +{ + this.editor.graph.tooltipHandler.hideTooltip(); + + if (this.dialogs == null) + { + this.dialogs = []; + } + + this.dialog = new Dialog(this, elt, w, h, modal, closable, onClose, noScroll); + this.dialogs.push(this.dialog); +}; + +/** + * Displays a print dialog. + */ +EditorUi.prototype.hideDialog = function(cancel) +{ + if (this.dialogs != null && this.dialogs.length > 0) + { + var dlg = this.dialogs.pop(); + dlg.close(cancel); + + this.dialog = (this.dialogs.length > 0) ? this.dialogs[this.dialogs.length - 1] : null; + + if (this.dialog == null && this.editor.graph.container.style.visibility != 'hidden') + { + this.editor.graph.container.focus(); + } + + mxUtils.clearSelection(); + this.editor.fireEvent(new mxEventObject('hideDialog')); + } +}; + +/** + * Display a color dialog. + */ +EditorUi.prototype.pickColor = function(color, apply) +{ + var graph = this.editor.graph; + var selState = graph.cellEditor.saveSelection(); + + var dlg = new ColorDialog(this, color || 'none', function(color) + { + graph.cellEditor.restoreSelection(selState); + apply(color); + }, function() + { + graph.cellEditor.restoreSelection(selState); + }); + this.showDialog(dlg.container, 230, 430, true, false); + dlg.init(); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +EditorUi.prototype.openFile = function() +{ + // Closes dialog after open + window.openFile = new OpenFile(mxUtils.bind(this, function(cancel) + { + this.hideDialog(cancel); + })); + + // Removes openFile if dialog is closed + this.showDialog(new OpenDialog(this).container, (Editor.useLocalStorage) ? 640 : 320, + (Editor.useLocalStorage) ? 480 : 220, true, true, function() + { + window.openFile = null; + }); +}; + +/** + * Extracs the graph model from the given HTML data from a data transfer event. + */ +EditorUi.prototype.extractGraphModelFromHtml = function(data) +{ + var result = null; + + try + { + var idx = data.indexOf('<mxGraphModel '); + + if (idx >= 0) + { + var idx2 = data.lastIndexOf('</mxGraphModel>'); + + if (idx2 > idx) + { + result = data.substring(idx, idx2 + 21).replace(/>/g, '>'). + replace(/</g, '<').replace(/\\"/g, '"').replace(/\n/g, ''); + } + } + } + catch (e) + { + // ignore + } + + return result; +}; + +/** + * Opens the given files in the editor. + */ +EditorUi.prototype.extractGraphModelFromEvent = function(evt) +{ + var result = null; + var data = null; + + if (evt != null) + { + var provider = (evt.dataTransfer != null) ? evt.dataTransfer : evt.clipboardData; + + if (provider != null) + { + if (document.documentMode == 10 || document.documentMode == 11) + { + data = provider.getData('Text'); + } + else + { + data = (mxUtils.indexOf(provider.types, 'text/html') >= 0) ? provider.getData('text/html') : null; + + if (mxUtils.indexOf(provider.types, 'text/plain' && (data == null || data.length == 0))) + { + data = provider.getData('text/plain'); + } + } + + if (data != null) + { + data = this.editor.graph.zapGremlins(mxUtils.trim(data)); + + // Tries parsing as HTML document with embedded XML + var xml = this.extractGraphModelFromHtml(data); + + if (xml != null) + { + data = xml; + } + } + } + } + + if (data != null && this.isCompatibleString(data)) + { + result = data; + } + + return result; +}; + +/** + * Hook for subclassers to return true if event data is a supported format. + * This implementation always returns false. + */ +EditorUi.prototype.isCompatibleString = function(data) +{ + return false; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +EditorUi.prototype.saveFile = function(forceDialog) +{ + if (!forceDialog && this.editor.filename != null) + { + this.save(this.editor.getOrCreateFilename()); + } + else + { + var dlg = new FilenameDialog(this, this.editor.getOrCreateFilename(), mxResources.get('save'), mxUtils.bind(this, function(name) + { + this.save(name); + }), null, mxUtils.bind(this, function(name) + { + if (name != null && name.length > 0) + { + return true; + } + + mxUtils.confirm(mxResources.get('invalidName')); + + return false; + })); + this.showDialog(dlg.container, 300, 100, true, true); + dlg.init(); + } +}; + +/** + * Saves the current graph under the given filename. + */ +EditorUi.prototype.save = function(name) +{ + if (name != null) + { + if (this.editor.graph.isEditing()) + { + this.editor.graph.stopEditing(); + } + + var xml = mxUtils.getXml(this.editor.getGraphXml()); + + try + { + if (Editor.useLocalStorage) + { + if (localStorage.getItem(name) != null && + !mxUtils.confirm(mxResources.get('replaceIt', [name]))) + { + return; + } + + localStorage.setItem(name, xml); + this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('saved')) + ' ' + new Date()); + } + else + { + if (xml.length < MAX_REQUEST_SIZE) + { + new mxXmlRequest(SAVE_URL, 'filename=' + encodeURIComponent(name) + + '&xml=' + encodeURIComponent(xml)).simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + mxUtils.popup(xml); + + return; + } + } + + this.editor.setModified(false); + this.editor.setFilename(name); + this.updateDocumentTitle(); + } + catch (e) + { + this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('errorSavingFile'))); + } + } +}; + +/** + * Executes the given layout. + */ +EditorUi.prototype.executeLayout = function(exec, animate, post) +{ + var graph = this.editor.graph; + + if (graph.isEnabled()) + { + graph.getModel().beginUpdate(); + try + { + exec(); + } + catch (e) + { + throw e; + } + finally + { + // Animates the changes in the graph model except + // for Camino, where animation is too slow + if (this.allowAnimation && animate && navigator.userAgent.indexOf('Camino') < 0) + { + // New API for animating graph layout results asynchronously + var morph = new mxMorphing(graph); + morph.addListener(mxEvent.DONE, mxUtils.bind(this, function() + { + graph.getModel().endUpdate(); + + if (post != null) + { + post(); + } + })); + + morph.startAnimation(); + } + else + { + graph.getModel().endUpdate(); + + if (post != null) + { + post(); + } + } + } + } +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showImageDialog = function(title, value, fn, ignoreExisting) +{ + var cellEditor = this.editor.graph.cellEditor; + var selState = cellEditor.saveSelection(); + var newValue = mxUtils.prompt(title, value); + cellEditor.restoreSelection(selState); + + if (newValue != null && newValue.length > 0) + { + var img = new Image(); + + img.onload = function() + { + fn(newValue, img.width, img.height); + }; + img.onerror = function() + { + fn(null); + mxUtils.alert(mxResources.get('fileNotFound')); + }; + + img.src = newValue; + } + else + { + fn(null); + } +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showLinkDialog = function(value, btnLabel, fn) +{ + var dlg = new LinkDialog(this, value, btnLabel, fn); + this.showDialog(dlg.container, 420, 90, true, true); + dlg.init(); +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showDataDialog = function(cell) +{ + if (cell != null) + { + var dlg = new EditDataDialog(this, cell); + this.showDialog(dlg.container, 340, 340, true, false, null, false); + dlg.init(); + } +}; + +/** + * Hides the current menu. + */ +EditorUi.prototype.showBackgroundImageDialog = function(apply) +{ + apply = (apply != null) ? apply : mxUtils.bind(this, function(image) + { + var change = new ChangePageSetup(this, null, image); + change.ignoreColor = true; + + this.editor.graph.model.execute(change); + }); + + var newValue = mxUtils.prompt(mxResources.get('backgroundImage'), ''); + + if (newValue != null && newValue.length > 0) + { + var img = new Image(); + + img.onload = function() + { + apply(new mxImage(newValue, img.width, img.height)); + }; + img.onerror = function() + { + apply(null); + mxUtils.alert(mxResources.get('fileNotFound')); + }; + + img.src = newValue; + } + else + { + apply(null); + } +}; + +/** + * Loads the stylesheet for this graph. + */ +EditorUi.prototype.setBackgroundImage = function(image) +{ + this.editor.graph.setBackgroundImage(image); + this.editor.graph.view.validateBackgroundImage(); + + this.fireEvent(new mxEventObject('backgroundImageChanged')); +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.confirm = function(msg, okFn, cancelFn) +{ + if (mxUtils.confirm(msg)) + { + if (okFn != null) + { + okFn(); + } + } + else if (cancelFn != null) + { + cancelFn(); + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.createOutline = function(wnd) +{ + var outline = new mxOutline(this.editor.graph); + outline.border = 20; + + mxEvent.addListener(window, 'resize', function() + { + outline.update(); + }); + + this.addListener('pageFormatChanged', function() + { + outline.update(); + }); + + return outline; +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.createKeyHandler = function(editor) +{ + var editorUi = this; + var graph = this.editor.graph; + var keyHandler = new mxKeyHandler(graph); + + var isEventIgnored = keyHandler.isEventIgnored; + keyHandler.isEventIgnored = function(evt) + { + // Handles undo/redo/ctrl+./,/u via action and allows ctrl+b/i only if editing value is HTML (except for FF and Safari) + return (!this.isControlDown(evt) || mxEvent.isShiftDown(evt) || (evt.keyCode != 90 && evt.keyCode != 89 && + evt.keyCode != 188 && evt.keyCode != 190 && evt.keyCode != 85)) && ((evt.keyCode != 66 && evt.keyCode != 73) || + !this.isControlDown(evt) || (this.graph.cellEditor.isContentEditing() && !mxClient.IS_FF && !mxClient.IS_SF)) && + isEventIgnored.apply(this, arguments); + }; + + // Ignores graph enabled state but not chromeless state + keyHandler.isEnabledForEvent = function(evt) + { + return (!mxEvent.isConsumed(evt) && this.isGraphEvent(evt) && this.isEnabled() && + (editorUi.dialogs == null || editorUi.dialogs.length == 0)); + }; + + // Routes command-key to control-key on Mac + keyHandler.isControlDown = function(evt) + { + return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey); + }; + + var queue = []; + var thread = null; + + // Helper function to move cells with the cursor keys + function nudge(keyCode, stepSize, resize) + { + queue.push(function() + { + if (!graph.isSelectionEmpty() && graph.isEnabled()) + { + stepSize = (stepSize != null) ? stepSize : 1; + + if (resize) + { + // Resizes all selected vertices + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i]) && graph.isCellResizable(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + + if (keyCode == 37) + { + geo.width = Math.max(0, geo.width - stepSize); + } + else if (keyCode == 38) + { + geo.height = Math.max(0, geo.height - stepSize); + } + else if (keyCode == 39) + { + geo.width += stepSize; + } + else if (keyCode == 40) + { + geo.height += stepSize; + } + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + else + { + // Moves vertices up/down in a stack layout + var cell = graph.getSelectionCell(); + var parent = graph.model.getParent(cell); + var layout = null; + + if (graph.getSelectionCount() == 1 && graph.model.isVertex(cell) && + graph.layoutManager != null && !graph.isCellLocked(cell)) + { + layout = graph.layoutManager.getLayout(parent); + } + + if (layout != null && layout.constructor == mxStackLayout) + { + var index = parent.getIndex(cell); + + if (keyCode == 37 || keyCode == 38) + { + graph.model.add(parent, cell, Math.max(0, index - 1)); + } + else if (keyCode == 39 ||keyCode == 40) + { + graph.model.add(parent, cell, Math.min(graph.model.getChildCount(parent), index + 1)); + } + } + else + { + var dx = 0; + var dy = 0; + + if (keyCode == 37) + { + dx = -stepSize; + } + else if (keyCode == 38) + { + dy = -stepSize; + } + else if (keyCode == 39) + { + dx = stepSize; + } + else if (keyCode == 40) + { + dy = stepSize; + } + + graph.moveCells(graph.getMovableCells(graph.getSelectionCells()), dx, dy); + } + } + } + }); + + if (thread != null) + { + window.clearTimeout(thread); + } + + thread = window.setTimeout(function() + { + if (queue.length > 0) + { + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < queue.length; i++) + { + queue[i](); + } + + queue = []; + } + finally + { + graph.getModel().endUpdate(); + } + graph.scrollCellToVisible(graph.getSelectionCell()); + } + }, 200); + }; + + // Overridden to handle special alt+shift+cursor keyboard shortcuts + var directions = {37: mxConstants.DIRECTION_WEST, 38: mxConstants.DIRECTION_NORTH, + 39: mxConstants.DIRECTION_EAST, 40: mxConstants.DIRECTION_SOUTH}; + + var keyHandlerGetFunction = keyHandler.getFunction; + + // Alt+Shift+Keycode mapping to action + var altShiftActions = {67: this.actions.get('clearWaypoints'), // Alt+Shift+C + 65: this.actions.get('connectionArrows'), // Alt+Shift+A + 76: this.actions.get('editLink'), // Alt+Shift+L + 80: this.actions.get('connectionPoints'), // Alt+Shift+P + 84: this.actions.get('editTooltip'), // Alt+Shift+T + 86: this.actions.get('pasteSize'), // Alt+Shift+V + 88: this.actions.get('copySize') // Alt+Shift+X + }; + + mxKeyHandler.prototype.getFunction = function(evt) + { + if (graph.isEnabled()) + { + // TODO: Add alt modified state in core API, here are some specific cases + if (mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) + { + var action = altShiftActions[evt.keyCode]; + + if (action != null) + { + return action.funct; + } + } + + if (evt.keyCode == 9 && mxEvent.isAltDown(evt)) + { + if (mxEvent.isShiftDown(evt)) + { + // Alt+Shift+Tab + return function() + { + graph.selectParentCell(); + }; + } + else + { + // Alt+Tab + return function() + { + graph.selectChildCell(); + }; + } + } + else if (directions[evt.keyCode] != null && !graph.isSelectionEmpty()) + { + if (mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) + { + if (graph.model.isVertex(graph.getSelectionCell())) + { + return function() + { + var cells = graph.connectVertex(graph.getSelectionCell(), directions[evt.keyCode], + graph.defaultEdgeLength, evt, true); + + if (cells != null && cells.length > 0) + { + if (cells.length == 1 && graph.model.isEdge(cells[0])) + { + graph.setSelectionCell(graph.model.getTerminal(cells[0], false)); + } + else + { + graph.setSelectionCell(cells[cells.length - 1]); + } + + graph.scrollCellToVisible(graph.getSelectionCell()); + + if (editorUi.hoverIcons != null) + { + editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); + } + } + }; + } + } + else + { + // Avoids consuming event if no vertex is selected by returning null below + // Cursor keys move and resize (ctrl) cells + if (this.isControlDown(evt)) + { + return function() + { + nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null, true); + }; + } + else + { + return function() + { + nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null); + }; + } + } + } + } + + return keyHandlerGetFunction.apply(this, arguments); + }; + + // Binds keystrokes to actions + keyHandler.bindAction = mxUtils.bind(this, function(code, control, key, shift) + { + var action = this.actions.get(key); + + if (action != null) + { + var f = function() + { + if (action.isEnabled()) + { + action.funct(); + } + }; + + if (control) + { + if (shift) + { + keyHandler.bindControlShiftKey(code, f); + } + else + { + keyHandler.bindControlKey(code, f); + } + } + else + { + if (shift) + { + keyHandler.bindShiftKey(code, f); + } + else + { + keyHandler.bindKey(code, f); + } + } + } + }); + + var ui = this; + var keyHandlerEscape = keyHandler.escape; + keyHandler.escape = function(evt) + { + keyHandlerEscape.apply(this, arguments); + }; + + // Ignores enter keystroke. Remove this line if you want the + // enter keystroke to stop editing. N, W, T are reserved. + keyHandler.enter = function() {}; + + keyHandler.bindControlShiftKey(36, function() { graph.exitGroup(); }); // Ctrl+Shift+Home + keyHandler.bindControlShiftKey(35, function() { graph.enterGroup(); }); // Ctrl+Shift+End + keyHandler.bindKey(36, function() { graph.home(); }); // Home + keyHandler.bindKey(35, function() { graph.refresh(); }); // End + keyHandler.bindAction(107, true, 'zoomIn'); // Ctrl+Plus + keyHandler.bindAction(109, true, 'zoomOut'); // Ctrl+Minus + keyHandler.bindAction(80, true, 'print'); // Ctrl+P + keyHandler.bindAction(79, true, 'outline', true); // Ctrl+Shift+O + keyHandler.bindAction(112, false, 'about'); // F1 + + if (!this.editor.chromeless || this.editor.editable) + { + keyHandler.bindControlKey(36, function() { if (graph.isEnabled()) { graph.foldCells(true); }}); // Ctrl+Home + keyHandler.bindControlKey(35, function() { if (graph.isEnabled()) { graph.foldCells(false); }}); // Ctrl+End + keyHandler.bindControlKey(13, function() { if (graph.isEnabled()) { graph.setSelectionCells(graph.duplicateCells(graph.getSelectionCells(), false)); }}); // Ctrl+Enter + keyHandler.bindAction(8, false, 'delete'); // Backspace + keyHandler.bindAction(8, true, 'deleteAll'); // Backspace + keyHandler.bindAction(46, false, 'delete'); // Delete + keyHandler.bindAction(46, true, 'deleteAll'); // Ctrl+Delete + keyHandler.bindAction(72, true, 'resetView'); // Ctrl+H + keyHandler.bindAction(72, true, 'fitWindow', true); // Ctrl+Shift+H + keyHandler.bindAction(74, true, 'fitPage'); // Ctrl+J + keyHandler.bindAction(74, true, 'fitTwoPages', true); // Ctrl+Shift+J + keyHandler.bindAction(48, true, 'customZoom'); // Ctrl+0 + keyHandler.bindAction(82, true, 'turn'); // Ctrl+R + keyHandler.bindAction(82, true, 'clearDefaultStyle', true); // Ctrl+Shift+R + keyHandler.bindAction(83, true, 'save'); // Ctrl+S + keyHandler.bindAction(83, true, 'saveAs', true); // Ctrl+Shift+S + keyHandler.bindAction(65, true, 'selectAll'); // Ctrl+A + keyHandler.bindAction(65, true, 'selectNone', true); // Ctrl+A + keyHandler.bindAction(73, true, 'selectVertices', true); // Ctrl+Shift+I + keyHandler.bindAction(69, true, 'selectEdges', true); // Ctrl+Shift+E + keyHandler.bindAction(69, true, 'editStyle'); // Ctrl+E + keyHandler.bindAction(66, true, 'bold'); // Ctrl+B + keyHandler.bindAction(66, true, 'toBack', true); // Ctrl+Shift+B + keyHandler.bindAction(70, true, 'toFront', true); // Ctrl+Shift+F + keyHandler.bindAction(68, true, 'duplicate'); // Ctrl+D + keyHandler.bindAction(68, true, 'setAsDefaultStyle', true); // Ctrl+Shift+D + keyHandler.bindAction(90, true, 'undo'); // Ctrl+Z + keyHandler.bindAction(89, true, 'autosize', true); // Ctrl+Shift+Y + keyHandler.bindAction(88, true, 'cut'); // Ctrl+X + keyHandler.bindAction(67, true, 'copy'); // Ctrl+C + keyHandler.bindAction(86, true, 'paste'); // Ctrl+V + keyHandler.bindAction(71, true, 'group'); // Ctrl+G + keyHandler.bindAction(77, true, 'editData'); // Ctrl+M + keyHandler.bindAction(71, true, 'grid', true); // Ctrl+Shift+G + keyHandler.bindAction(73, true, 'italic'); // Ctrl+I + keyHandler.bindAction(76, true, 'lockUnlock'); // Ctrl+L + keyHandler.bindAction(76, true, 'layers', true); // Ctrl+Shift+L + keyHandler.bindAction(80, true, 'formatPanel', true); // Ctrl+Shift+P + keyHandler.bindAction(85, true, 'underline'); // Ctrl+U + keyHandler.bindAction(85, true, 'ungroup', true); // Ctrl+Shift+U + keyHandler.bindAction(190, true, 'superscript'); // Ctrl+. + keyHandler.bindAction(188, true, 'subscript'); // Ctrl+, + keyHandler.bindKey(13, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // Enter + keyHandler.bindKey(113, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // F2 + } + + if (!mxClient.IS_WIN) + { + keyHandler.bindAction(90, true, 'redo', true); // Ctrl+Shift+Z + } + else + { + keyHandler.bindAction(89, true, 'redo'); // Ctrl+Y + } + + return keyHandler; +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +EditorUi.prototype.destroy = function() +{ + if (this.editor != null) + { + this.editor.destroy(); + this.editor = null; + } + + if (this.menubar != null) + { + this.menubar.destroy(); + this.menubar = null; + } + + if (this.toolbar != null) + { + this.toolbar.destroy(); + this.toolbar = null; + } + + if (this.sidebar != null) + { + this.sidebar.destroy(); + this.sidebar = null; + } + + if (this.keyHandler != null) + { + this.keyHandler.destroy(); + this.keyHandler = null; + } + + if (this.keydownHandler != null) + { + mxEvent.removeListener(document, 'keydown', this.keydownHandler); + this.keydownHandler = null; + } + + if (this.keyupHandler != null) + { + mxEvent.removeListener(document, 'keyup', this.keyupHandler); + this.keyupHandler = null; + } + + if (this.resizeHandler != null) + { + mxEvent.removeListener(window, 'resize', this.resizeHandler); + this.resizeHandler = null; + } + + if (this.gestureHandler != null) + { + mxEvent.removeGestureListeners(document, this.gestureHandler); + this.gestureHandler = null; + } + + if (this.orientationChangeHandler != null) + { + mxEvent.removeListener(window, 'orientationchange', this.orientationChangeHandler); + this.orientationChangeHandler = null; + } + + if (this.scrollHandler != null) + { + mxEvent.removeListener(window, 'scroll', this.scrollHandler); + this.scrollHandler = null; + } + + if (this.destroyFunctions != null) + { + for (var i = 0; i < this.destroyFunctions.length; i++) + { + this.destroyFunctions[i](); + } + + this.destroyFunctions = null; + } + + var c = [this.menubarContainer, this.toolbarContainer, this.sidebarContainer, + this.formatContainer, this.diagramContainer, this.footerContainer, + this.chromelessToolbar, this.hsplit, this.sidebarFooterContainer, + this.layersDialog]; + + for (var i = 0; i < c.length; i++) + { + if (c[i] != null && c[i].parentNode != null) + { + c[i].parentNode.removeChild(c[i]); + } + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Format.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Format.js new file mode 100644 index 00000000..a0d2c6fb --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Format.js @@ -0,0 +1,5496 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +Format = function(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.labelIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.currentIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.showCloseButton = true; + +/** + * Background color for inactive tabs. + */ +Format.prototype.inactiveTabBackgroundColor = '#d7d7d7'; + +/** + * Background color for inactive tabs. + */ +Format.prototype.roundableShapes = ['label', 'rectangle', 'internalStorage', 'corner', + 'parallelogram', 'swimlane', 'triangle', 'trapezoid', + 'ext', 'step', 'tee', 'process', 'link', + 'rhombus', 'offPageConnector', 'loopLimit', 'hexagon', + 'manualInput', 'curlyBracket', 'singleArrow', 'callout', + 'doubleArrow', 'flexArrow', 'card', 'umlLifeline']; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + this.update = mxUtils.bind(this, function(sender, evt) + { + this.clearSelectionState(); + this.refresh(); + }); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.EDITING_STARTED, this.update); + graph.addListener(mxEvent.EDITING_STOPPED, this.update); + graph.getModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.ROOT, mxUtils.bind(this, function() + { + this.refresh(); + })); + + editor.addListener('autosaveChanged', mxUtils.bind(this, function() + { + this.refresh(); + })); + + this.refresh(); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.clearSelectionState = function() +{ + this.selectionState = null; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.getSelectionState = function() +{ + if (this.selectionState == null) + { + this.selectionState = this.createSelectionState(); + } + + return this.selectionState; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.createSelectionState = function() +{ + var cells = this.editorUi.editor.graph.getSelectionCells(); + var result = this.initSelectionState(); + + for (var i = 0; i < cells.length; i++) + { + this.updateSelectionStateForCell(result, cells[i], cells); + } + + return result; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.initSelectionState = function() +{ + return {vertices: [], edges: [], x: null, y: null, width: null, height: null, style: {}, + containsImage: false, containsLabel: false, fill: true, glass: true, rounded: true, + comic: true, autoSize: false, image: true, shadow: true, lineJumps: true}; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.updateSelectionStateForCell = function(result, cell, cells) +{ + var graph = this.editorUi.editor.graph; + + if (graph.getModel().isVertex(cell)) + { + result.vertices.push(cell); + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + if (geo.width > 0) + { + if (result.width == null) + { + result.width = geo.width; + } + else if (result.width != geo.width) + { + result.width = ''; + } + } + else + { + result.containsLabel = true; + } + + if (geo.height > 0) + { + if (result.height == null) + { + result.height = geo.height; + } + else if (result.height != geo.height) + { + result.height = ''; + } + } + else + { + result.containsLabel = true; + } + + if (!geo.relative || geo.offset != null) + { + var x = (geo.relative) ? geo.offset.x : geo.x; + var y = (geo.relative) ? geo.offset.y : geo.y; + + if (result.x == null) + { + result.x = x; + } + else if (result.x != x) + { + result.x = ''; + } + + if (result.y == null) + { + result.y = y; + } + else if (result.y != y) + { + result.y = ''; + } + } + } + } + else if (graph.getModel().isEdge(cell)) + { + result.edges.push(cell); + } + + var state = graph.view.getState(cell); + + if (state != null) + { + result.autoSize = result.autoSize || this.isAutoSizeState(state); + result.glass = result.glass && this.isGlassState(state); + result.rounded = result.rounded && this.isRoundedState(state); + result.lineJumps = result.lineJumps && this.isLineJumpState(state); + result.comic = result.comic && this.isComicState(state); + result.image = result.image && this.isImageState(state); + result.shadow = result.shadow && this.isShadowState(state); + result.fill = result.fill && this.isFillState(state); + + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + result.containsImage = result.containsImage || shape == 'image'; + + for (var key in state.style) + { + var value = state.style[key]; + + if (value != null) + { + if (result.style[key] == null) + { + result.style[key] = value; + } + else if (result.style[key] != value) + { + result.style[key] = ''; + } + } + } + } +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isFillState = function(state) +{ + return state.view.graph.model.isVertex(state.cell) || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'arrow' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'filledEdge' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'flexArrow'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isGlassState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'rectangle' || shape == 'internalStorage' || + shape == 'ext' || shape == 'umlLifeline' || shape == 'swimlane' || + shape == 'process'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isRoundedState = function(state) +{ + return (state.shape != null) ? state.shape.isRoundable() : + mxUtils.indexOf(this.roundableShapes, mxUtils.getValue(state.style, + mxConstants.STYLE_SHAPE, null)) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isLineJumpState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + var curved = mxUtils.getValue(state.style, mxConstants.STYLE_CURVED, false); + + return !curved && (shape == 'connector' || shape == 'filledEdge'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isComicState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return mxUtils.indexOf(['label', 'rectangle', 'internalStorage', 'corner', 'parallelogram', 'note', 'collate', + 'swimlane', 'triangle', 'trapezoid', 'ext', 'step', 'tee', 'process', 'link', 'rhombus', + 'offPageConnector', 'loopLimit', 'hexagon', 'manualInput', 'singleArrow', 'doubleArrow', + 'flexArrow', 'filledEdge', 'card', 'umlLifeline', 'connector', 'folder', 'component', 'sortShape', + 'cross', 'umlFrame', 'cube', 'isoCube', 'isoRectangle', 'partialRectangle'], shape) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isAutoSizeState = function(state) +{ + return mxUtils.getValue(state.style, mxConstants.STYLE_AUTOSIZE, null) == '1'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isImageState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'image'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isShadowState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape != 'image'); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.clear = function() +{ + this.container.innerHTML = ''; + + // Destroy existing panels + if (this.panels != null) + { + for (var i = 0; i < this.panels.length; i++) + { + this.panels[i].destroy(); + } + } + + this.panels = []; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.refresh = function() +{ + // Performance tweak: No refresh needed if not visible + if (this.container.style.width == '0px') + { + return; + } + + this.clear(); + var ui = this.editorUi; + var graph = ui.editor.graph; + + var div = document.createElement('div'); + div.style.whiteSpace = 'nowrap'; + div.style.color = 'rgb(112, 112, 112)'; + div.style.textAlign = 'left'; + div.style.cursor = 'default'; + + var label = document.createElement('div'); + label.style.border = '1px solid #c0c0c0'; + label.style.borderWidth = '0px 0px 1px 0px'; + label.style.textAlign = 'center'; + label.style.fontWeight = 'bold'; + label.style.overflow = 'hidden'; + label.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + label.style.paddingTop = '8px'; + label.style.height = (mxClient.IS_QUIRKS) ? '34px' : '25px'; + label.style.width = '100%'; + this.container.appendChild(div); + + if (graph.isSelectionEmpty()) + { + mxUtils.write(label, mxResources.get('diagram')); + + // Adds button to hide the format panel since + // people don't seem to find the toolbar button + // and the menu item in the format menu + if (this.showCloseButton) + { + var img = document.createElement('img'); + img.setAttribute('border', '0'); + img.setAttribute('src', Dialog.prototype.closeImage); + img.setAttribute('title', mxResources.get('hide')); + img.style.position = 'absolute'; + img.style.display = 'block'; + img.style.right = '0px'; + img.style.top = '8px'; + img.style.cursor = 'pointer'; + img.style.marginTop = '1px'; + img.style.marginRight = '17px'; + img.style.border = '1px solid transparent'; + img.style.padding = '1px'; + img.style.opacity = 0.5; + label.appendChild(img) + + mxEvent.addListener(img, 'click', function() + { + ui.actions.get('formatPanel').funct(); + }); + } + + div.appendChild(label); + this.panels.push(new DiagramFormatPanel(this, ui, div)); + } + else if (graph.isEditing()) + { + mxUtils.write(label, mxResources.get('text')); + div.appendChild(label); + this.panels.push(new TextFormatPanel(this, ui, div)); + } + else + { + var containsLabel = this.getSelectionState().containsLabel; + var currentLabel = null; + var currentPanel = null; + + var addClickHandler = mxUtils.bind(this, function(elt, panel, index) + { + var clickHandler = mxUtils.bind(this, function(evt) + { + if (currentLabel != elt) + { + if (containsLabel) + { + this.labelIndex = index; + } + else + { + this.currentIndex = index; + } + + if (currentLabel != null) + { + currentLabel.style.backgroundColor = this.inactiveTabBackgroundColor; + currentLabel.style.borderBottomWidth = '1px'; + } + + currentLabel = elt; + currentLabel.style.backgroundColor = ''; + currentLabel.style.borderBottomWidth = '0px'; + + if (currentPanel != panel) + { + if (currentPanel != null) + { + currentPanel.style.display = 'none'; + } + + currentPanel = panel; + currentPanel.style.display = ''; + } + } + }); + + mxEvent.addListener(elt, 'click', clickHandler); + + if (index == ((containsLabel) ? this.labelIndex : this.currentIndex)) + { + // Invokes handler directly as a workaround for no click on DIV in KHTML. + clickHandler(); + } + }); + + var idx = 0; + + label.style.backgroundColor = this.inactiveTabBackgroundColor; + label.style.borderLeftWidth = '1px'; + label.style.width = (containsLabel) ? '50%' : '33.3%'; + label.style.width = (containsLabel) ? '50%' : '33.3%'; + var label2 = label.cloneNode(false); + var label3 = label2.cloneNode(false); + + // Workaround for ignored background in IE + label2.style.backgroundColor = this.inactiveTabBackgroundColor; + label3.style.backgroundColor = this.inactiveTabBackgroundColor; + + // Style + if (containsLabel) + { + label2.style.borderLeftWidth = '0px'; + } + else + { + label.style.borderLeftWidth = '0px'; + mxUtils.write(label, mxResources.get('style')); + div.appendChild(label); + + var stylePanel = div.cloneNode(false); + stylePanel.style.display = 'none'; + this.panels.push(new StyleFormatPanel(this, ui, stylePanel)); + this.container.appendChild(stylePanel); + + addClickHandler(label, stylePanel, idx++); + } + + // Text + mxUtils.write(label2, mxResources.get('text')); + div.appendChild(label2); + + var textPanel = div.cloneNode(false); + textPanel.style.display = 'none'; + this.panels.push(new TextFormatPanel(this, ui, textPanel)); + this.container.appendChild(textPanel); + + // Arrange + mxUtils.write(label3, mxResources.get('arrange')); + div.appendChild(label3); + + var arrangePanel = div.cloneNode(false); + arrangePanel.style.display = 'none'; + this.panels.push(new ArrangePanel(this, ui, arrangePanel)); + this.container.appendChild(arrangePanel); + + addClickHandler(label2, textPanel, idx++); + addClickHandler(label3, arrangePanel, idx++); + } +}; + +/** + * Base class for format panels. + */ +BaseFormatPanel = function(format, editorUi, container) +{ + this.format = format; + this.editorUi = editorUi; + this.container = container; + this.listeners = []; +}; + +/** + * + */ +BaseFormatPanel.prototype.buttonBackgroundColor = 'white'; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.getSelectionState = function() +{ + var graph = this.editorUi.editor.graph; + var cells = graph.getSelectionCells(); + var shape = null; + + for (var i = 0; i < cells.length; i++) + { + var state = graph.view.getState(cells[i]); + + if (state != null) + { + var tmp = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + if (tmp != null) + { + if (shape == null) + { + shape = tmp; + } + else if (shape != tmp) + { + return null; + } + } + + } + } + + return shape; +}; + +/** + * Install input handler. + */ +BaseFormatPanel.prototype.installInputHandler = function(input, key, defaultValue, min, max, unit, textEditFallback, isFloat) +{ + unit = (unit != null) ? unit : ''; + isFloat = (isFloat != null) ? isFloat : false; + + var ui = this.editorUi; + var graph = ui.editor.graph; + + min = (min != null) ? min : 1; + max = (max != null) ? max : 999; + + var selState = null; + var updating = false; + + var update = mxUtils.bind(this, function(evt) + { + var value = (isFloat) ? parseFloat(input.value) : parseInt(input.value); + + // Special case: angle mod 360 + if (!isNaN(value) && key == mxConstants.STYLE_ROTATION) + { + // Workaround for decimal rounding errors in floats is to + // use integer and round all numbers to two decimal point + value = mxUtils.mod(Math.round(value * 100), 36000) / 100; + } + + value = Math.min(max, Math.max(min, (isNaN(value)) ? defaultValue : value)); + + if (graph.cellEditor.isContentEditing() && textEditFallback) + { + if (!updating) + { + updating = true; + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + textEditFallback(value); + input.value = value + unit; + + // Restore focus and selection in input + updating = false; + } + } + else if (value != mxUtils.getValue(this.format.getSelectionState().style, key, defaultValue)) + { + if (graph.isEditing()) + { + graph.stopEditing(true); + } + + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(key, value, graph.getSelectionCells()); + + // Handles special case for fontSize where HTML labels are parsed and updated + if (key == mxConstants.STYLE_FONTSIZE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontSize = value + 'px'; + elt.removeAttribute('size'); + }); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + + input.value = value + unit; + mxEvent.consume(evt); + }); + + if (textEditFallback && graph.cellEditor.isContentEditing()) + { + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + } + + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'blur', update); + + return update; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createPanel = function() +{ + var div = document.createElement('div'); + div.style.padding = '12px 0px 12px 18px'; + div.style.borderBottom = '1px solid #c0c0c0'; + + return div; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createTitle = function(title) +{ + var div = document.createElement('div'); + div.style.padding = '0px 0px 6px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.fontWeight = 'bold'; + mxUtils.write(div, title); + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createStepper = function(input, update, step, height, disableFocus, defaultValue) +{ + step = (step != null) ? step : 1; + height = (height != null) ? height : 8; + + if (mxClient.IS_QUIRKS) + { + height = height - 2; + } + else if (mxClient.IS_MT || document.documentMode >= 8) + { + height = height + 1; + } + + var stepper = document.createElement('div'); + mxUtils.setPrefixedStyle(stepper.style, 'borderRadius', '3px'); + stepper.style.border = '1px solid rgb(192, 192, 192)'; + stepper.style.position = 'absolute'; + + var up = document.createElement('div'); + up.style.borderBottom = '1px solid rgb(192, 192, 192)'; + up.style.position = 'relative'; + up.style.height = height + 'px'; + up.style.width = '10px'; + up.className = 'geBtnUp'; + stepper.appendChild(up); + + var down = up.cloneNode(false); + down.style.border = 'none'; + down.style.height = height + 'px'; + down.className = 'geBtnDown'; + stepper.appendChild(down); + + mxEvent.addListener(down, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '2'; + } + + var val = parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val - step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + mxEvent.addListener(up, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '0'; + } + + var val = parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val + step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + // Disables transfer of focus to DIV but also :active CSS + // so it's only used for fontSize where the focus should + // stay on the selected text, but not for any other input. + if (disableFocus) + { + var currentSelection = null; + + mxEvent.addGestureListeners(stepper, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + currentSelection = document.selection.createRange(); + } + + mxEvent.consume(evt); + }, + null, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (currentSelection != null) + { + try + { + currentSelection.select(); + } + catch (e) + { + // ignore + } + + currentSelection = null; + mxEvent.consume(evt); + } + } + ); + } + + return stepper; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createOption = function(label, isCheckedFn, setCheckedFn, listener) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + div.appendChild(cb); + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var applying = false; + var value = isCheckedFn(); + + var apply = function(newValue) + { + if (!applying) + { + applying = true; + + if (newValue) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + if (value != newValue) + { + value = newValue; + + // Checks if the color value needs to be updated in the model + if (isCheckedFn() != value) + { + setCheckedFn(value); + } + } + + applying = false; + } + }; + + mxEvent.addListener(div, 'click', function(evt) + { + if (cb.getAttribute('disabled') != 'disabled') + { + // Toggles checkbox state for click on label + var source = mxEvent.getSource(evt); + + if (source == div || source == span) + { + cb.checked = !cb.checked; + } + + apply(cb.checked); + } + }); + + apply(value); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * The string 'null' means use null in values. + */ +BaseFormatPanel.prototype.createCellOption = function(label, key, defaultValue, enabledValue, disabledValue, fn, action, stopEditing) +{ + enabledValue = (enabledValue != null) ? ((enabledValue == 'null') ? null : enabledValue) : '1'; + disabledValue = (disabledValue != null) ? ((disabledValue == 'null') ? null : disabledValue) : '0'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, key, defaultValue) != disabledValue; + } + + return null; + }, function(checked) + { + if (stopEditing) + { + graph.stopEditing(); + } + + if (action != null) + { + action.funct(); + } + else + { + graph.getModel().beginUpdate(); + try + { + var value = (checked) ? enabledValue : disabledValue; + graph.setCellStyles(key, value, graph.getSelectionCells()); + + if (fn != null) + { + fn(graph.getSelectionCells(), value); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, key, defaultValue) != disabledValue); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }); +}; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setColorFn, defaultColor, listener, callbackFn, hideCheckbox) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + + if (!hideCheckbox) + { + div.appendChild(cb); + } + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var applying = false; + var value = getColorFn(); + + var btn = null; + + var apply = function(color, disableUpdate, forceUpdate) + { + if (!applying) + { + applying = true; + btn.innerHTML = '
'; + + // Fine-tuning in Firefox, quirks mode and IE8 standards + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + btn.firstChild.style.margin = '0px'; + } + + if (color != null && color != mxConstants.NONE) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + + if (callbackFn != null) + { + callbackFn(color); + } + + if (!disableUpdate) + { + value = color; + + // Checks if the color value needs to be updated in the model + if (forceUpdate || hideCheckbox || getColorFn() != value) + { + setColorFn(value); + } + } + + applying = false; + } + }; + + btn = mxUtils.button('', mxUtils.bind(this, function(evt) + { + this.editorUi.pickColor(value, function(color) + { + apply(color, null, true); + }); + mxEvent.consume(evt); + })); + + btn.style.position = 'absolute'; + btn.style.marginTop = '-4px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '0px' : '20px'; + btn.style.height = '22px'; + btn.className = 'geColorBtn'; + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + div.appendChild(btn); + + mxEvent.addListener(div, 'click', function(evt) + { + var source = mxEvent.getSource(evt); + + if (source == cb || source.nodeName != 'INPUT') + { + // Toggles checkbox state for click on label + if (source != cb) + { + cb.checked = !cb.checked; + } + + // Overrides default value with current value to make it easier + // to restore previous value if the checkbox is clicked twice + if (!cb.checked && value != null && value != mxConstants.NONE && + defaultColor != mxConstants.NONE) + { + defaultColor = value; + } + + apply((cb.checked) ? defaultColor : mxConstants.NONE); + } + }); + + apply(value, true); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createCellColorOption = function(label, colorKey, defaultColor, callbackFn, setStyleFn) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createColorOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, colorKey, null); + } + + return null; + }, function(color) + { + graph.getModel().beginUpdate(); + try + { + if (setStyleFn != null) + { + setStyleFn(color); + } + + graph.setCellStyles(colorKey, color, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [colorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }, defaultColor || mxConstants.NONE, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, colorKey, null)); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }, callbackFn); +}; + +/** + * + */ +BaseFormatPanel.prototype.addArrow = function(elt, height) +{ + height = (height != null) ? height : 10; + + var arrow = document.createElement('div'); + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + arrow.style.padding = '6px'; + arrow.style.paddingRight = '4px'; + + var m = (10 - height); + + if (m == 2) + { + arrow.style.paddingTop = 6 + 'px'; + } + else if (m > 0) + { + arrow.style.paddingTop = (6 - m) + 'px'; + } + else + { + arrow.style.marginTop = '-2px'; + } + + arrow.style.height = height + 'px'; + arrow.style.borderLeft = '1px solid #a0a0a0'; + arrow.innerHTML = ''; + mxUtils.setOpacity(arrow, 70); + + var symbol = elt.getElementsByTagName('div')[0]; + + if (symbol != null) + { + symbol.style.paddingRight = '6px'; + symbol.style.marginLeft = '4px'; + symbol.style.marginTop = '-1px'; + symbol.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + mxUtils.setOpacity(symbol, 60); + } + + mxUtils.setOpacity(elt, 100); + elt.style.border = '1px solid #a0a0a0'; + elt.style.backgroundColor = this.buttonBackgroundColor; + elt.style.backgroundImage = 'none'; + elt.style.width = 'auto'; + elt.className += ' geColorBtn'; + mxUtils.setPrefixedStyle(elt.style, 'borderRadius', '3px'); + + elt.appendChild(arrow); + + return symbol; +}; + +/** + * + */ +BaseFormatPanel.prototype.addUnitInput = function(container, unit, right, width, update, step, marginTop, disableFocus) +{ + marginTop = (marginTop != null) ? marginTop : 0; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.marginTop = '-2px'; + input.style.right = (right + 12) + 'px'; + input.style.width = width + 'px'; + container.appendChild(input); + + var stepper = this.createStepper(input, update, step, null, disableFocus); + stepper.style.marginTop = (marginTop - 2) + 'px'; + stepper.style.right = right + 'px'; + container.appendChild(stepper); + + return input; +}; + +/** + * + */ +BaseFormatPanel.prototype.createRelativeOption = function(label, key, width, handler, init) +{ + width = (width != null) ? width : 44; + + var graph = this.editorUi.editor.graph; + var div = this.createPanel(); + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + mxUtils.write(div, label); + div.style.fontWeight = 'bold'; + + var update = mxUtils.bind(this, function(evt) + { + if (handler != null) + { + handler(input); + } + else + { + var value = parseInt(input.value); + value = Math.min(100, Math.max(0, (isNaN(value)) ? 100 : value)); + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null && value != mxUtils.getValue(state.style, key, 100)) + { + // Removes entry in style (assumes 100 is default for relative values) + if (value == 100) + { + value = null; + } + + graph.setCellStyles(key, value, graph.getSelectionCells()); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = ((value != null) ? value : '100') + ' %'; + } + + mxEvent.consume(evt); + }); + + var input = this.addUnitInput(div, '%', 20, width, update, 10, -15, handler != null); + + if (key != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || input != document.activeElement) + { + var ss = this.format.getSelectionState(); + var tmp = parseInt(mxUtils.getValue(ss.style, key, 100)); + input.value = (isNaN(tmp)) ? '' : tmp + ' %'; + } + }); + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + listener(null, null, true); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + if (init != null) + { + init(input); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.addLabel = function(div, title, right, width) +{ + width = (width != null) ? width : 61; + + var label = document.createElement('div'); + mxUtils.write(label, title); + label.style.position = 'absolute'; + label.style.right = right + 'px'; + label.style.width = width + 'px'; + label.style.marginTop = '6px'; + label.style.textAlign = 'center'; + div.appendChild(label); +}; + +/** + * + */ +BaseFormatPanel.prototype.addKeyHandler = function(input, listener) +{ + mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e) + { + if (e.keyCode == 13) + { + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + if (listener != null) + { + listener(null, null, true); + } + + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + })); +}; + +/** + * + */ +BaseFormatPanel.prototype.styleButtons = function(elts) +{ + for (var i = 0; i < elts.length; i++) + { + mxUtils.setPrefixedStyle(elts[i].style, 'borderRadius', '3px'); + mxUtils.setOpacity(elts[i], 100); + elts[i].style.border = '1px solid #a0a0a0'; + elts[i].style.padding = '4px'; + elts[i].style.paddingTop = '3px'; + elts[i].style.paddingRight = '1px'; + elts[i].style.margin = '1px'; + elts[i].style.width = '24px'; + elts[i].style.height = '20px'; + elts[i].className += ' geColorBtn'; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +BaseFormatPanel.prototype.destroy = function() +{ + if (this.listeners != null) + { + for (var i = 0; i < this.listeners.length; i++) + { + this.listeners[i].destroy(); + } + + this.listeners = null; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(ArrangePanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel.prototype.init = function() +{ + var graph = this.editorUi.editor.graph; + var ss = this.format.getSelectionState(); + + this.container.appendChild(this.addLayerOps(this.createPanel())); + // Special case that adds two panels + this.addGeometry(this.container); + this.addEdgeGeometry(this.container); + + if (!ss.containsLabel || ss.edges.length == 0) + { + this.container.appendChild(this.addAngle(this.createPanel())); + } + + if (!ss.containsLabel && ss.edges.length == 0) + { + this.container.appendChild(this.addFlip(this.createPanel())); + } + + if (ss.vertices.length > 1) + { + this.container.appendChild(this.addAlign(this.createPanel())); + this.container.appendChild(this.addDistribute(this.createPanel())); + } + + this.container.appendChild(this.addGroupOps(this.createPanel())); +}; + +/** + * + */ +ArrangePanel.prototype.addLayerOps = function(div) +{ + var ui = this.editorUi; + + var btn = mxUtils.button(mxResources.get('toFront'), function(evt) + { + ui.actions.get('toFront').funct(); + }) + + btn.setAttribute('title', mxResources.get('toFront') + ' (' + this.editorUi.actions.get('toFront').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('toBack'), function(evt) + { + ui.actions.get('toBack').funct(); + }) + + btn.setAttribute('title', mxResources.get('toBack') + ' (' + this.editorUi.actions.get('toBack').shortcut + ')'); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addGroupOps = function(div) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var cell = graph.getSelectionCell(); + var ss = this.format.getSelectionState(); + var count = 0; + var btn = null; + + div.style.paddingTop = '8px'; + div.style.paddingBottom = '6px'; + + if (graph.getSelectionCount() > 1) + { + btn = mxUtils.button(mxResources.get('group'), function(evt) + { + ui.actions.get('group').funct(); + }) + + btn.setAttribute('title', mxResources.get('group') + ' (' + this.editorUi.actions.get('group').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && + graph.getModel().getChildCount(cell) > 0) + { + btn = mxUtils.button(mxResources.get('ungroup'), function(evt) + { + ui.actions.get('ungroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('ungroup') + ' (' + + this.editorUi.actions.get('ungroup').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (ss.vertices.length > 0) + { + if (count > 0) + { + mxUtils.br(div); + count = 0; + } + + var btn = mxUtils.button(mxResources.get('copySize'), function(evt) + { + ui.actions.get('copySize').funct(); + }); + + btn.setAttribute('title', mxResources.get('copySize') + ' (' + + this.editorUi.actions.get('copySize').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + count++; + + if (ui.copiedSize != null) + { + var btn2 = mxUtils.button(mxResources.get('pasteSize'), function(evt) + { + ui.actions.get('pasteSize').funct(); + }); + + btn2.setAttribute('title', mxResources.get('pasteSize') + ' (' + + this.editorUi.actions.get('pasteSize').shortcut + ')'); + + div.appendChild(btn2); + count++; + + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + btn2.style.width = '100px'; + btn2.style.marginBottom = '2px'; + } + } + + if (graph.getSelectionCount() == 1 && graph.getModel().isVertex(cell) && + graph.getModel().isVertex(graph.getModel().getParent(cell))) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('removeFromGroup'), function(evt) + { + ui.actions.get('removeFromGroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('removeFromGroup')); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() > 0) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('clearWaypoints'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearWaypoints').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearWaypoints') + ' (' + this.editorUi.actions.get('clearWaypoints').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + count++; + } + + if (graph.getSelectionCount() == 1) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + + btn = mxUtils.button(mxResources.get('editLink'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editLink').funct(); + })); + + btn.setAttribute('title', mxResources.get('editLink')); + btn.style.width = '100px'; + btn.style.marginLeft = '2px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (count == 0) + { + div.style.display = 'none'; + } + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAlign = function(div) +{ + var graph = this.editorUi.editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + div.appendChild(this.createTitle(mxResources.get('align'))); + + var stylePanel = document.createElement('div'); + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '0px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + div.style.height = '60px'; + } + + var left = this.editorUi.toolbar.addButton('geSprite-alignleft', mxResources.get('left'), + function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, stylePanel); + var center = this.editorUi.toolbar.addButton('geSprite-aligncenter', mxResources.get('center'), + function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, stylePanel); + var right = this.editorUi.toolbar.addButton('geSprite-alignright', mxResources.get('right'), + function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, stylePanel); + + var top = this.editorUi.toolbar.addButton('geSprite-aligntop', mxResources.get('top'), + function() { graph.alignCells(mxConstants.ALIGN_TOP); }, stylePanel); + var middle = this.editorUi.toolbar.addButton('geSprite-alignmiddle', mxResources.get('middle'), + function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, stylePanel); + var bottom = this.editorUi.toolbar.addButton('geSprite-alignbottom', mxResources.get('bottom'), + function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, stylePanel); + + this.styleButtons([left, center, right, top, middle, bottom]); + right.style.marginRight = '6px'; + div.appendChild(stylePanel); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addFlip = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '10px'; + + var span = document.createElement('div'); + span.style.marginTop = '2px'; + span.style.marginBottom = '8px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('flip')); + div.appendChild(span); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addDistribute = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + + div.appendChild(this.createTitle(mxResources.get('distribute'))); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.distributeCells(true); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.distributeCells(false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAngle = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + + var input = null; + var update = null; + var btn = null; + + if (ss.edges.length == 0) + { + mxUtils.write(span, mxResources.get('angle')); + div.appendChild(span); + + input = this.addUnitInput(div, '°', 20, 44, function() + { + update.apply(this, arguments); + }); + + mxUtils.br(div); + div.style.paddingTop = '10px'; + } + else + { + div.style.paddingTop = '8px'; + } + + if (!ss.containsLabel) + { + var label = mxResources.get('reverse'); + + if (ss.vertices.length > 0 && ss.edges.length > 0) + { + label = mxResources.get('turn') + ' / ' + label; + } + else if (ss.vertices.length > 0) + { + label = mxResources.get('turn'); + } + + btn = mxUtils.button(label, function(evt) + { + ui.actions.get('turn').funct(); + }) + + btn.setAttribute('title', label + ' (' + this.editorUi.actions.get('turn').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + if (input != null) + { + btn.style.marginTop = '8px'; + } + } + + if (input != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || document.activeElement != input) + { + ss = this.format.getSelectionState(); + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0)); + input.value = (isNaN(tmp)) ? '' : tmp + '°'; + } + }); + + update = this.installInputHandler(input, mxConstants.STYLE_ROTATION, 0, 0, 360, '°', null, true); + this.addKeyHandler(input, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addGeometry = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '50px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('size')); + div.appendChild(span); + + var widthUpdate, heightUpdate, leftUpdate, topUpdate; + var width = this.addUnitInput(div, 'pt', 84, 44, function() + { + widthUpdate.apply(this, arguments); + }); + var height = this.addUnitInput(div, 'pt', 20, 44, function() + { + heightUpdate.apply(this, arguments); + }); + + var autosizeBtn = document.createElement('div'); + autosizeBtn.className = 'geSprite geSprite-fit'; + autosizeBtn.setAttribute('title', mxResources.get('autosize') + ' (' + this.editorUi.actions.get('autosize').shortcut + ')'); + autosizeBtn.style.position = 'relative'; + autosizeBtn.style.cursor = 'pointer'; + autosizeBtn.style.marginTop = '-3px'; + autosizeBtn.style.border = '0px'; + autosizeBtn.style.left = '52px'; + mxUtils.setOpacity(autosizeBtn, 50); + + mxEvent.addListener(autosizeBtn, 'mouseenter', function() + { + mxUtils.setOpacity(autosizeBtn, 100); + }); + + mxEvent.addListener(autosizeBtn, 'mouseleave', function() + { + mxUtils.setOpacity(autosizeBtn, 50); + }); + + mxEvent.addListener(autosizeBtn, 'click', function() + { + ui.actions.get('autosize').funct(); + }); + + div.appendChild(autosizeBtn); + this.addLabel(div, mxResources.get('width'), 84); + this.addLabel(div, mxResources.get('height'), 20); + mxUtils.br(div); + + var wrapper = document.createElement('div'); + wrapper.style.paddingTop = '8px'; + wrapper.style.paddingRight = '20px'; + wrapper.style.whiteSpace = 'nowrap'; + wrapper.style.textAlign = 'right'; + var opt = this.createCellOption(mxResources.get('constrainProportions'), + mxConstants.STYLE_ASPECT, null, 'fixed', 'null'); + opt.style.width = '100%'; + wrapper.appendChild(opt); + div.appendChild(wrapper); + + var constrainCheckbox = opt.getElementsByTagName('input')[0]; + this.addKeyHandler(width, listener); + this.addKeyHandler(height, listener); + + widthUpdate = this.addGeometryHandler(width, function(geo, value) + { + if (geo.width > 0) + { + var value = Math.max(1, value); + + if (constrainCheckbox.checked) + { + geo.height = Math.round((geo.height * value * 100) / geo.width) / 100; + } + + geo.width = value; + } + }); + heightUpdate = this.addGeometryHandler(height, function(geo, value) + { + if (geo.height > 0) + { + var value = Math.max(1, value); + + if (constrainCheckbox.checked) + { + geo.width = Math.round((geo.width * value * 100) / geo.height) / 100; + } + + geo.height = value; + } + }); + + container.appendChild(div); + + var div2 = this.createPanel(); + div2.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('position')); + div2.appendChild(span); + + var left = this.addUnitInput(div2, 'pt', 84, 44, function() + { + leftUpdate.apply(this, arguments); + }); + var top = this.addUnitInput(div2, 'pt', 20, 44, function() + { + topUpdate.apply(this, arguments); + }); + + mxUtils.br(div2); + this.addLabel(div2, mxResources.get('left'), 84); + this.addLabel(div2, mxResources.get('top'), 20); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + + if (!rect.containsLabel && rect.vertices.length == graph.getSelectionCount() && + rect.width != null && rect.height != null) + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + width.value = rect.width + ((rect.width == '') ? '' : ' pt'); + } + + if (force || document.activeElement != height) + { + height.value = rect.height + ((rect.height == '') ? '' : ' pt'); + } + } + else + { + div.style.display = 'none'; + } + + if (rect.vertices.length == graph.getSelectionCount() && + rect.x != null && rect.y != null) + { + div2.style.display = ''; + + if (force || document.activeElement != left) + { + left.value = rect.x + ((rect.x == '') ? '' : ' pt'); + } + + if (force || document.activeElement != top) + { + top.value = rect.y + ((rect.y == '') ? '' : ' pt'); + } + } + else + { + div2.style.display = 'none'; + } + }); + + this.addKeyHandler(left, listener); + this.addKeyHandler(top, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + leftUpdate = this.addGeometryHandler(left, function(geo, value) + { + if (geo.relative) + { + geo.offset.x = value; + } + else + { + geo.x = value; + } + }); + topUpdate = this.addGeometryHandler(top, function(geo, value) + { + if (geo.relative) + { + geo.offset.y = value; + } + else + { + geo.y = value; + } + }); + + container.appendChild(div2); +}; + +/** + * + */ +ArrangePanel.prototype.addGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' pt'; + } + else if (isNaN(value)) + { + input.value = initialValue + ' pt'; + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +ArrangePanel.prototype.addEdgeGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (isNaN(value)) + { + input.value = initialValue + ' pt'; + } + else if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isEdge(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' pt'; + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +/** + * + */ +ArrangePanel.prototype.addEdgeGeometry = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('width')); + div.appendChild(span); + + var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate; + var width = this.addUnitInput(div, 'pt', 20, 44, function() + { + widthUpdate.apply(this, arguments); + }); + + mxUtils.br(div); + this.addKeyHandler(width, listener); + + function widthUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(width.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(rect.style, 'width', mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth)) + { + graph.setCellStyles('width', value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['width'], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + width.value = value + ' pt'; + mxEvent.consume(evt); + }; + + mxEvent.addListener(width, 'blur', widthUpdate); + mxEvent.addListener(width, 'change', widthUpdate); + + container.appendChild(div); + + var divs = this.createPanel(); + divs.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'Start'); + divs.appendChild(span); + + var xs = this.addUnitInput(divs, 'pt', 84, 44, function() + { + xsUpdate.apply(this, arguments); + }); + var ys = this.addUnitInput(divs, 'pt', 20, 44, function() + { + ysUpdate.apply(this, arguments); + }); + + mxUtils.br(divs); + this.addLabel(divs, mxResources.get('left'), 84); + this.addLabel(divs, mxResources.get('top'), 20); + container.appendChild(divs); + this.addKeyHandler(xs, listener); + this.addKeyHandler(ys, listener); + + var divt = this.createPanel(); + divt.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'End'); + divt.appendChild(span); + + var xt = this.addUnitInput(divt, 'pt', 84, 44, function() + { + xtUpdate.apply(this, arguments); + }); + var yt = this.addUnitInput(divt, 'pt', 20, 44, function() + { + ytUpdate.apply(this, arguments); + }); + + mxUtils.br(divt); + this.addLabel(divt, mxResources.get('left'), 84); + this.addLabel(divt, mxResources.get('top'), 20); + container.appendChild(divt); + this.addKeyHandler(xt, listener); + this.addKeyHandler(yt, listener); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + var cell = graph.getSelectionCell(); + + if (rect.style.shape == 'link' || rect.style.shape == 'flexArrow') + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + var value = mxUtils.getValue(rect.style, 'width', + mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth); + width.value = value + ' pt'; + } + } + else + { + div.style.display = 'none'; + } + + if (graph.getSelectionCount() == 1 && graph.model.isEdge(cell)) + { + var geo = graph.model.getGeometry(cell); + + if (geo.sourcePoint != null && graph.model.getTerminal(cell, true) == null) + { + xs.value = geo.sourcePoint.x; + ys.value = geo.sourcePoint.y; + } + else + { + divs.style.display = 'none'; + } + + if (geo.targetPoint != null && graph.model.getTerminal(cell, false) == null) + { + xt.value = geo.targetPoint.x; + yt.value = geo.targetPoint.y; + } + else + { + divt.style.display = 'none'; + } + } + else + { + divs.style.display = 'none'; + divt.style.display = 'none'; + } + }); + + xsUpdate = this.addEdgeGeometryHandler(xs, function(geo, value) + { + geo.sourcePoint.x = value; + }); + + ysUpdate = this.addEdgeGeometryHandler(ys, function(geo, value) + { + geo.sourcePoint.y = value; + }); + + xtUpdate = this.addEdgeGeometryHandler(xt, function(geo, value) + { + geo.targetPoint.x = value; + }); + + ytUpdate = this.addEdgeGeometryHandler(yt, function(geo, value) + { + geo.targetPoint.y = value; + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(TextFormatPanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.init = function() +{ + this.container.style.borderBottom = 'none'; + this.addFont(this.container); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.addFont = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var title = this.createTitle(mxResources.get('font')); + title.style.paddingLeft = '18px'; + title.style.paddingTop = '10px'; + title.style.paddingBottom = '6px'; + container.appendChild(title); + + var stylePanel = this.createPanel(); + stylePanel.style.paddingTop = '2px'; + stylePanel.style.paddingBottom = '2px'; + stylePanel.style.position = 'relative'; + stylePanel.style.marginLeft = '-2px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + stylePanel.style.display = 'block'; + } + + if (graph.cellEditor.isContentEditing()) + { + var cssPanel = stylePanel.cloneNode(); + + var cssMenu = this.editorUi.toolbar.addMenu(mxResources.get('style'), + mxResources.get('style'), true, 'formatBlock', cssPanel); + cssMenu.style.color = 'rgb(112, 112, 112)'; + cssMenu.style.whiteSpace = 'nowrap'; + cssMenu.style.overflow = 'hidden'; + cssMenu.style.margin = '0px'; + this.addArrow(cssMenu); + cssMenu.style.width = '192px'; + cssMenu.style.height = '15px'; + + var arrow = cssMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + container.appendChild(cssPanel); + + // Workaround for offset in FF + if (mxClient.IS_FF) + { + cssMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; + } + } + + container.appendChild(stylePanel); + + var colorPanel = this.createPanel(); + colorPanel.style.marginTop = '8px'; + colorPanel.style.borderTop = '1px solid #c0c0c0'; + colorPanel.style.paddingTop = '6px'; + colorPanel.style.paddingBottom = '6px'; + + var fontMenu = this.editorUi.toolbar.addMenu('Helvetica', mxResources.get('fontFamily'), true, 'fontFamily', stylePanel); + fontMenu.style.color = 'rgb(112, 112, 112)'; + fontMenu.style.whiteSpace = 'nowrap'; + fontMenu.style.overflow = 'hidden'; + fontMenu.style.margin = '0px'; + + this.addArrow(fontMenu); + fontMenu.style.width = '192px'; + fontMenu.style.height = '15px'; + + // Workaround for offset in FF + if (mxClient.IS_FF) + { + fontMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; + } + + var stylePanel2 = stylePanel.cloneNode(false); + stylePanel2.style.marginLeft = '-3px'; + var fontStyleItems = this.editorUi.toolbar.addItems(['bold', 'italic', 'underline'], stylePanel2, true); + fontStyleItems[0].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); + fontStyleItems[1].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); + fontStyleItems[2].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); + + var verticalItem = this.editorUi.toolbar.addItems(['vertical'], stylePanel2, true)[0]; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel2); + + this.styleButtons(fontStyleItems); + this.styleButtons([verticalItem]); + + var stylePanel3 = stylePanel.cloneNode(false); + stylePanel3.style.marginLeft = '-3px'; + stylePanel3.style.paddingBottom = '0px'; + + // Helper function to return a wrapper function does not pass any arguments + var callFn = function(fn) + { + return function() + { + return fn(); + }; + }; + + var left = this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifyleft', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT])), stylePanel3); + var center = this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifycenter', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER])), stylePanel3); + var right = this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifyright', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT])), stylePanel3); + + this.styleButtons([left, center, right]); + + if (graph.cellEditor.isContentEditing()) + { + var clear = this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('removeFormat'), + function() + { + document.execCommand('removeformat', false, null); + }, stylePanel2); + this.styleButtons([clear]); + } + + var top = this.editorUi.toolbar.addButton('geSprite-top', mxResources.get('top'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP])), stylePanel3); + var middle = this.editorUi.toolbar.addButton('geSprite-middle', mxResources.get('middle'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE])), stylePanel3); + var bottom = this.editorUi.toolbar.addButton('geSprite-bottom', mxResources.get('bottom'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM])), stylePanel3); + + this.styleButtons([top, middle, bottom]); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel3); + + // Hack for updating UI state below based on current text selection + // currentTable is the current selected DOM table updated below + var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow; + + if (graph.cellEditor.isContentEditing()) + { + top.style.display = 'none'; + middle.style.display = 'none'; + bottom.style.display = 'none'; + verticalItem.style.display = 'none'; + + full = this.editorUi.toolbar.addButton('geSprite-justifyfull', null, + function() + { + document.execCommand('justifyfull', false, null); + }, stylePanel3); + this.styleButtons([full, + sub = this.editorUi.toolbar.addButton('geSprite-subscript', + mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)', + function() + { + document.execCommand('subscript', false, null); + }, stylePanel3), sup = this.editorUi.toolbar.addButton('geSprite-superscript', + mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)', + function() + { + document.execCommand('superscript', false, null); + }, stylePanel3)]); + full.style.marginRight = '9px'; + + var tmp = stylePanel3.cloneNode(false); + tmp.style.paddingTop = '4px'; + var btns = [this.editorUi.toolbar.addButton('geSprite-orderedlist', mxResources.get('numberedList'), + function() + { + document.execCommand('insertorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-unorderedlist', mxResources.get('bulletedList'), + function() + { + document.execCommand('insertunorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-outdent', mxResources.get('decreaseIndent'), + function() + { + document.execCommand('outdent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-indent', mxResources.get('increaseIndent'), + function() + { + document.execCommand('indent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-code', mxResources.get('html'), + function() + { + graph.cellEditor.toggleViewMode(); + }, tmp)]; + this.styleButtons(btns); + btns[btns.length - 1].style.marginLeft = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + tmp.style.height = '40'; + } + + container.appendChild(tmp); + } + else + { + fontStyleItems[2].style.marginRight = '9px'; + right.style.marginRight = '9px'; + } + + // Label position + var stylePanel4 = stylePanel.cloneNode(false); + stylePanel4.style.marginLeft = '0px'; + stylePanel4.style.paddingTop = '8px'; + stylePanel4.style.paddingBottom = '4px'; + stylePanel4.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel4, mxResources.get('position')); + + // Adds label position options + var positionSelect = document.createElement('select'); + positionSelect.style.position = 'absolute'; + positionSelect.style.right = '20px'; + positionSelect.style.width = '97px'; + positionSelect.style.marginTop = '-2px'; + + var directions = ['topLeft', 'top', 'topRight', 'left', 'center', 'right', 'bottomLeft', 'bottom', 'bottomRight']; + var lset = {'topLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM], + 'top': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM], + 'topRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM], + 'left': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE], + 'center': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE], + 'right': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE], + 'bottomLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP], + 'bottom': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP], + 'bottomRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP]}; + + for (var i = 0; i < directions.length; i++) + { + var positionOption = document.createElement('option'); + positionOption.setAttribute('value', directions[i]); + mxUtils.write(positionOption, mxResources.get(directions[i])); + positionSelect.appendChild(positionOption); + } + + stylePanel4.appendChild(positionSelect); + + // Writing direction + var stylePanel5 = stylePanel.cloneNode(false); + stylePanel5.style.marginLeft = '0px'; + stylePanel5.style.paddingTop = '4px'; + stylePanel5.style.paddingBottom = '4px'; + stylePanel5.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel5, mxResources.get('writingDirection')); + + // Adds writing direction options + // LATER: Handle reselect of same option in all selects (change event + // is not fired for same option so have opened state on click) and + // handle multiple different styles for current selection + var dirSelect = document.createElement('select'); + dirSelect.style.position = 'absolute'; + dirSelect.style.right = '20px'; + dirSelect.style.width = '97px'; + dirSelect.style.marginTop = '-2px'; + + // NOTE: For automatic we use the value null since automatic + // requires the text to be non formatted and non-wrapped + var dirs = ['automatic', 'leftToRight', 'rightToLeft']; + var dirSet = {'automatic': null, + 'leftToRight': mxConstants.TEXT_DIRECTION_LTR, + 'rightToLeft': mxConstants.TEXT_DIRECTION_RTL}; + + for (var i = 0; i < dirs.length; i++) + { + var dirOption = document.createElement('option'); + dirOption.setAttribute('value', dirs[i]); + mxUtils.write(dirOption, mxResources.get(dirs[i])); + dirSelect.appendChild(dirOption); + } + + stylePanel5.appendChild(dirSelect); + + if (!graph.isEditing()) + { + container.appendChild(stylePanel4); + + mxEvent.addListener(positionSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var vals = lset[positionSelect.value]; + + if (vals != null) + { + graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, vals[0], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, vals[1], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, vals[3], graph.getSelectionCells()); + } + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // LATER: Update dir in text editor while editing and update style with label + // NOTE: The tricky part is handling and passing on the auto value + container.appendChild(stylePanel5); + + mxEvent.addListener(dirSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_TEXT_DIRECTION, dirSet[dirSelect.value], graph.getSelectionCells()); + mxEvent.consume(evt); + }); + } + + // Font size + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + } + + input.style.width = '46px'; + input.style.height = (mxClient.IS_QUIRKS) ? '21px' : '17px'; + stylePanel2.appendChild(input); + + // Workaround for font size 4 if no text is selected is update font size below + // after first character was entered (as the font element is lazy created) + var pendingFontSize = null; + + var inputUpdate = this.installInputHandler(input, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize, 1, 999, ' pt', + function(fontSize) + { + // IE does not support containsNode + // KNOWN: Fixes font size issues but bypasses undo + if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + var container = (selection.rangeCount > 0) ? selection.getRangeAt(0).commonAncestorContainer : + graph.cellEditor.textarea; + + function updateSize(elt, ignoreContains) + { + if (elt != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(elt) && + (ignoreContains || selection.containsNode(elt, true))) + { + if (elt.nodeName == 'FONT') + { + elt.removeAttribute('size'); + elt.style.fontSize = fontSize + 'px'; + } + else + { + var css = mxUtils.getCurrentStyle(elt); + + if (css.fontSize != fontSize + 'px') + { + if (mxUtils.getCurrentStyle(elt.parentNode).fontSize != fontSize + 'px') + { + elt.style.fontSize = fontSize + 'px'; + } + else + { + elt.style.fontSize = ''; + } + } + } + } + }; + + // Wraps text node or mixed selection with leading text in a font element + if (container == graph.cellEditor.textarea || + container.nodeType != mxConstants.NODETYPE_ELEMENT) + { + document.execCommand('fontSize', false, '1'); + } + + if (container != graph.cellEditor.textarea) + { + container = container.parentNode; + } + + if (container.nodeType == mxConstants.NODETYPE_ELEMENT) + { + var elts = container.getElementsByTagName('*'); + updateSize(container); + + for (var i = 0; i < elts.length; i++) + { + updateSize(elts[i]); + } + } + + input.value = fontSize + ' pt'; + } + else if (window.getSelection || document.selection) + { + // Checks selection + var par = null; + + if (document.selection) + { + par = document.selection.createRange().parentElement(); + } + else + { + var selection = window.getSelection(); + + if (selection.rangeCount > 0) + { + par = selection.getRangeAt(0).commonAncestorContainer; + } + } + + // Node.contains does not work for text nodes in IE11 + function isOrContains(container, node) + { + while (node != null) + { + if (node === container) + { + return true; + } + + node = node.parentNode; + } + + return false; + }; + + if (par != null && isOrContains(graph.cellEditor.textarea, par)) + { + pendingFontSize = fontSize; + + // Workaround for can't set font size in px is to change font size afterwards + document.execCommand('fontSize', false, '4'); + var elts = graph.cellEditor.textarea.getElementsByTagName('font'); + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].getAttribute('size') == '4') + { + elts[i].removeAttribute('size'); + elts[i].style.fontSize = pendingFontSize + 'px'; + + // Overrides fontSize in input with the one just assigned as a workaround + // for potential fontSize values of parent elements that don't match + window.setTimeout(function() + { + input.value = pendingFontSize + ' pt'; + pendingFontSize = null; + }, 0); + + break; + } + } + } + } + }, true); + + var stepper = this.createStepper(input, inputUpdate, 1, 10, true, Menus.prototype.defaultFontSize); + stepper.style.display = input.style.display; + stepper.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + stepper.style.right = '20px'; + } + + stylePanel2.appendChild(stepper); + + var arrow = fontMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + + var bgColorApply = null; + var currentBgColor = '#ffffff'; + + var fontColorApply = null; + var currentFontColor = '#000000'; + + var bgPanel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('backgroundColor'), function() + { + return currentBgColor; + }, function(color) + { + document.execCommand('backcolor', false, (color != mxConstants.NONE) ? color : 'transparent'); + }, '#ffffff', + { + install: function(apply) { bgColorApply = apply; }, + destroy: function() { bgColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('backgroundColor'), mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, '#ffffff', null, function(color) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.backgroundColor = null; + }); + }); + bgPanel.style.fontWeight = 'bold'; + + var borderPanel = this.createCellColorOption(mxResources.get('borderColor'), mxConstants.STYLE_LABEL_BORDERCOLOR, '#000000'); + borderPanel.style.fontWeight = 'bold'; + + var panel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('fontColor'), function() + { + return currentFontColor; + }, function(color) + { + document.execCommand('forecolor', false, (color != mxConstants.NONE) ? color : 'transparent'); + }, '#000000', + { + install: function(apply) { fontColorApply = apply; }, + destroy: function() { fontColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('fontColor'), mxConstants.STYLE_FONTCOLOR, '#000000', function(color) + { + if (color == null || color == mxConstants.NONE) + { + bgPanel.style.display = 'none'; + } + else + { + bgPanel.style.display = ''; + } + + borderPanel.style.display = bgPanel.style.display; + }, function(color) + { + if (color == null || color == mxConstants.NONE) + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, '1', graph.getSelectionCells()); + } + else + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, graph.getSelectionCells()); + } + + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.removeAttribute('color'); + elt.style.color = null; + }); + }); + panel.style.fontWeight = 'bold'; + + colorPanel.appendChild(panel); + colorPanel.appendChild(bgPanel); + + if (!graph.cellEditor.isContentEditing()) + { + colorPanel.appendChild(borderPanel); + } + + container.appendChild(colorPanel); + + var extraPanel = this.createPanel(); + extraPanel.style.paddingTop = '2px'; + extraPanel.style.paddingBottom = '4px'; + + // LATER: Fix toggle using '' instead of 'null' + var wwOpt = this.createCellOption(mxResources.get('wordWrap'), mxConstants.STYLE_WHITE_SPACE, null, 'wrap', 'null', null, null, true); + wwOpt.style.fontWeight = 'bold'; + + // Word wrap in edge labels only supported via labelWidth style + if (!ss.containsLabel && !ss.autoSize && ss.edges.length == 0) + { + extraPanel.appendChild(wwOpt); + } + + // Delegates switch of style to formattedText action as it also convertes newlines + var htmlOpt = this.createCellOption(mxResources.get('formattedText'), 'html', '0', + null, null, null, ui.actions.get('formattedText')); + htmlOpt.style.fontWeight = 'bold'; + extraPanel.appendChild(htmlOpt); + + var spacingPanel = this.createPanel(); + spacingPanel.style.paddingTop = '10px'; + spacingPanel.style.paddingBottom = '28px'; + spacingPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('spacing')); + spacingPanel.appendChild(span); + + var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate; + var topSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + topUpdate.apply(this, arguments); + }); + var globalSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + globalUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('top'), 91); + this.addLabel(spacingPanel, mxResources.get('global'), 20); + mxUtils.br(spacingPanel); + mxUtils.br(spacingPanel); + + var leftSpacing = this.addUnitInput(spacingPanel, 'pt', 162, 44, function() + { + leftUpdate.apply(this, arguments); + }); + var bottomSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + bottomUpdate.apply(this, arguments); + }); + var rightSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + rightUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('left'), 162); + this.addLabel(spacingPanel, mxResources.get('bottom'), 91); + this.addLabel(spacingPanel, mxResources.get('right'), 20); + + if (!graph.cellEditor.isContentEditing()) + { + container.appendChild(extraPanel); + container.appendChild(this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_TEXT_OPACITY)); + container.appendChild(spacingPanel); + } + else + { + var selState = null; + var lineHeightInput = null; + + container.appendChild(this.createRelativeOption(mxResources.get('lineheight'), null, null, function(input) + { + var value = (input.value == '') ? 120 : parseInt(input.value); + value = Math.max(0, (isNaN(value)) ? 120 : value); + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null && node == graph.cellEditor.textarea && graph.cellEditor.textarea.firstChild != null) + { + if (graph.cellEditor.textarea.firstChild.nodeName != 'P') + { + graph.cellEditor.textarea.innerHTML = '

' + graph.cellEditor.textarea.innerHTML + '

'; + } + + node = graph.cellEditor.textarea.firstChild; + } + + if (node != null && node != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(node)) + { + node.style.lineHeight = value + '%'; + } + + input.value = value + ' %'; + }, function(input) + { + // Used in CSS handler to update current value + lineHeightInput = input; + + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + input.value = '120 %'; + })); + + var insertPanel = stylePanel.cloneNode(false); + insertPanel.style.paddingLeft = '0px'; + var insertBtns = this.editorUi.toolbar.addItems(['link', 'image'], insertPanel, true); + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-horizontalrule', mxResources.get('insertHorizontalRule'), + function() + { + document.execCommand('inserthorizontalrule', false); + }, insertPanel), + this.editorUi.toolbar.addMenuFunctionInContainer(insertPanel, 'geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.addInsertTableItem(menu); + }))]; + this.styleButtons(insertBtns); + this.styleButtons(btns); + + var wrapper2 = this.createPanel(); + wrapper2.style.paddingTop = '10px'; + wrapper2.style.paddingBottom = '10px'; + wrapper2.appendChild(this.createTitle(mxResources.get('insert'))); + wrapper2.appendChild(insertPanel); + container.appendChild(wrapper2); + + if (mxClient.IS_QUIRKS) + { + wrapper2.style.height = '70'; + } + + var tablePanel = stylePanel.cloneNode(false); + tablePanel.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex : 0)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex + 1 : -1)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableCell != null) + { + graph.deleteColumn(currentTable, tableCell.cellIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex + 1)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.deleteRow(currentTable, tableRow.sectionRowIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + var wrapper3 = this.createPanel(); + wrapper3.style.paddingTop = '10px'; + wrapper3.style.paddingBottom = '10px'; + wrapper3.appendChild(this.createTitle(mxResources.get('table'))); + wrapper3.appendChild(tablePanel); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + wrapper3.style.height = '70'; + } + + var tablePanel2 = stylePanel.cloneNode(false); + tablePanel2.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-strokecolor', mxResources.get('borderColor'), + mxUtils.bind(this, function() + { + if (currentTable != null) + { + // Converts rgb(r,g,b) values + var color = currentTable.style.borderColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + currentTable.removeAttribute('border'); + currentTable.style.border = ''; + currentTable.style.borderCollapse = ''; + } + else + { + currentTable.setAttribute('border', '1'); + currentTable.style.border = '1px solid ' + newColor; + currentTable.style.borderCollapse = 'collapse'; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fillcolor', mxResources.get('backgroundColor'), + mxUtils.bind(this, function() + { + // Converts rgb(r,g,b) values + if (currentTable != null) + { + var color = currentTable.style.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + currentTable.style.backgroundColor = ''; + } + else + { + currentTable.style.backgroundColor = newColor; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fit', mxResources.get('spacing'), + function() + { + if (currentTable != null) + { + var value = currentTable.getAttribute('cellPadding') || 0; + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + if (newValue != null && newValue.length > 0) + { + currentTable.setAttribute('cellPadding', newValue); + } + else + { + currentTable.removeAttribute('cellPadding'); + } + }), mxResources.get('spacing')); + ui.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'left'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'center'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'right'); + } + }, tablePanel2)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(wrapper3); + mxUtils.br(wrapper3); + } + + wrapper3.appendChild(tablePanel2); + container.appendChild(wrapper3); + + tableWrapper = wrapper3; + } + + function setSelected(elt, selected) + { + if (mxClient.IS_IE && (mxClient.IS_QUIRKS || document.documentMode < 10)) + { + elt.style.filter = (selected) ? 'progid:DXImageTransform.Microsoft.Gradient('+ + 'StartColorStr=\'#c5ecff\', EndColorStr=\'#87d4fb\', GradientType=0)' : ''; + } + else + { + elt.style.backgroundImage = (selected) ? 'linear-gradient(#c5ecff 0px,#87d4fb 100%)' : ''; + } + }; + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0); + setSelected(fontStyleItems[0], (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD); + setSelected(fontStyleItems[1], (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC); + setSelected(fontStyleItems[2], (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE); + fontMenu.firstChild.nodeValue = mxUtils.htmlEntities(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTFAMILY, Menus.prototype.defaultFont)); + + setSelected(verticalItem, mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, '1') == '0'); + + if (force || document.activeElement != input) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + var align = mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); + setSelected(left, align == mxConstants.ALIGN_LEFT); + setSelected(center, align == mxConstants.ALIGN_CENTER); + setSelected(right, align == mxConstants.ALIGN_RIGHT); + + var valign = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); + setSelected(top, valign == mxConstants.ALIGN_TOP); + setSelected(middle, valign == mxConstants.ALIGN_MIDDLE); + setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM); + + var pos = mxUtils.getValue(ss.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + var vpos = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE); + + if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'top'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topRight'; + } + else if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottom'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomRight'; + } + else if (pos == mxConstants.ALIGN_LEFT) + { + positionSelect.value = 'left'; + } + else if (pos == mxConstants.ALIGN_RIGHT) + { + positionSelect.value = 'right'; + } + else + { + positionSelect.value = 'center'; + } + + var dir = mxUtils.getValue(ss.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION); + + if (dir == mxConstants.TEXT_DIRECTION_RTL) + { + dirSelect.value = 'rightToLeft'; + } + else if (dir == mxConstants.TEXT_DIRECTION_LTR) + { + dirSelect.value = 'leftToRight'; + } + else if (dir == mxConstants.TEXT_DIRECTION_AUTO) + { + dirSelect.value = 'automatic'; + } + + if (force || document.activeElement != globalSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2)); + globalSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != topSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0)); + topSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != rightSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0)); + rightSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != bottomSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0)); + bottomSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != leftSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0)); + leftSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + globalUpdate = this.installInputHandler(globalSpacing, mxConstants.STYLE_SPACING, 2, -999, 999, ' pt'); + topUpdate = this.installInputHandler(topSpacing, mxConstants.STYLE_SPACING_TOP, 0, -999, 999, ' pt'); + rightUpdate = this.installInputHandler(rightSpacing, mxConstants.STYLE_SPACING_RIGHT, 0, -999, 999, ' pt'); + bottomUpdate = this.installInputHandler(bottomSpacing, mxConstants.STYLE_SPACING_BOTTOM, 0, -999, 999, ' pt'); + leftUpdate = this.installInputHandler(leftSpacing, mxConstants.STYLE_SPACING_LEFT, 0, -999, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(globalSpacing, listener); + this.addKeyHandler(topSpacing, listener); + this.addKeyHandler(rightSpacing, listener); + this.addKeyHandler(bottomSpacing, listener); + this.addKeyHandler(leftSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + if (graph.cellEditor.isContentEditing()) + { + var updating = false; + + var updateCssHandler = function() + { + if (!updating) + { + updating = true; + + window.setTimeout(function() + { + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null) + { + // Workaround for commonAncestor on range in IE11 returning parent of common ancestor + if (node == graph.cellEditor.textarea && graph.cellEditor.textarea.children.length == 1 && + graph.cellEditor.textarea.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) + { + node = graph.cellEditor.textarea.firstChild; + } + + function getRelativeLineHeight(fontSize, lineHeight, elt) + { + if (elt.style.lineHeight.substring(elt.style.lineHeight.length - 1) == '%') + { + return parseInt(elt.style.lineHeight) / 100; + } + else + { + return (lineHeight.substring(lineHeight.length - 2) == 'px') ? + parseFloat(lineHeight) / fontSize : parseInt(lineHeight); + } + }; + + function getAbsoluteFontSize(fontSize) + { + if (fontSize.substring(fontSize.length - 2) == 'px') + { + return parseFloat(fontSize); + } + else + { + return mxConstants.DEFAULT_FONTSIZE; + } + } + + //var realCss = mxUtils.getCurrentStyle(selectedElement); + var css = mxUtils.getCurrentStyle(node); + var fontSize = getAbsoluteFontSize(css.fontSize); + var lineHeight = getRelativeLineHeight(fontSize, css.lineHeight, node); + + // Finds common font size + var elts = node.getElementsByTagName('*'); + + // IE does not support containsNode + if (elts.length > 0 && window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + + for (var i = 0; i < elts.length; i++) + { + if (selection.containsNode(elts[i], true)) + { + temp = mxUtils.getCurrentStyle(elts[i]); + fontSize = Math.max(getAbsoluteFontSize(temp.fontSize), fontSize); + var lh = getRelativeLineHeight(fontSize, temp.lineHeight, elts[i]); + + if (lh != lineHeight || isNaN(lh)) + { + lineHeight = ''; + } + } + } + } + + if (css != null) + { + setSelected(fontStyleItems[0], css.fontWeight == 'bold' || graph.getParentByName(node, 'B', graph.cellEditor.textarea) != null); + setSelected(fontStyleItems[1], css.fontStyle == 'italic' || graph.getParentByName(node, 'I', graph.cellEditor.textarea) != null); + setSelected(fontStyleItems[2], graph.getParentByName(node, 'U', graph.cellEditor.textarea) != null); + setSelected(left, css.textAlign == 'left'); + setSelected(center, css.textAlign == 'center'); + setSelected(right, css.textAlign == 'right'); + setSelected(full, css.textAlign == 'justify'); + setSelected(sup, graph.getParentByName(node, 'SUP', graph.cellEditor.textarea) != null); + setSelected(sub, graph.getParentByName(node, 'SUB', graph.cellEditor.textarea) != null); + + currentTable = graph.getParentByName(node, 'TABLE', graph.cellEditor.textarea); + tableRow = (currentTable == null) ? null : graph.getParentByName(node, 'TR', currentTable); + tableCell = (currentTable == null) ? null : graph.getParentByName(node, 'TD', currentTable); + tableWrapper.style.display = (currentTable != null) ? '' : 'none'; + + if (document.activeElement != input) + { + if (node.nodeName == 'FONT' && node.getAttribute('size') == '4' && + pendingFontSize != null) + { + node.removeAttribute('size'); + node.style.fontSize = pendingFontSize + ' pt'; + pendingFontSize = null; + } + else + { + input.value = (isNaN(fontSize)) ? '' : fontSize + ' pt'; + } + + var lh = parseFloat(lineHeight); + + if (!isNaN(lh)) + { + lineHeightInput.value = Math.round(lh * 100) + ' %'; + } + else + { + lineHeightInput.value = '100 %'; + } + } + + // Converts rgb(r,g,b) values + var color = css.color.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + var color2 = css.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + + // Updates the color picker for the current font + if (fontColorApply != null) + { + if (color.charAt(0) == '#') + { + currentFontColor = color; + } + else + { + currentFontColor = '#000000'; + } + + fontColorApply(currentFontColor, true); + } + + if (bgColorApply != null) + { + if (color2.charAt(0) == '#') + { + currentBgColor = color2; + } + else + { + currentBgColor = null; + } + + bgColorApply(currentBgColor, true); + } + + // Workaround for firstChild is null or not an object + // in the log which seems to be IE8- only / 29.01.15 + if (fontMenu.firstChild != null) + { + // Strips leading and trailing quotes + var ff = css.fontFamily; + + if (ff.charAt(0) == '\'') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '\'') + { + ff = ff.substring(0, ff.length - 1); + } + + if (ff.charAt(0) == '"') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '"') + { + ff = ff.substring(0, ff.length - 1); + } + + fontMenu.firstChild.nodeValue = ff; + } + } + } + + updating = false; + }, 0); + } + }; + + mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) + mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); + this.listeners.push({destroy: function() + { + // No need to remove listener since textarea is destroyed after edit + }}); + updateCssHandler(); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(StyleFormatPanel, BaseFormatPanel); + +/** + * + */ +StyleFormatPanel.prototype.defaultStrokeColor = 'black'; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + if (ss.containsImage && ss.vertices.length == 1 && ss.style.shape == 'image' && + ss.style.image != null && ss.style.image.substring(0, 19) == 'data:image/svg+xml;') + { + this.container.appendChild(this.addSvgStyles(this.createPanel())); + } + + if (!ss.containsImage || ss.style.shape == 'image') + { + this.container.appendChild(this.addFill(this.createPanel())); + } + + this.container.appendChild(this.addStroke(this.createPanel())); + this.container.appendChild(this.addLineJumps(this.createPanel())); + var opacityPanel = this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_OPACITY, 41); + opacityPanel.style.paddingTop = '8px'; + opacityPanel.style.paddingBottom = '8px'; + this.container.appendChild(opacityPanel); + this.container.appendChild(this.addEffects(this.createPanel())); + var opsPanel = this.addEditOps(this.createPanel()); + + if (opsPanel.firstChild != null) + { + mxUtils.br(opsPanel); + } + + this.container.appendChild(this.addStyleOps(opsPanel)); +}; + +/** + * Use browser for parsing CSS. + */ +StyleFormatPanel.prototype.getCssRules = function(css) +{ + var doc = document.implementation.createHTMLDocument(''); + var styleElement = document.createElement('style'); + + mxUtils.setTextContent(styleElement, css); + doc.body.appendChild(styleElement); + + return styleElement.sheet.cssRules; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgStyles = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + container.style.fontWeight = 'bold'; + container.style.display = 'none'; + + try + { + var exp = ss.style.editableCssRules; + + if (exp != null) + { + var regex = new RegExp(exp); + + var data = ss.style.image.substring(ss.style.image.indexOf(',') + 1); + var xml = (window.atob) ? atob(data) : Base64.decode(data, true); + var svg = mxUtils.parseXml(xml); + + if (svg != null) + { + var styles = svg.getElementsByTagName('style'); + + for (var i = 0; i < styles.length; i++) + { + var rules = this.getCssRules(mxUtils.getTextContent(styles[i])); + + for (var j = 0; j < rules.length; j++) + { + this.addSvgRule(container, rules[j], svg, styles[i], rules, j, regex); + } + } + } + } + } + catch (e) + { + // ignore + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgRule = function(container, rule, svg, styleElem, rules, ruleIndex, regex) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + + if (regex.test(rule.selectorText)) + { + function rgb2hex(rgb) + { + rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + + return (rgb && rgb.length === 4) ? "#" + + ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + }; + + var addStyleRule = mxUtils.bind(this, function(rule, key, label) + { + if (rule.style[key] != '') + { + var option = this.createColorOption(label + ' ' + rule.selectorText, function() + { + return rgb2hex(rule.style[key]); + }, function(color) + { + rules[ruleIndex].style[key] = color; + var cssTxt = ''; + + for (var i = 0; i < rules.length; i++) + { + cssTxt += rules[i].cssText + ' '; + } + + styleElem.textContent = cssTxt; + var xml = mxUtils.getXml(svg.documentElement); + + graph.setCellStyles(mxConstants.STYLE_IMAGE, 'data:image/svg+xml,' + + ((window.btoa) ? btoa(xml) : Base64.encode(xml, true)), + graph.getSelectionCells()); + }, '#ffffff', + { + install: function(apply) + { + // ignore + }, + destroy: function() + { + // ignore + } + }); + + container.appendChild(option); + + // Shows container if rules are added + container.style.display = ''; + } + }); + + addStyleRule(rule, 'fill', mxResources.get('fill')); + addStyleRule(rule, 'stroke', mxResources.get('line')); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEditOps = function(div) +{ + var ss = this.format.getSelectionState(); + var btn = null; + + if (this.editorUi.editor.graph.getSelectionCount() == 1) + { + btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + } + + if (ss.image) + { + var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('image').funct(); + })); + + btn2.setAttribute('title', mxResources.get('editImage')); + btn2.style.marginBottom = '2px'; + + if (btn == null) + { + btn2.style.width = '202px'; + } + else + { + btn.style.width = '100px'; + btn2.style.width = '100px'; + btn2.style.marginLeft = '2px'; + } + + div.appendChild(btn2); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addFill = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + + // Adds gradient direction option + var gradientSelect = document.createElement('select'); + gradientSelect.style.position = 'absolute'; + gradientSelect.style.marginTop = '-2px'; + gradientSelect.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + gradientSelect.style.width = '70px'; + + // Stops events from bubbling to color option event handler + mxEvent.addListener(gradientSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var gradientPanel = this.createCellColorOption(mxResources.get('gradient'), mxConstants.STYLE_GRADIENTCOLOR, '#ffffff', function(color) + { + if (color == null || color == mxConstants.NONE) + { + gradientSelect.style.display = 'none'; + } + else + { + gradientSelect.style.display = ''; + } + }); + + var fillKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BACKGROUND : mxConstants.STYLE_FILLCOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('background') : mxResources.get('fill'); + + var fillPanel = this.createCellColorOption(label, fillKey, '#ffffff'); + fillPanel.style.fontWeight = 'bold'; + + var tmpColor = mxUtils.getValue(ss.style, fillKey, null); + gradientPanel.style.display = (tmpColor != null && tmpColor != mxConstants.NONE && + ss.fill && ss.style.shape != 'image') ? '' : 'none'; + + var directions = [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST, + mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST]; + + for (var i = 0; i < directions.length; i++) + { + var gradientOption = document.createElement('option'); + gradientOption.setAttribute('value', directions[i]); + mxUtils.write(gradientOption, mxResources.get(directions[i])); + gradientSelect.appendChild(gradientOption); + } + + gradientPanel.appendChild(gradientSelect); + + var listener = mxUtils.bind(this, function() + { + ss = this.format.getSelectionState(); + var value = mxUtils.getValue(ss.style, mxConstants.STYLE_GRADIENT_DIRECTION, mxConstants.DIRECTION_SOUTH); + + // Handles empty string which is not allowed as a value + if (value == '') + { + value = mxConstants.DIRECTION_SOUTH; + } + + gradientSelect.value = value; + container.style.display = (ss.fill) ? '' : 'none'; + + var fillColor = mxUtils.getValue(ss.style, mxConstants.STYLE_FILLCOLOR, null); + + if (!ss.fill || ss.containsImage || fillColor == null || fillColor == mxConstants.NONE || ss.style.shape == 'filledEdge') + { + gradientPanel.style.display = 'none'; + } + else + { + gradientPanel.style.display = ''; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + mxEvent.addListener(gradientSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION, gradientSelect.value, graph.getSelectionCells()); + mxEvent.consume(evt); + }); + + container.appendChild(fillPanel); + container.appendChild(gradientPanel); + + // Adds custom colors + var custom = this.getCustomColors(); + + for (var i = 0; i < custom.length; i++) + { + container.appendChild(this.createCellColorOption(custom[i].title, custom[i].key, custom[i].defaultValue)); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.getCustomColors = function() +{ + var ss = this.format.getSelectionState(); + var result = []; + + if (ss.style.shape == 'swimlane') + { + result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); + } + + return result; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStroke = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + + container.style.paddingTop = '4px'; + container.style.paddingBottom = '4px'; + container.style.whiteSpace = 'normal'; + + var colorPanel = document.createElement('div'); + colorPanel.style.fontWeight = 'bold'; + + // Adds gradient direction option + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '72px'; + styleSelect.style.width = '80px'; + + var styles = ['sharp', 'rounded', 'curved']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED]; + // Default for rounded is 1 + var values = ['0', null]; + + if (styleSelect.value == 'rounded') + { + values = ['1', null]; + } + else if (styleSelect.value == 'curved') + { + values = [null, '1']; + } + + for (var i = 0; i < keys.length; i++) + { + graph.setCellStyles(keys[i], values[i], graph.getSelectionCells()); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', keys, + 'values', values, 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var strokeKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BORDER : mxConstants.STYLE_STROKECOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('border') : mxResources.get('line'); + + var lineColor = this.createCellColorOption(label, strokeKey, '#000000'); + lineColor.appendChild(styleSelect); + colorPanel.appendChild(lineColor); + + // Used if only edges selected + var stylePanel = colorPanel.cloneNode(false); + stylePanel.style.fontWeight = 'normal'; + stylePanel.style.whiteSpace = 'nowrap'; + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '16px' + stylePanel.style.marginBottom = '2px'; + stylePanel.style.marginTop = '2px'; + stylePanel.className = 'geToolbarContainer'; + + var addItem = mxUtils.bind(this, function(menu, width, cssName, keys, values) + { + var item = this.editorUi.menus.styleChange(menu, '', keys, values, 'geIcon', null); + + var pat = document.createElement('div'); + pat.style.width = width + 'px'; + pat.style.height = '1px'; + pat.style.borderBottom = '1px ' + cssName + ' ' + this.defaultStrokeColor; + pat.style.paddingTop = '6px'; + + item.firstChild.firstChild.style.padding = '0px 4px 0px 4px'; + item.firstChild.firstChild.style.width = width + 'px'; + item.firstChild.firstChild.appendChild(pat); + + return item; + }); + + var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 75, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 75, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + // Used for mixed selection (vertices and edges) + var altStylePanel = stylePanel.cloneNode(false); + + var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-connection', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], [null, null, null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); + })); + + var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 33, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 33, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + var stylePanel2 = stylePanel.cloneNode(false); + + // Stroke width + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '2px'; + input.style.width = '41px'; + input.setAttribute('title', mxResources.get('linewidth')); + + stylePanel.appendChild(input); + + var altInput = input.cloneNode(true); + altStylePanel.appendChild(altInput); + + function update(evt) + { + // Maximum stroke width is 999 + var value = parseInt(input.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = value + ' pt'; + mxEvent.consume(evt); + }; + + function altUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(altInput.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + altInput.value = value + ' pt'; + mxEvent.consume(evt); + }; + + var stepper = this.createStepper(input, update, 1, 9); + stepper.style.display = input.style.display; + stepper.style.marginTop = '2px'; + stylePanel.appendChild(stepper); + + var altStepper = this.createStepper(altInput, altUpdate, 1, 9); + altStepper.style.display = altInput.style.display; + altStepper.style.marginTop = '2px'; + altStylePanel.appendChild(altStepper); + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + input.style.height = '15px'; + stepper.style.right = '20px'; + + altInput.style.position = 'absolute'; + altInput.style.right = '32px'; + altInput.style.height = '15px'; + altStepper.style.right = '20px'; + } + else + { + input.style.height = '17px'; + altInput.style.height = '17px'; + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + mxEvent.addListener(altInput, 'blur', altUpdate); + mxEvent.addListener(altInput, 'change', altUpdate); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(stylePanel2); + mxUtils.br(stylePanel2); + } + + var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape != 'arrow') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + + if (ss.style.shape == 'connector') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); + } + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); + } + })); + + var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-startclassic', mxResources.get('linestart'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-startclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-startclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-startopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-startopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['openAsync', 0], 'geIcon geSprite geSprite-startopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-startblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-startblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 1], 'geIcon geSprite geSprite-startasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-startoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-startdiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-startthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-startclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-startclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-startblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 0], 'geIcon geSprite geSprite-startasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-startovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-startdiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-startthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['dash', 0], 'geIcon geSprite geSprite-startdash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['cross', 0], 'geIcon geSprite geSprite-startcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-startcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circle', 1], 'geIcon geSprite geSprite-startcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERone', 0], 'geIcon geSprite geSprite-starterone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-starteronetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmany', 0], 'geIcon geSprite geSprite-startermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-starteronetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-starteroneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-startermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-endclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-endclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-endopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-endopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['openAsync', 0], 'geIcon geSprite geSprite-endopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-endblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-endblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 1], 'geIcon geSprite geSprite-endasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-endoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-enddiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-endthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-endclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-endclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-endblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 0], 'geIcon geSprite geSprite-endasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-endovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-enddiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-endthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['dash', 0], 'geIcon geSprite geSprite-enddash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['cross', 0], 'geIcon geSprite geSprite-endcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-endcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circle', 1], 'geIcon geSprite geSprite-endcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERone', 0], 'geIcon geSprite geSprite-enderone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-enderonetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmany', 0], 'geIcon geSprite geSprite-endermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-enderonetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-enderoneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-endermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + this.addArrow(edgeShape, 8); + this.addArrow(edgeStyle); + this.addArrow(lineStart); + this.addArrow(lineEnd); + + var symbol = this.addArrow(pattern, 9); + symbol.className = 'geIcon'; + symbol.style.width = '84px'; + + var altSymbol = this.addArrow(altPattern, 9); + altSymbol.className = 'geIcon'; + altSymbol.style.width = '22px'; + + var solid = document.createElement('div'); + solid.style.width = '85px'; + solid.style.height = '1px'; + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + solid.style.marginBottom = '9px'; + symbol.appendChild(solid); + + var altSolid = document.createElement('div'); + altSolid.style.width = '23px'; + altSolid.style.height = '1px'; + altSolid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + altSolid.style.marginBottom = '9px'; + altSymbol.appendChild(altSolid); + + pattern.style.height = '15px'; + altPattern.style.height = '15px'; + edgeShape.style.height = '15px'; + edgeStyle.style.height = '17px'; + lineStart.style.marginLeft = '3px'; + lineStart.style.height = '17px'; + lineEnd.style.marginLeft = '3px'; + lineEnd.style.height = '17px'; + + container.appendChild(colorPanel); + container.appendChild(altStylePanel); + container.appendChild(stylePanel); + + var arrowPanel = stylePanel.cloneNode(false); + arrowPanel.style.paddingBottom = '6px'; + arrowPanel.style.paddingTop = '4px'; + arrowPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '2px'; + span.style.fontWeight = 'normal'; + span.style.width = '76px'; + + mxUtils.write(span, mxResources.get('lineend')); + arrowPanel.appendChild(span); + + var endSpacingUpdate, endSizeUpdate; + var endSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + endSpacingUpdate.apply(this, arguments); + }); + var endSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + endSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + + var spacer = document.createElement('div'); + spacer.style.height = '8px'; + arrowPanel.appendChild(spacer); + + span = span.cloneNode(false); + mxUtils.write(span, mxResources.get('linestart')); + arrowPanel.appendChild(span); + + var startSpacingUpdate, startSizeUpdate; + var startSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + startSpacingUpdate.apply(this, arguments); + }); + var startSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + startSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + this.addLabel(arrowPanel, mxResources.get('spacing'), 74, 50); + this.addLabel(arrowPanel, mxResources.get('size'), 20, 50); + mxUtils.br(arrowPanel); + + var perimeterPanel = colorPanel.cloneNode(false); + perimeterPanel.style.fontWeight = 'normal'; + perimeterPanel.style.position = 'relative'; + perimeterPanel.style.paddingLeft = '16px' + perimeterPanel.style.marginBottom = '2px'; + perimeterPanel.style.marginTop = '6px'; + perimeterPanel.style.borderWidth = '0px'; + perimeterPanel.style.paddingBottom = '18px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '1px'; + span.style.fontWeight = 'normal'; + span.style.width = '120px'; + mxUtils.write(span, mxResources.get('perimeter')); + perimeterPanel.appendChild(span); + + var perimeterUpdate; + var perimeterSpacing = this.addUnitInput(perimeterPanel, 'pt', 20, 41, function() + { + perimeterUpdate.apply(this, arguments); + }); + + if (ss.edges.length == graph.getSelectionCount()) + { + container.appendChild(stylePanel2); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + mxUtils.br(container); + } + + container.appendChild(arrowPanel); + } + else if (ss.vertices.length == graph.getSelectionCount()) + { + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(perimeterPanel); + } + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var color = mxUtils.getValue(ss.style, strokeKey, null); + + if (force || document.activeElement != input) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != altInput) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + altInput.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + styleSelect.style.visibility = (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') ? '' : 'hidden'; + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + styleSelect.value = 'curved'; + } + else if (mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == '1') + { + styleSelect.value = 'rounded'; + } + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == '1') + { + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null) + { + solid.style.borderBottom = '1px dashed ' + this.defaultStrokeColor; + } + else + { + solid.style.borderBottom = '1px dotted ' + this.defaultStrokeColor; + } + } + else + { + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + } + + altSolid.style.borderBottom = solid.style.borderBottom; + + // Updates toolbar icon for edge style + var edgeStyleDiv = edgeStyle.getElementsByTagName('div')[0]; + var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null); + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == '1') + { + es = null; + } + + if (es == 'orthogonalEdgeStyle' && mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + edgeStyleDiv.className = 'geSprite geSprite-curved'; + } + else if (es == 'straight' || es == 'none' || es == null) + { + edgeStyleDiv.className = 'geSprite geSprite-straight'; + } + else if (es == 'entityRelationEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-entity'; + } + else if (es == 'elbowEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalelbow' : 'geSprite-horizontalelbow'); + } + else if (es == 'isometricEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalisometric' : 'geSprite-horizontalisometric'); + } + else + { + edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; + } + + // Updates icon for edge shape + var edgeShapeDiv = edgeShape.getElementsByTagName('div')[0]; + + if (ss.style.shape == 'link') + { + edgeShapeDiv.className = 'geSprite geSprite-linkedge'; + } + else if (ss.style.shape == 'flexArrow') + { + edgeShapeDiv.className = 'geSprite geSprite-arrow'; + } + else if (ss.style.shape == 'arrow') + { + edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; + } + else + { + edgeShapeDiv.className = 'geSprite geSprite-connection'; + } + + if (ss.edges.length == graph.getSelectionCount()) + { + altStylePanel.style.display = ''; + stylePanel.style.display = 'none'; + } + else + { + altStylePanel.style.display = 'none'; + stylePanel.style.display = ''; + } + + function updateArrow(marker, fill, elt, prefix) + { + var markerDiv = elt.getElementsByTagName('div')[0]; + + markerDiv.className = ui.getCssClassForMarker(prefix, ss.style.shape, marker, fill); + + if (markerDiv.className == 'geSprite geSprite-noarrow') + { + markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('none')); + markerDiv.style.backgroundImage = 'none'; + markerDiv.style.verticalAlign = 'top'; + markerDiv.style.marginTop = '5px'; + markerDiv.style.fontSize = '10px'; + markerDiv.style.filter = 'none'; + markerDiv.style.color = this.defaultStrokeColor; + markerDiv.nextSibling.style.marginTop = '0px'; + } + + return markerDiv; + }; + + var sourceDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null), + mxUtils.getValue(ss.style, 'startFill', '1'), lineStart, 'start'); + var targetDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null), + mxUtils.getValue(ss.style, 'endFill', '1'), lineEnd, 'end'); + + // Special cases for markers + if (ss.style.shape == 'arrow') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-endblocktrans'; + } + else if (ss.style.shape == 'link') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-noarrow'; + } + + mxUtils.setOpacity(edgeStyle, (ss.style.shape == 'arrow') ? 30 : 100); + + if (ss.style.shape != 'connector' && ss.style.shape != 'flexArrow' && ss.style.shape != 'filledEdge') + { + mxUtils.setOpacity(lineStart, 30); + mxUtils.setOpacity(lineEnd, 30); + } + else + { + mxUtils.setOpacity(lineStart, 100); + mxUtils.setOpacity(lineEnd, 100); + } + + if (force || document.activeElement != startSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE)); + startSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0)); + startSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != endSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)); + endSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0)); + endSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != perimeterSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0)); + perimeterSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + startSizeUpdate = this.installInputHandler(startSize, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + startSpacingUpdate = this.installInputHandler(startSpacing, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0, -999, 999, ' pt'); + endSizeUpdate = this.installInputHandler(endSize, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + endSpacingUpdate = this.installInputHandler(endSpacing, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0, -999, 999, ' pt'); + perimeterUpdate = this.installInputHandler(perimeterSpacing, mxConstants.STYLE_PERIMETER_SPACING, 0, 0, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(startSize, listener); + this.addKeyHandler(startSpacing, listener); + this.addKeyHandler(endSize, listener); + this.addKeyHandler(endSpacing, listener); + this.addKeyHandler(perimeterSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return container; +}; + +/** + * Adds UI for configuring line jumps. + */ +StyleFormatPanel.prototype.addLineJumps = function(container) +{ + var ss = this.format.getSelectionState(); + + if (Graph.lineJumpsEnabled && ss.edges.length > 0 && + ss.vertices.length == 0 && ss.lineJumps) + { + container.style.padding = '8px 0px 24px 18px'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.fontWeight = 'bold'; + span.style.width = '80px'; + + mxUtils.write(span, mxResources.get('lineJumps')); + container.appendChild(span); + + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '76px'; + styleSelect.style.width = '62px'; + + var styles = ['none', 'arc', 'gap', 'sharp']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles('jumpStyle', styleSelect.value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['jumpStyle'], + 'values', [styleSelect.value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + container.appendChild(styleSelect); + + var jumpSizeUpdate; + + var jumpSize = this.addUnitInput(container, 'pt', 22, 33, function() + { + jumpSizeUpdate.apply(this, arguments); + }); + + jumpSizeUpdate = this.installInputHandler(jumpSize, 'jumpSize', + Graph.defaultJumpSize, 0, 999, ' pt'); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + styleSelect.value = mxUtils.getValue(ss.style, 'jumpStyle', 'none'); + + if (force || document.activeElement != jumpSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, 'jumpSize', Graph.defaultJumpSize)); + jumpSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + this.addKeyHandler(jumpSize, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + else + { + container.style.display = 'none'; + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEffects = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingTop = '0px'; + div.style.paddingBottom = '2px'; + + var table = document.createElement('table'); + + if (mxClient.IS_QUIRKS) + { + table.style.fontSize = '1em'; + } + + table.style.width = '100%'; + table.style.fontWeight = 'bold'; + table.style.paddingRight = '20px'; + var tbody = document.createElement('tbody'); + var row = document.createElement('tr'); + row.style.padding = '0px'; + var left = document.createElement('td'); + left.style.padding = '0px'; + left.style.width = '50%'; + left.setAttribute('valign', 'top'); + + var right = left.cloneNode(true); + right.style.paddingLeft = '8px'; + row.appendChild(left); + row.appendChild(right); + tbody.appendChild(row); + table.appendChild(tbody); + div.appendChild(table); + + var current = left; + var count = 0; + + var addOption = mxUtils.bind(this, function(label, key, defaultValue) + { + var opt = this.createCellOption(label, key, defaultValue); + opt.style.width = '100%'; + current.appendChild(opt); + current = (current == left) ? right : left; + count++; + }); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + + left.innerHTML = ''; + right.innerHTML = ''; + current = left; + + if (ss.rounded) + { + addOption(mxResources.get('rounded'), mxConstants.STYLE_ROUNDED, 0); + } + + if (ss.style.shape == 'swimlane') + { + addOption(mxResources.get('divider'), 'swimlaneLine', 1); + } + + if (!ss.containsImage) + { + addOption(mxResources.get('shadow'), mxConstants.STYLE_SHADOW, 0); + } + + if (ss.glass) + { + addOption(mxResources.get('glass'), mxConstants.STYLE_GLASS, 0); + } + + if (ss.comic) + { + addOption(mxResources.get('comic'), 'comic', 0); + } + + if (count == 0) + { + div.style.display = 'none'; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return div; +} + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStyleOps = function(div) +{ + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + + var btn = mxUtils.button(mxResources.get('setAsDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('setAsDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('setAsDefaultStyle') + ' (' + this.editorUi.actions.get('setAsDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(DiagramFormatPanel, BaseFormatPanel); + +/** + * Switch to disable page view. + */ +DiagramFormatPanel.showPageView = true; + +/** + * Specifies if the background image option should be shown. Default is true. + */ +DiagramFormatPanel.prototype.showBackgroundImageOption = true; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + this.container.appendChild(this.addView(this.createPanel())); + + if (graph.isEnabled()) + { + this.container.appendChild(this.addOptions(this.createPanel())); + this.container.appendChild(this.addPaperSize(this.createPanel())); + this.container.appendChild(this.addStyleOps(this.createPanel())); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addView = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('view'))); + + // Grid + this.addGridOption(div); + + if (graph.isEnabled()) + { + // Page View + if (DiagramFormatPanel.showPageView) + { + div.appendChild(this.createOption(mxResources.get('pageView'), function() + { + return graph.pageVisible; + }, function(checked) + { + ui.actions.get('pageView').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.pageVisible); + }; + + ui.addListener('pageViewChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + // Background + var bg = this.createColorOption(mxResources.get('background'), function() + { + return graph.background; + }, function(color) + { + var change = new ChangePageSetup(ui, color); + change.ignoreImage = true; + + graph.model.execute(change); + }, '#ffffff', + { + install: function(apply) + { + this.listener = function() + { + apply(graph.background); + }; + + ui.addListener('backgroundColorChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + if (this.showBackgroundImageOption) + { + var btn = mxUtils.button(mxResources.get('image'), function(evt) + { + ui.showBackgroundImageDialog(); + mxEvent.consume(evt); + }) + + btn.style.position = 'absolute'; + btn.className = 'geColorBtn'; + btn.style.marginTop = '-4px'; + btn.style.paddingBottom = (document.documentMode == 11 || mxClient.IS_MT) ? '0px' : '2px'; + btn.style.height = '22px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + btn.style.width = '56px'; + + bg.appendChild(btn); + } + + div.appendChild(bg); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addOptions = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + if (graph.isEnabled()) + { + // Connection arrows + div.appendChild(this.createOption(mxResources.get('connectionArrows'), function() + { + return graph.connectionArrowsEnabled; + }, function(checked) + { + ui.actions.get('connectionArrows').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionArrowsEnabled); + }; + + ui.addListener('connectionArrowsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Connection points + div.appendChild(this.createOption(mxResources.get('connectionPoints'), function() + { + return graph.connectionHandler.isEnabled(); + }, function(checked) + { + ui.actions.get('connectionPoints').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionHandler.isEnabled()); + }; + + ui.addListener('connectionPointsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Guides + div.appendChild(this.createOption(mxResources.get('guides'), function() + { + return graph.graphHandler.guidesEnabled; + }, function(checked) + { + ui.actions.get('guides').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.graphHandler.guidesEnabled); + }; + + ui.addListener('guidesEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + return div; +}; + +/** + * + */ +DiagramFormatPanel.prototype.addGridOption = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.width = '38px'; + input.value = graph.getGridSize() + ' pt'; + + var stepper = this.createStepper(input, update); + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + input.value = graph.getGridSize(); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + function update(evt) + { + var value = parseInt(input.value); + value = Math.max(1, (isNaN(value)) ? 10 : value); + + if (value != graph.getGridSize()) + { + graph.setGridSize(value) + } + + input.value = value + ' pt'; + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + if (mxClient.IS_SVG) + { + input.style.marginTop = '-2px'; + input.style.right = '84px'; + stepper.style.marginTop = '-16px'; + stepper.style.right = '72px'; + + var panel = this.createColorOption(mxResources.get('grid'), function() + { + var color = graph.view.gridColor; + + return (graph.isGridEnabled()) ? color : null; + }, function(color) + { + if (color == mxConstants.NONE) + { + graph.setGridEnabled(false); + } + else + { + graph.setGridEnabled(true); + ui.setGridColor(color); + } + + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, '#e0e0e0', + { + install: function(apply) + { + this.listener = function() + { + apply((graph.isGridEnabled()) ? graph.view.gridColor : null); + }; + + ui.addListener('gridColorChanged', this.listener); + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + panel.appendChild(input); + panel.appendChild(stepper); + container.appendChild(panel); + } + else + { + input.style.marginTop = '2px'; + input.style.right = '32px'; + stepper.style.marginTop = '2px'; + stepper.style.right = '20px'; + + container.appendChild(input); + container.appendChild(stepper); + + container.appendChild(this.createOption(mxResources.get('grid'), function() + { + return graph.isGridEnabled(); + }, function(checked) + { + graph.setGridEnabled(checked); + + if (graph.isGridEnabled()) + { + graph.view.gridColor = '#e0e0e0'; + } + + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, + { + install: function(apply) + { + this.listener = function() + { + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + apply(graph.isGridEnabled()); + }; + + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addDocumentProperties = function(div) +{ + // Hook for subclassers + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addPaperSize = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('paperSize'))); + + var accessor = PageSetupDialog.addPageFormatPanel(div, 'formatpanel', graph.pageFormat, function(pageFormat) + { + if (graph.pageFormat == null || graph.pageFormat.width != pageFormat.width || + graph.pageFormat.height != pageFormat.height) + { + var change = new ChangePageSetup(ui, null, null, pageFormat); + change.ignoreColor = true; + change.ignoreImage = true; + + graph.model.execute(change); + } + }); + + this.addKeyHandler(accessor.widthInput, function() + { + accessor.set(graph.pageFormat); + }); + this.addKeyHandler(accessor.heightInput, function() + { + accessor.set(graph.pageFormat); + }); + + var listener = function() + { + accessor.set(graph.pageFormat); + }; + + ui.addListener('pageFormatChanged', listener); + this.listeners.push({destroy: function() { ui.removeListener(listener); }}); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addStyleOps = function(div) +{ + var btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + mxUtils.br(div); + + btn = mxUtils.button(mxResources.get('clearDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearDefaultStyle') + ' (' + this.editorUi.actions.get('clearDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.destroy = function() +{ + BaseFormatPanel.prototype.destroy.apply(this, arguments); + + if (this.gridEnabledListener) + { + this.editorUi.removeListener(this.gridEnabledListener); + this.gridEnabledListener = null; + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Graph.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Graph.js new file mode 100644 index 00000000..33ddd57b --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Graph.js @@ -0,0 +1,8344 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +// Workaround for allowing target="_blank" in HTML sanitizer +// see https://code.google.com/p/google-caja/issues/detail?can=2&q=&colspec=ID%20Type%20Status%20Priority%20Owner%20Summary&groupby=&sort=&id=1296 +if (typeof html4 !== 'undefined') +{ + html4.ATTRIBS["a::target"] = 0; + html4.ATTRIBS["source::src"] = 0; + html4.ATTRIBS["video::src"] = 0; + // Would be nice for tooltips but probably a security risk... + //html4.ATTRIBS["video::autoplay"] = 0; + //html4.ATTRIBS["video::autobuffer"] = 0; +} + +/** + * Sets global constants. + */ +// Changes default colors +mxConstants.SHADOW_OPACITY = 0.25; +mxConstants.SHADOWCOLOR = '#000000'; +mxConstants.VML_SHADOWCOLOR = '#d0d0d0'; +mxGraph.prototype.pageBreakColor = '#c0c0c0'; +mxGraph.prototype.pageScale = 1; + +// Letter page format is default in US, Canada and Mexico +(function() +{ + try + { + if (navigator != null && navigator.language != null) + { + var lang = navigator.language.toLowerCase(); + mxGraph.prototype.pageFormat = (lang === 'en-us' || lang === 'en-ca' || lang === 'es-mx') ? + mxConstants.PAGE_FORMAT_LETTER_PORTRAIT : mxConstants.PAGE_FORMAT_A4_PORTRAIT; + } + } + catch (e) + { + // ignore + } +})(); + +// Matches label positions of mxGraph 1.x +mxText.prototype.baseSpacingTop = 5; +mxText.prototype.baseSpacingBottom = 1; + +// Keeps edges between relative child cells inside parent +mxGraphModel.prototype.ignoreRelativeEdgeParent = false; + +// Defines grid properties +mxGraphView.prototype.gridImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCgAKAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAAKAAoAAAIJ1I6py+0Po2wFADs=' : + IMAGE_PATH + '/grid.gif'; +mxGraphView.prototype.gridSteps = 4; +mxGraphView.prototype.minGridSize = 4; + +// UrlParams is null in embed mode +mxGraphView.prototype.gridColor = '#e0e0e0'; + +// Alternative text for unsupported foreignObjects +mxSvgCanvas2D.prototype.foAltText = '[Not supported by viewer]'; + +/** + * Constructs a new graph instance. Note that the constructor does not take a + * container because the graph instance is needed for creating the UI, which + * in turn will create the container for the graph. Hence, the container is + * assigned later in EditorUi. + */ +/** + * Defines graph class. + */ +Graph = function(container, model, renderHint, stylesheet, themes) +{ + mxGraph.call(this, container, model, renderHint, stylesheet); + + this.themes = themes || this.defaultThemes; + this.currentEdgeStyle = mxUtils.clone(this.defaultEdgeStyle); + this.currentVertexStyle = mxUtils.clone(this.defaultVertexStyle); + + // Sets the base domain URL and domain path URL for relative links. + var b = this.baseUrl; + var p = b.indexOf('//'); + this.domainUrl = ''; + this.domainPathUrl = ''; + + if (p > 0) + { + var d = b.indexOf('/', p + 2); + + if (d > 0) + { + this.domainUrl = b.substring(0, d); + } + + d = b.lastIndexOf('/'); + + if (d > 0) + { + this.domainPathUrl = b.substring(0, d + 1); + } + } + + // Adds support for HTML labels via style. Note: Currently, only the Java + // backend supports HTML labels but CSS support is limited to the following: + // http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html + // TODO: Wrap should not affect isHtmlLabel output (should be handled later) + this.isHtmlLabel = function(cell) + { + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return style['html'] == '1' || style[mxConstants.STYLE_WHITE_SPACE] == 'wrap'; + }; + + // Implements a listener for hover and click handling on edges + if (this.edgeMode) + { + var start = { + point: null, + event: null, + state: null, + handle: null, + selected: false + }; + + // Uses this event to process mouseDown to check the selection state before it is changed + this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) + { + if (evt.getProperty('eventName') == 'mouseDown' && this.isEnabled()) + { + var me = evt.getProperty('event'); + + if (!mxEvent.isControlDown(me.getEvent()) && !mxEvent.isShiftDown(me.getEvent())) + { + var state = me.getState(); + + if (state != null) + { + // Checks if state was removed in call to stopEditing above + if (this.model.isEdge(state.cell)) + { + start.point = new mxPoint(me.getGraphX(), me.getGraphY()); + start.selected = this.isCellSelected(state.cell); + start.state = state; + start.event = me; + + if (state.text != null && state.text.boundingBox != null && + mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) + { + start.handle = mxEvent.LABEL_HANDLE; + } + else + { + var handler = this.selectionCellsHandler.getHandler(state.cell); + + if (handler != null && handler.bends != null && handler.bends.length > 0) + { + start.handle = handler.getHandleForEvent(me); + } + } + } + } + } + } + })); + + var mouseDown = null; + + this.addMouseListener( + { + mouseDown: function(sender, me) {}, + mouseMove: mxUtils.bind(this, function(sender, me) + { + // Checks if any other handler is active + var handlerMap = this.selectionCellsHandler.handlers.map; + + for (var key in handlerMap) + { + if (handlerMap[key].index != null) + { + return; + } + } + + if (this.isEnabled() && !this.panningHandler.isActive() && !mxEvent.isControlDown(me.getEvent()) && + !mxEvent.isShiftDown(me.getEvent()) && !mxEvent.isAltDown(me.getEvent())) + { + var tol = this.tolerance; + + if (start.point != null && start.state != null && start.event != null) + { + var state = start.state; + + if (Math.abs(start.point.x - me.getGraphX()) > tol || + Math.abs(start.point.y - me.getGraphY()) > tol) + { + // Lazy selection for edges inside groups + if (!this.isCellSelected(state.cell)) + { + this.setSelectionCell(state.cell); + } + + var handler = this.selectionCellsHandler.getHandler(state.cell); + + if (handler != null && handler.bends != null && handler.bends.length > 0) + { + var handle = handler.getHandleForEvent(start.event); + var edgeStyle = this.view.getEdgeStyle(state); + var entity = edgeStyle == mxEdgeStyle.EntityRelation; + + // Handles special case where label was clicked on unselected edge in which + // case the label will be moved regardless of the handle that is returned + if (!start.selected && start.handle == mxEvent.LABEL_HANDLE) + { + handle = start.handle; + } + + if (!entity || handle == 0 || handle == handler.bends.length - 1 || handle == mxEvent.LABEL_HANDLE) + { + // Source or target handle or connected for direct handle access or orthogonal line + // with just two points where the central handle is moved regardless of mouse position + if (handle == mxEvent.LABEL_HANDLE || handle == 0 || state.visibleSourceState != null || + handle == handler.bends.length - 1 || state.visibleTargetState != null) + { + if (!entity && handle != mxEvent.LABEL_HANDLE) + { + var pts = state.absolutePoints; + + // Default case where handles are at corner points handles + // drag of corner as drag of existing point + if (pts != null && ((edgeStyle == null && handle == null) || + edgeStyle == mxEdgeStyle.OrthConnector)) + { + // Does not use handles if they were not initially visible + handle = start.handle; + + if (handle == null) + { + var box = new mxRectangle(start.point.x, start.point.y); + box.grow(mxEdgeHandler.prototype.handleImage.width / 2); + + if (mxUtils.contains(box, pts[0].x, pts[0].y)) + { + // Moves source terminal handle + handle = 0; + } + else if (mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) + { + // Moves target terminal handle + handle = handler.bends.length - 1; + } + else + { + // Checks if edge has no bends + var nobends = edgeStyle != null && (pts.length == 2 || (pts.length == 3 && + ((Math.round(pts[0].x - pts[1].x) == 0 && Math.round(pts[1].x - pts[2].x) == 0) || + (Math.round(pts[0].y - pts[1].y) == 0 && Math.round(pts[1].y - pts[2].y) == 0)))); + + if (nobends) + { + // Moves central handle for straight orthogonal edges + handle = 2; + } + else + { + // Finds and moves vertical or horizontal segment + handle = mxUtils.findNearestSegment(state, start.point.x, start.point.y); + + // Converts segment to virtual handle index + if (edgeStyle == null) + { + handle = mxEvent.VIRTUAL_HANDLE - handle; + } + // Maps segment to handle + else + { + handle += 1; + } + } + } + } + } + + // Creates a new waypoint and starts moving it + if (handle == null) + { + handle = mxEvent.VIRTUAL_HANDLE; + } + } + + handler.start(me.getGraphX(), me.getGraphX(), handle); + start.state = null; + start.event = null; + start.point = null; + start.handle = null; + start.selected = false; + me.consume(); + + // Removes preview rectangle in graph handler + this.graphHandler.reset(); + } + } + else if (entity && (state.visibleSourceState != null || state.visibleTargetState != null)) + { + // Disables moves on entity to make it consistent + this.graphHandler.reset(); + me.consume(); + } + } + } + } + else + { + // Updates cursor for unselected edges under the mouse + var state = me.getState(); + + if (state != null) + { + // Checks if state was removed in call to stopEditing above + if (this.model.isEdge(state.cell)) + { + var cursor = null; + var pts = state.absolutePoints; + + if (pts != null) + { + var box = new mxRectangle(me.getGraphX(), me.getGraphY()); + box.grow(mxEdgeHandler.prototype.handleImage.width / 2); + + if (state.text != null && state.text.boundingBox != null && + mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) + { + cursor = 'move'; + } + else if (mxUtils.contains(box, pts[0].x, pts[0].y) || + mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) + { + cursor = 'pointer'; + } + else if (state.visibleSourceState != null || state.visibleTargetState != null) + { + // Moving is not allowed for entity relation but still indicate hover state + var tmp = this.view.getEdgeStyle(state); + cursor = 'crosshair'; + + if (tmp != mxEdgeStyle.EntityRelation && this.isOrthogonal(state)) + { + var idx = mxUtils.findNearestSegment(state, me.getGraphX(), me.getGraphY()); + + if (idx < pts.length - 1 && idx >= 0) + { + cursor = (Math.round(pts[idx].x - pts[idx + 1].x) == 0) ? + 'col-resize' : 'row-resize'; + } + } + } + } + + if (cursor != null) + { + state.setCursor(cursor); + } + } + } + } + } + }), + mouseUp: mxUtils.bind(this, function(sender, me) + { + start.state = null; + start.event = null; + start.point = null; + start.handle = null; + }) + }); + } + + // HTML entities are displayed as plain text in wrapped plain text labels + this.cellRenderer.getLabelValue = function(state) + { + var result = mxCellRenderer.prototype.getLabelValue.apply(this, arguments); + + if (state.view.graph.isHtmlLabel(state.cell)) + { + if (state.style['html'] != 1) + { + result = mxUtils.htmlEntities(result, false); + } + else + { + result = state.view.graph.sanitizeHtml(result); + } + } + + return result; + }; + + // All code below not available and not needed in embed mode + if (typeof mxVertexHandler !== 'undefined') + { + this.setConnectable(true); + this.setDropEnabled(true); + this.setPanning(true); + this.setTooltips(true); + this.setAllowLoops(true); + this.allowAutoPanning = true; + this.resetEdgesOnConnect = false; + this.constrainChildren = false; + this.constrainRelativeChildren = true; + + // Do not scroll after moving cells + this.graphHandler.scrollOnMove = false; + this.graphHandler.scaleGrid = true; + + // Disables cloning of connection sources by default + this.connectionHandler.setCreateTarget(false); + this.connectionHandler.insertBeforeSource = true; + + // Disables built-in connection starts + this.connectionHandler.isValidSource = function(cell, me) + { + return false; + }; + + // Sets the style to be used when an elbow edge is double clicked + this.alternateEdgeStyle = 'vertical'; + + if (stylesheet == null) + { + this.loadStylesheet(); + } + + // Adds page centers to the guides for moving cells + var graphHandlerGetGuideStates = this.graphHandler.getGuideStates; + this.graphHandler.getGuideStates = function() + { + var result = graphHandlerGetGuideStates.apply(this, arguments); + + // Create virtual cell state for page centers + if (this.graph.pageVisible) + { + var guides = []; + + var pf = this.graph.pageFormat; + var ps = this.graph.pageScale; + var pw = pf.width * ps; + var ph = pf.height * ps; + var t = this.graph.view.translate; + var s = this.graph.view.scale; + + var layout = this.graph.getPageLayout(); + + for (var i = 0; i < layout.width; i++) + { + guides.push(new mxRectangle(((layout.x + i) * pw + t.x) * s, + (layout.y * ph + t.y) * s, pw * s, ph * s)); + } + + for (var j = 0; j < layout.height; j++) + { + guides.push(new mxRectangle((layout.x * pw + t.x) * s, + ((layout.y + j) * ph + t.y) * s, pw * s, ph * s)); + } + + // Page center guides have predence over normal guides + result = guides.concat(result); + } + + return result; + }; + + // Overrides zIndex for dragElement + mxDragSource.prototype.dragElementZIndex = mxPopupMenu.prototype.zIndex; + + // Overrides color for virtual guides for page centers + mxGuide.prototype.getGuideColor = function(state, horizontal) + { + return (state.cell == null) ? '#ffa500' /* orange */ : mxConstants.GUIDE_COLOR; + }; + + // Changes color of move preview for black backgrounds + this.graphHandler.createPreviewShape = function(bounds) + { + this.previewColor = (this.graph.background == '#000000') ? '#ffffff' : mxGraphHandler.prototype.previewColor; + + return mxGraphHandler.prototype.createPreviewShape.apply(this, arguments); + }; + + // Handles parts of cells by checking if part=1 is in the style and returning the parent + // if the parent is not already in the list of cells. container style is used to disable + // step into swimlanes and dropTarget style is used to disable acting as a drop target. + // LATER: Handle recursive parts + this.graphHandler.getCells = function(initialCell) + { + var cells = mxGraphHandler.prototype.getCells.apply(this, arguments); + var newCells = []; + + for (var i = 0; i < cells.length; i++) + { + var state = this.graph.view.getState(cells[i]); + var style = (state != null) ? state.style : this.graph.getCellStyle(cells[i]); + + if (mxUtils.getValue(style, 'part', '0') == '1') + { + var parent = this.graph.model.getParent(cells[i]); + + if (this.graph.model.isVertex(parent) && mxUtils.indexOf(cells, parent) < 0) + { + newCells.push(parent); + } + } + else + { + newCells.push(cells[i]); + } + } + + return newCells; + }; + + // Handles parts of cells when cloning the source for new connections + this.connectionHandler.createTargetVertex = function(evt, source) + { + var state = this.graph.view.getState(source); + var style = (state != null) ? state.style : this.graph.getCellStyle(source); + + if (mxUtils.getValue(style, 'part', false)) + { + var parent = this.graph.model.getParent(source); + + if (this.graph.model.isVertex(parent)) + { + source = parent; + } + } + + return mxConnectionHandler.prototype.createTargetVertex.apply(this, arguments); + }; + + var rubberband = new mxRubberband(this); + + this.getRubberband = function() + { + return rubberband; + }; + + // Timer-based activation of outline connect in connection handler + var startTime = new Date().getTime(); + var timeOnTarget = 0; + + var connectionHandlerMouseMove = this.connectionHandler.mouseMove; + + this.connectionHandler.mouseMove = function() + { + var prev = this.currentState; + connectionHandlerMouseMove.apply(this, arguments); + + if (prev != this.currentState) + { + startTime = new Date().getTime(); + timeOnTarget = 0; + } + else + { + timeOnTarget = new Date().getTime() - startTime; + } + }; + + // Activates outline connect after 1500ms with touch event or if alt is pressed inside the shape + // outlineConnect=0 is a custom style that means do not connect to strokes inside the shape, + // or in other words, connect to the shape's perimeter if the highlight is under the mouse + // (the name is because the highlight, including all strokes, is called outline in the code) + var connectionHandleIsOutlineConnectEvent = this.connectionHandler.isOutlineConnectEvent; + + this.connectionHandler.isOutlineConnectEvent = function(me) + { + return (this.currentState != null && me.getState() == this.currentState && timeOnTarget > 2000) || + ((this.currentState == null || mxUtils.getValue(this.currentState.style, 'outlineConnect', '1') != '0') && + connectionHandleIsOutlineConnectEvent.apply(this, arguments)); + }; + + // Adds shift+click to toggle selection state + var isToggleEvent = this.isToggleEvent; + this.isToggleEvent = function(evt) + { + return isToggleEvent.apply(this, arguments) || mxEvent.isShiftDown(evt); + }; + + // Workaround for Firefox where first mouse down is received + // after tap and hold if scrollbars are visible, which means + // start rubberband immediately if no cell is under mouse. + var isForceRubberBandEvent = rubberband.isForceRubberbandEvent; + rubberband.isForceRubberbandEvent = function(me) + { + return isForceRubberBandEvent.apply(this, arguments) || + (mxUtils.hasScrollbars(this.graph.container) && mxClient.IS_FF && + mxClient.IS_WIN && me.getState() == null && mxEvent.isTouchEvent(me.getEvent())); + }; + + // Shows hand cursor while panning + var prevCursor = null; + + this.panningHandler.addListener(mxEvent.PAN_START, mxUtils.bind(this, function() + { + if (this.isEnabled()) + { + prevCursor = this.container.style.cursor; + this.container.style.cursor = 'move'; + } + })); + + this.panningHandler.addListener(mxEvent.PAN_END, mxUtils.bind(this, function() + { + if (this.isEnabled()) + { + this.container.style.cursor = prevCursor; + } + })); + + this.popupMenuHandler.autoExpand = true; + + this.popupMenuHandler.isSelectOnPopup = function(me) + { + return mxEvent.isMouseEvent(me.getEvent()); + }; + + // Handles links if graph is read-only or cell is locked + var click = this.click; + this.click = function(me) + { + var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell); + + if ((!this.isEnabled() || locked) && !me.isConsumed()) + { + var cell = (locked) ? me.sourceState.cell : me.getCell(); + + if (cell != null) + { + var link = this.getLinkForCell(cell); + + if (link != null) + { + if (this.isCustomLink(link)) + { + this.customLinkClicked(link); + } + else + { + this.openLink(link); + } + } + } + } + else + { + return click.apply(this, arguments); + } + }; + + // Redirects tooltips for locked cells + this.tooltipHandler.getStateForEvent = function(me) + { + return me.sourceState; + }; + + // Redirects cursor for locked cells + var getCursorForMouseEvent = this.getCursorForMouseEvent; + this.getCursorForMouseEvent = function(me) + { + var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell); + + return this.getCursorForCell((locked) ? me.sourceState.cell : me.getCell()); + }; + + // Shows pointer cursor for clickable cells with links + // ie. if the graph is disabled and cells cannot be selected + var getCursorForCell = this.getCursorForCell; + this.getCursorForCell = function(cell) + { + if (!this.isEnabled() || this.isCellLocked(cell)) + { + var link = this.getLinkForCell(cell); + + if (link != null) + { + return 'pointer'; + } + else if (this.isCellLocked(cell)) + { + return 'default'; + } + } + + return getCursorForCell.apply(this, arguments); + }; + + // Changes rubberband selection to be recursive + this.selectRegion = function(rect, evt) + { + var cells = this.getAllCells(rect.x, rect.y, rect.width, rect.height); + this.selectCellsForEvent(cells, evt); + + return cells; + }; + + // Recursive implementation for rubberband selection + this.getAllCells = function(x, y, width, height, parent, result) + { + result = (result != null) ? result : []; + + if (width > 0 || height > 0) + { + var model = this.getModel(); + var right = x + width; + var bottom = y + height; + + if (parent == null) + { + parent = this.getCurrentRoot(); + + if (parent == null) + { + parent = model.getRoot(); + } + } + + if (parent != null) + { + var childCount = model.getChildCount(parent); + + for (var i = 0; i < childCount; i++) + { + var cell = model.getChildAt(parent, i); + var state = this.view.getState(cell); + + if (state != null && this.isCellVisible(cell) && mxUtils.getValue(state.style, 'locked', '0') != '1') + { + var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0; + var box = state; + + if (deg != 0) + { + box = mxUtils.getBoundingBox(box, deg); + } + + if ((model.isEdge(cell) || model.isVertex(cell)) && + box.x >= x && box.y + box.height <= bottom && + box.y >= y && box.x + box.width <= right) + { + result.push(cell); + } + + this.getAllCells(x, y, width, height, cell, result); + } + } + } + } + + return result; + }; + + // Never removes cells from parents that are being moved + var graphHandlerShouldRemoveCellsFromParent = this.graphHandler.shouldRemoveCellsFromParent; + this.graphHandler.shouldRemoveCellsFromParent = function(parent, cells, evt) + { + if (this.graph.isCellSelected(parent)) + { + return false; + } + + return graphHandlerShouldRemoveCellsFromParent.apply(this, arguments); + }; + + // Unlocks all cells + this.isCellLocked = function(cell) + { + var pState = this.view.getState(cell); + + while (pState != null) + { + if (mxUtils.getValue(pState.style, 'locked', '0') == '1') + { + return true; + } + + pState = this.view.getState(this.model.getParent(pState.cell)); + } + + return false; + }; + + var tapAndHoldSelection = null; + + // Uses this event to process mouseDown to check the selection state before it is changed + this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) + { + if (evt.getProperty('eventName') == 'mouseDown') + { + var me = evt.getProperty('event'); + var state = me.getState(); + + if (state != null && !this.isSelectionEmpty() && !this.isCellSelected(state.cell)) + { + tapAndHoldSelection = this.getSelectionCells(); + } + else + { + tapAndHoldSelection = null; + } + } + })); + + // Tap and hold on background starts rubberband for multiple selected + // cells the cell associated with the event is deselected + this.addListener(mxEvent.TAP_AND_HOLD, mxUtils.bind(this, function(sender, evt) + { + if (!mxEvent.isMultiTouchEvent(evt)) + { + var me = evt.getProperty('event'); + var cell = evt.getProperty('cell'); + + if (cell == null) + { + var pt = mxUtils.convertPoint(this.container, + mxEvent.getClientX(me), mxEvent.getClientY(me)); + rubberband.start(pt.x, pt.y); + } + else if (tapAndHoldSelection != null) + { + this.addSelectionCells(tapAndHoldSelection); + } + else if (this.getSelectionCount() > 1 && this.isCellSelected(cell)) + { + this.removeSelectionCell(cell); + } + + // Blocks further processing of the event + tapAndHoldSelection = null; + evt.consume(); + } + })); + + // On connect the target is selected and we clone the cell of the preview edge for insert + this.connectionHandler.selectCells = function(edge, target) + { + this.graph.setSelectionCell(target || edge); + }; + + // Shows connection points only if cell not selected + this.connectionHandler.constraintHandler.isStateIgnored = function(state, source) + { + return source && state.view.graph.isCellSelected(state.cell); + }; + + // Updates constraint handler if the selection changes + this.selectionModel.addListener(mxEvent.CHANGE, mxUtils.bind(this, function() + { + var ch = this.connectionHandler.constraintHandler; + + if (ch.currentFocus != null && ch.isStateIgnored(ch.currentFocus, true)) + { + ch.currentFocus = null; + ch.constraints = null; + ch.destroyIcons(); + } + + ch.destroyFocusHighlight(); + })); + + // Initializes touch interface + if (Graph.touchStyle) + { + this.initTouch(); + } + + /** + * Adds locking + */ + var graphUpdateMouseEvent = this.updateMouseEvent; + this.updateMouseEvent = function(me) + { + me = graphUpdateMouseEvent.apply(this, arguments); + + if (me.state != null && this.isCellLocked(me.getCell())) + { + me.state = null; + } + + return me; + }; + } + + //Create a unique offset object for each graph instance. + this.currentTranslate = new mxPoint(0, 0); +}; + +/** + * Specifies if the touch UI should be used (cannot detect touch in FF so always on for Windows/Linux) + */ +Graph.touchStyle = mxClient.IS_TOUCH || (mxClient.IS_FF && mxClient.IS_WIN) || navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 || window.urlParams == null || urlParams['touch'] == '1'; + +/** + * Shortcut for capability check. + */ +Graph.fileSupport = window.File != null && window.FileReader != null && window.FileList != null && + (window.urlParams == null || urlParams['filesupport'] != '0'); + +/** + * Default size for line jumps. + */ +Graph.lineJumpsEnabled = true; + +/** + * Default size for line jumps. + */ +Graph.defaultJumpSize = 6; + +/** + * Helper function (requires atob). + */ +Graph.createSvgImage = function(w, h, data) +{ + var tmp = unescape(encodeURIComponent( + '' + + '' + data + '')); + + return new mxImage('data:image/svg+xml;base64,' + ((window.btoa) ? btoa(tmp) : Base64.encode(tmp, true)), w, h) +}; + +/** + * Graph inherits from mxGraph. + */ +mxUtils.extend(Graph, mxGraph); + +/** + * Allows all values in fit. + */ +Graph.prototype.minFitScale = null; + +/** + * Allows all values in fit. + */ +Graph.prototype.maxFitScale = null; + +/** + * Sets the policy for links. Possible values are "self" to replace any framesets, + * "blank" to load the URL in and "auto" (default). + */ +Graph.prototype.linkPolicy = (urlParams['target'] == 'frame') ? 'blank' : (urlParams['target'] || 'auto'); + +/** + * Target for links that open in a new window. Default is _blank. + */ +Graph.prototype.linkTarget = (urlParams['target'] == 'frame') ? '_self' : '_blank'; + +/** + * Scrollbars are enabled on non-touch devices (not including Firefox because touch events + * cannot be detected in Firefox, see above). + */ +Graph.prototype.defaultScrollbars = !mxClient.IS_IOS; + +/** + * Specifies if the page should be visible for new files. Default is true. + */ +Graph.prototype.defaultPageVisible = true; + +/** + * Specifies if the app should run in chromeless mode. Default is false. + * This default is only used if the contructor argument is null. + */ +Graph.prototype.lightbox = false; + +/** + * + */ +Graph.prototype.defaultPageBackgroundColor = '#ffffff'; + +/** + * + */ +Graph.prototype.defaultPageBorderColor = '#ffffff'; + +/** + * + */ +Graph.prototype.defaultGraphBackground = '#ffffff'; + +/** + * Specifies the size of the size for "tiles" to be used for a graph with + * scrollbars but no visible background page. A good value is large + * enough to reduce the number of repaints that is caused for auto- + * translation, which depends on this value, and small enough to give + * a small empty buffer around the graph. Default is 400x400. + */ +Graph.prototype.scrollTileSize = new mxRectangle(0, 0, 400, 400); + +/** + * Overrides the background color and paints a transparent background. + */ +Graph.prototype.transparentBackground = true; + +/** + * Sets the default target for all links in cells. + */ +Graph.prototype.defaultEdgeLength = 80; + +/** + * Disables move of bends/segments without selecting. + */ +Graph.prototype.edgeMode = false; + +/** + * Allows all values in fit. + */ +Graph.prototype.connectionArrowsEnabled = true; + +/** + * Specifies the regular expression for matching placeholders. + */ +Graph.prototype.placeholderPattern = new RegExp('%(date\{.*\}|[^%^\{^\}]+)%', 'g'); + +/** + * Specifies the regular expression for matching placeholders. + */ +Graph.prototype.absoluteUrlPattern = new RegExp('^(?:[a-z]+:)?//', 'i'); + +/** + * Specifies the default name for the theme. Default is 'default'. + */ +Graph.prototype.defaultThemeName = 'default'; + +/** + * Specifies the default name for the theme. Default is 'default'. + */ +Graph.prototype.defaultThemes = {}; + +/** + * Base URL for relative links. + */ +Graph.prototype.baseUrl = (urlParams['base'] != null) ? + decodeURIComponent(urlParams['base']) : + (((window != window.top) ? document.referrer : + document.location.toString()).split('#')[0]); + +/** + * Specifies if the label should be edited after an insert. + */ +Graph.prototype.editAfterInsert = false; + +/** + * Installs child layout styles. + */ +Graph.prototype.init = function(container) +{ + mxGraph.prototype.init.apply(this, arguments); + + // Intercepts links with no target attribute and opens in new window + this.cellRenderer.initializeLabel = function(state, shape) + { + mxCellRenderer.prototype.initializeLabel.apply(this, arguments); + + // Checks tolerance for clicks on links + var tol = state.view.graph.tolerance; + var handleClick = true; + var first = null; + + var down = mxUtils.bind(this, function(evt) + { + handleClick = true; + first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + }); + + var move = mxUtils.bind(this, function(evt) + { + handleClick = handleClick && first != null && + Math.abs(first.x - mxEvent.getClientX(evt)) < tol && + Math.abs(first.y - mxEvent.getClientY(evt)) < tol; + }); + + var up = mxUtils.bind(this, function(evt) + { + if (handleClick) + { + var elt = mxEvent.getSource(evt) + + while (elt != null && elt != shape.node) + { + if (elt.nodeName.toLowerCase() == 'a') + { + state.view.graph.labelLinkClicked(state, elt, evt); + break; + } + + elt = elt.parentNode; + } + } + }); + + mxEvent.addGestureListeners(shape.node, down, move, up); + mxEvent.addListener(shape.node, 'click', function(evt) + { + mxEvent.consume(evt); + }); + }; + + this.initLayoutManager(); +}; + +/** + * Implements zoom and offset via CSS transforms. This is currently only used + * in read-only as there are fewer issues with the mxCellState not being scaled + * and translated. + * + * KNOWN ISSUES TO FIX: + * - Apply CSS transforms to HTML labels in IE11 + */ +(function() +{ + /** + * Uses CSS transforms for scale and translate. + */ + Graph.prototype.useCssTransforms = false; + + /** + * Contains the scale. + */ + Graph.prototype.currentScale = 1; + + /** + * Contains the offset. + */ + Graph.prototype.currentTranslate = new mxPoint(0, 0); + + /** + * Only foreignObject supported for now (no IE11). + */ + Graph.prototype.isCssTransformsSupported = function() + { + return this.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO; + }; + + /** + * Function: getCellAt + * + * Needs to modify original method for recursive call. + */ + Graph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn) + { + if (this.useCssTransforms) + { + x = x / this.currentScale - this.currentTranslate.x; + y = y / this.currentScale - this.currentTranslate.y; + } + + return this.getScaledCellAt.apply(this, arguments); + }; + + /** + * Function: getScaledCellAt + * + * Overridden for recursion. + */ + Graph.prototype.getScaledCellAt = function(x, y, parent, vertices, edges, ignoreFn) + { + vertices = (vertices != null) ? vertices : true; + edges = (edges != null) ? edges : true; + + if (parent == null) + { + parent = this.getCurrentRoot(); + + if (parent == null) + { + parent = this.getModel().getRoot(); + } + } + + if (parent != null) + { + var childCount = this.model.getChildCount(parent); + + for (var i = childCount - 1; i >= 0; i--) + { + var cell = this.model.getChildAt(parent, i); + var result = this.getScaledCellAt(x, y, cell, vertices, edges, ignoreFn); + + if (result != null) + { + return result; + } + else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) || + vertices && this.model.isVertex(cell))) + { + var state = this.view.getState(cell); + + if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) && + this.intersects(state, x, y)) + { + return cell; + } + } + } + } + + return null; + }; + + + /** + * Function: repaint + * + * Updates the highlight after a change of the model or view. + */ + mxCellHighlight.prototype.getStrokeWidth = function(state) + { + var s = this.strokeWidth; + + if (this.graph.useCssTransforms) + { + s /= this.graph.currentScale; + } + + return s; + }; + + /** + * Function: getGraphBounds + * + * Overrides getGraphBounds to use bounding box from SVG. + */ + mxGraphView.prototype.getGraphBounds = function() + { + var b = this.graphBounds; + + if (this.graph.useCssTransforms) + { + var t = this.graph.currentTranslate; + var s = this.graph.currentScale; + + b = new mxRectangle( + (b.x + t.x) * s, (b.y + t.y) * s, + b.width * s, b.height * s); + } + + return b; + }; + + /** + * Function: viewStateChanged + * + * Overrides to bypass full cell tree validation. + * TODO: Check if this improves performance + */ + mxGraphView.prototype.viewStateChanged = function() + { + if (this.graph.useCssTransforms) + { + this.validate(); + this.graph.sizeDidChange(); + } + else + { + this.revalidate(); + this.graph.sizeDidChange(); + } + }; + + /** + * Function: validate + * + * Overrides validate to normalize validation view state and pass + * current state to CSS transform. + */ + var graphViewValidate = mxGraphView.prototype.validate; + + mxGraphView.prototype.validate = function(cell) + { + if (this.graph.useCssTransforms) + { + this.graph.currentScale = this.scale; + this.graph.currentTranslate.x = this.translate.x; + this.graph.currentTranslate.y = this.translate.y; + + this.scale = 1; + this.translate.x = 0; + this.translate.y = 0; + } + + graphViewValidate.apply(this, arguments); + + if (this.graph.useCssTransforms) + { + this.graph.updateCssTransform(); + + this.scale = this.graph.currentScale; + this.translate.x = this.graph.currentTranslate.x; + this.translate.y = this.graph.currentTranslate.y; + } + }; + + /** + * Function: updateCssTransform + * + * Zooms out of the graph by . + */ + Graph.prototype.updateCssTransform = function() + { + var temp = this.view.getDrawPane(); + + if (temp != null) + { + var g = this.view.getDrawPane().parentNode; + var prev = g.getAttribute('transform'); + g.setAttribute('transformOrigin', '0 0'); + g.setAttribute('transform', 'scale(' + this.currentScale + ',' + this.currentScale + ')' + + 'translate(' + this.currentTranslate.x + ',' + this.currentTranslate.y + ')'); + + // Applies workarounds only if translate has changed + if (prev != g.getAttribute('transform')) + { + try + { + // Applies transform to labels outside of the SVG DOM + // Excluded via isCssTransformsSupported +// if (mxClient.NO_FO) +// { +// var transform = 'scale(' + this.currentScale + ')' + 'translate(' + +// this.currentTranslate.x + 'px,' + this.currentTranslate.y + 'px)'; +// +// this.view.states.visit(mxUtils.bind(this, function(cell, state) +// { +// if (state.text != null && state.text.node != null) +// { +// // Stores initial CSS transform that is used for the label alignment +// if (state.text.originalTransform == null) +// { +// state.text.originalTransform = state.text.node.style.transform; +// } +// +// state.text.node.style.transform = transform + state.text.originalTransform; +// } +// })); +// } + // Workaround for https://bugs.webkit.org/show_bug.cgi?id=93358 in WebKit + // Adding an absolute position DIV before the SVG seems to mitigate the problem. + if (mxClient.IS_GC) + { + if (this.mathEnabled && (this.webKitForceRepaintNode == null || + this.webKitForceRepaintNode.parentNode == null) && + this.container.firstChild.nodeName == 'svg') + { + this.webKitForceRepaintNode = document.createElement('div'); + this.webKitForceRepaintNode.style.cssText = 'position:absolute;'; + g.ownerSVGElement.parentNode.insertBefore(this.webKitForceRepaintNode, g.ownerSVGElement); + } + else if (this.webKitForceRepaintNode != null && (!this.mathEnabled || + (this.container.firstChild.nodeName != 'svg' && + this.container.firstChild != this.webKitForceRepaintNode))) + { + if (this.webKitForceRepaintNode.parentNode != null) + { + this.webKitForceRepaintNode.parentNode.removeChild(this.webKitForceRepaintNode); + } + + this.webKitForceRepaintNode = null; + } + } + // Workaround for https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4320441/ + else if (mxClient.IS_EDGE) + { + // Recommended workaround is to do this on all + // foreignObjects, but this seems to be faster + var val = g.style.display; + g.style.display = 'none'; + g.getBBox(); + g.style.display = val; + } + } + catch (e) + { + // ignore + console.log('err', e); + } + } + } + }; + + var graphViewValidateBackgroundPage = mxGraphView.prototype.validateBackgroundPage; + + mxGraphView.prototype.validateBackgroundPage = function() + { + var useCssTranforms = this.graph.useCssTransforms, scale = this.scale, + translate = this.translate; + + if (useCssTranforms) + { + this.scale = this.graph.currentScale; + this.translate = this.graph.currentTranslate; + } + + graphViewValidateBackgroundPage.apply(this, arguments); + + if (useCssTranforms) + { + this.scale = scale; + this.translate = translate; + } + }; + + var graphUpdatePageBreaks = mxGraph.prototype.updatePageBreaks; + + mxGraph.prototype.updatePageBreaks = function(visible, width, height) + { + var useCssTranforms = this.useCssTransforms, scale = this.view.scale, + translate = this.view.translate; + + if (useCssTranforms) + { + this.view.scale = 1; + this.view.translate = new mxPoint(0, 0); + this.useCssTransforms = false; + } + + graphUpdatePageBreaks.apply(this, arguments); + + if (useCssTranforms) + { + this.view.scale = scale; + this.view.translate = translate; + this.useCssTransforms = true; + } + }; + +})(); + +/** + * Sets the XML node for the current diagram. + */ +Graph.prototype.isLightboxView = function() +{ + return this.lightbox; +}; + +/** + * Installs automatic layout via styles + */ +Graph.prototype.labelLinkClicked = function(state, elt, evt) +{ + var href = elt.getAttribute('href'); + + if (href != null && !this.isCustomLink(href) && (mxEvent.isLeftMouseButton(evt) && + !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)) + { + if (!this.isEnabled() || this.isCellLocked(state.cell)) + { + var target = this.isBlankLink(href) ? this.linkTarget : '_top'; + this.openLink(this.getAbsoluteUrl(href), target); + } + + mxEvent.consume(evt); + } +}; + +/** + * Returns the size of the page format scaled with the page size. + */ +Graph.prototype.openLink = function(href, target) +{ + var result = window; + + // Workaround for blocking in same iframe + if (target == '_self' && window != window.top) + { + window.location.href = href; + } + else + { + // Avoids page reload for anchors (workaround for IE but used everywhere) + if (href.substring(0, this.baseUrl.length) == this.baseUrl && + href.charAt(this.baseUrl.length) == '#' && + target == '_top' && window == window.top) + { + var hash = href.split('#')[1]; + + // Forces navigation if on same hash + if (window.location.hash == '#' + hash) + { + window.location.hash = ''; + } + + window.location.hash = hash; + } + else + { + result = window.open(href, target); + } + } + + return result; +}; + +/** + * Adds support for page links. + */ +Graph.prototype.getLinkTitle = function(href) +{ + return href.substring(href.lastIndexOf('/') + 1); +}; + +/** + * Adds support for page links. + */ +Graph.prototype.isCustomLink = function(href) +{ + return href.substring(0, 5) == 'data:'; +}; + +/** + * Adds support for page links. + */ +Graph.prototype.customLinkClicked = function(link) +{ + console.log('customLinkClicked not implemented'); + // Hook for subclassers +}; + +/** + * Returns true if the fiven href references an external protocol that + * should never open in a new window. Default returns true for mailto. + */ +Graph.prototype.isExternalProtocol = function(href) +{ + return href.substring(0, 7) === 'mailto:'; +}; + +/** + * Hook for links to open in same window. Default returns true for anchors, + * links to same domain or if target == 'self' in the config. + */ +Graph.prototype.isBlankLink = function(href) +{ + return !this.isExternalProtocol(href) && + (this.linkPolicy === 'blank' || + (this.linkPolicy !== 'self' && + !this.isRelativeUrl(href) && + href.substring(0, this.domainUrl.length) !== this.domainUrl)); +}; + +/** + * + */ +Graph.prototype.isRelativeUrl = function(url) +{ + return url != null && !this.absoluteUrlPattern.test(url) && + url.substring(0, 5) !== 'data:' && + !this.isExternalProtocol(url); +}; + +/** + * Installs automatic layout via styles + */ +Graph.prototype.initLayoutManager = function() +{ + this.layoutManager = new mxLayoutManager(this); + + this.layoutManager.getLayout = function(cell) + { + var state = this.graph.view.getState(cell); + var style = (state != null) ? state.style : this.graph.getCellStyle(cell); + + if (style['childLayout'] == 'stackLayout') + { + var stackLayout = new mxStackLayout(this.graph, true); + stackLayout.resizeParentMax = mxUtils.getValue(style, 'resizeParentMax', '1') == '1'; + stackLayout.horizontal = mxUtils.getValue(style, 'horizontalStack', '1') == '1'; + stackLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + stackLayout.resizeLast = mxUtils.getValue(style, 'resizeLast', '0') == '1'; + stackLayout.spacing = style['stackSpacing'] || stackLayout.spacing; + stackLayout.border = style['stackBorder'] || stackLayout.border; + stackLayout.marginLeft = style['marginLeft'] || 0; + stackLayout.marginRight = style['marginRight'] || 0; + stackLayout.marginTop = style['marginTop'] || 0; + stackLayout.marginBottom = style['marginBottom'] || 0; + stackLayout.fill = true; + + return stackLayout; + } + else if (style['childLayout'] == 'treeLayout') + { + var treeLayout = new mxCompactTreeLayout(this.graph); + treeLayout.horizontal = mxUtils.getValue(style, 'horizontalTree', '1') == '1'; + treeLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + treeLayout.groupPadding = mxUtils.getValue(style, 'parentPadding', 20); + treeLayout.levelDistance = mxUtils.getValue(style, 'treeLevelDistance', 30); + treeLayout.maintainParentLocation = true; + treeLayout.edgeRouting = false; + treeLayout.resetEdges = false; + + return treeLayout; + } + else if (style['childLayout'] == 'flowLayout') + { + var flowLayout = new mxHierarchicalLayout(this.graph, mxUtils.getValue(style, + 'flowOrientation', mxConstants.DIRECTION_EAST)); + flowLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; + flowLayout.parentBorder = mxUtils.getValue(style, 'parentPadding', 20); + flowLayout.maintainParentLocation = true; + + // Special undocumented styles for changing the hierarchical + flowLayout.intraCellSpacing = mxUtils.getValue(style, 'intraCellSpacing', mxHierarchicalLayout.prototype.intraCellSpacing); + flowLayout.interRankCellSpacing = mxUtils.getValue(style, 'interRankCellSpacing', mxHierarchicalLayout.prototype.interRankCellSpacing); + flowLayout.interHierarchySpacing = mxUtils.getValue(style, 'interHierarchySpacing', mxHierarchicalLayout.prototype.interHierarchySpacing); + flowLayout.parallelEdgeSpacing = mxUtils.getValue(style, 'parallelEdgeSpacing', mxHierarchicalLayout.prototype.parallelEdgeSpacing); + + return flowLayout; + } + + return null; + }; +}; + + /** + * Returns the size of the page format scaled with the page size. + */ +Graph.prototype.getPageSize = function() +{ + return (this.pageVisible) ? new mxRectangle(0, 0, this.pageFormat.width * this.pageScale, + this.pageFormat.height * this.pageScale) : this.scrollTileSize; +}; + +/** + * Returns a rectangle describing the position and count of the + * background pages, where x and y are the position of the top, + * left page and width and height are the vertical and horizontal + * page count. + */ +Graph.prototype.getPageLayout = function() +{ + var size = this.getPageSize(); + var bounds = this.getGraphBounds(); + + if (bounds.width == 0 || bounds.height == 0) + { + return new mxRectangle(0, 0, 1, 1); + } + else + { + // Computes untransformed graph bounds + var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x); + var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y); + var w = Math.floor(bounds.width / this.view.scale); + var h = Math.floor(bounds.height / this.view.scale); + + var x0 = Math.floor(x / size.width); + var y0 = Math.floor(y / size.height); + var w0 = Math.ceil((x + w) / size.width) - x0; + var h0 = Math.ceil((y + h) / size.height) - y0; + + return new mxRectangle(x0, y0, w0, h0); + } +}; + +/** + * Sanitizes the given HTML markup. + */ +Graph.prototype.sanitizeHtml = function(value, editing) +{ + // Uses https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer + // NOTE: Original minimized sanitizer was modified to support + // data URIs for images, mailto and special data:-links. + // LATER: Add MathML to whitelisted tags + function urlX(link) + { + if (link != null && link.toString().toLowerCase().substring(0, 11) !== 'javascript:') + { + return link; + } + + return null; + }; + function idX(id) { return id }; + + return html_sanitize(value, urlX, idX); +}; + +/** + * Revalidates all cells with placeholders in the current graph model. + */ +Graph.prototype.updatePlaceholders = function() +{ + var model = this.model; + var validate = false; + + for (var key in this.model.cells) + { + var cell = this.model.cells[key]; + + if (this.isReplacePlaceholders(cell)) + { + this.view.invalidate(cell, false, false); + validate = true; + } + } + + if (validate) + { + this.view.validate(); + } +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.isReplacePlaceholders = function(cell) +{ + return cell.value != null && typeof(cell.value) == 'object' && + cell.value.getAttribute('placeholders') == '1'; +}; + +/** + * Returns true if the given mouse wheel event should be used for zooming. This + * is invoked if no dialogs are showing and returns true with Alt or Control + * (except macOS) is pressed. + */ +Graph.prototype.isZoomWheelEvent = function(evt) +{ + return mxEvent.isAltDown(evt) || (mxEvent.isMetaDown(evt) && mxClient.IS_MAC) || + (mxEvent.isControlDown(evt) && !mxClient.IS_MAC); +}; + +/** + * Adds Alt+click to select cells behind cells. + */ +Graph.prototype.isTransparentClickEvent = function(evt) +{ + return mxEvent.isAltDown(evt); +}; + +/** + * Adds ctrl+shift+connect to disable connections. + */ +Graph.prototype.isIgnoreTerminalEvent = function(evt) +{ + return mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt); +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.isSplitTarget = function(target, cells, evt) +{ + return !this.model.isEdge(cells[0]) && + !mxEvent.isAltDown(evt) && !mxEvent.isShiftDown(evt) && + mxGraph.prototype.isSplitTarget.apply(this, arguments); +}; + +/** + * Adds support for placeholders in labels. + */ +Graph.prototype.getLabel = function(cell) +{ + var result = mxGraph.prototype.getLabel.apply(this, arguments); + + if (result != null && this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') == null) + { + result = this.replacePlaceholders(cell, result); + } + + return result; +}; + +/** + * Adds labelMovable style. + */ +Graph.prototype.isLabelMovable = function(cell) +{ + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return !this.isCellLocked(cell) && + ((this.model.isEdge(cell) && this.edgeLabelsMovable) || + (this.model.isVertex(cell) && (this.vertexLabelsMovable || + mxUtils.getValue(style, 'labelMovable', '0') == '1'))); +}; + +/** + * Adds event if grid size is changed. + */ +Graph.prototype.setGridSize = function(value) +{ + this.gridSize = value; + this.fireEvent(new mxEventObject('gridSizeChanged')); +}; + +/** + * Private helper method. + */ +Graph.prototype.getGlobalVariable = function(name) +{ + var val = null; + + if (name == 'date') + { + val = new Date().toLocaleDateString(); + } + else if (name == 'time') + { + val = new Date().toLocaleTimeString(); + } + else if (name == 'timestamp') + { + val = new Date().toLocaleString(); + } + else if (name.substring(0, 5) == 'date{') + { + var fmt = name.substring(5, name.length - 1); + val = this.formatDate(new Date(), fmt); + } + + return val; +}; + +/** + * Formats a date, see http://blog.stevenlevithan.com/archives/date-time-format + */ +Graph.prototype.formatDate = function(date, mask, utc) +{ + // LATER: Cache regexs + if (this.dateFormatCache == null) + { + this.dateFormatCache = { + i18n: { + dayNames: [ + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" + ], + monthNames: [ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" + ] + }, + + masks: { + "default": "ddd mmm dd yyyy HH:MM:ss", + shortDate: "m/d/yy", + mediumDate: "mmm d, yyyy", + longDate: "mmmm d, yyyy", + fullDate: "dddd, mmmm d, yyyy", + shortTime: "h:MM TT", + mediumTime: "h:MM:ss TT", + longTime: "h:MM:ss TT Z", + isoDate: "yyyy-mm-dd", + isoTime: "HH:MM:ss", + isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", + isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" + } + }; + } + + var dF = this.dateFormatCache; + var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, + timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, + timezoneClip = /[^-+\dA-Z]/g, + pad = function (val, len) { + val = String(val); + len = len || 2; + while (val.length < len) val = "0" + val; + return val; + }; + + // You can't provide utc if you skip other args (use the "UTC:" mask prefix) + if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { + mask = date; + date = undefined; + } + + // Passing date through Date applies Date.parse, if necessary + date = date ? new Date(date) : new Date; + if (isNaN(date)) throw SyntaxError("invalid date"); + + mask = String(dF.masks[mask] || mask || dF.masks["default"]); + + // Allow setting the utc argument via the mask + if (mask.slice(0, 4) == "UTC:") { + mask = mask.slice(4); + utc = true; + } + + var _ = utc ? "getUTC" : "get", + d = date[_ + "Date"](), + D = date[_ + "Day"](), + m = date[_ + "Month"](), + y = date[_ + "FullYear"](), + H = date[_ + "Hours"](), + M = date[_ + "Minutes"](), + s = date[_ + "Seconds"](), + L = date[_ + "Milliseconds"](), + o = utc ? 0 : date.getTimezoneOffset(), + flags = { + d: d, + dd: pad(d), + ddd: dF.i18n.dayNames[D], + dddd: dF.i18n.dayNames[D + 7], + m: m + 1, + mm: pad(m + 1), + mmm: dF.i18n.monthNames[m], + mmmm: dF.i18n.monthNames[m + 12], + yy: String(y).slice(2), + yyyy: y, + h: H % 12 || 12, + hh: pad(H % 12 || 12), + H: H, + HH: pad(H), + M: M, + MM: pad(M), + s: s, + ss: pad(s), + l: pad(L, 3), + L: pad(L > 99 ? Math.round(L / 10) : L), + t: H < 12 ? "a" : "p", + tt: H < 12 ? "am" : "pm", + T: H < 12 ? "A" : "P", + TT: H < 12 ? "AM" : "PM", + Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), + o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), + S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] + }; + + return mask.replace(token, function ($0) + { + return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); + }); +}; + +/** + * + */ +Graph.prototype.createLayersDialog = function() +{ + var div = document.createElement('div'); + div.style.position = 'absolute'; + + var model = this.getModel(); + var childCount = model.getChildCount(model.root); + + for (var i = 0; i < childCount; i++) + { + (mxUtils.bind(this, function(layer) + { + var span = document.createElement('div'); + span.style.overflow = 'hidden'; + span.style.textOverflow = 'ellipsis'; + span.style.padding = '2px'; + span.style.whiteSpace = 'nowrap'; + + var cb = document.createElement('input'); + cb.style.display = 'inline-block'; + cb.setAttribute('type', 'checkbox'); + + if (model.isVisible(layer)) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + } + + span.appendChild(cb); + + var title = this.convertValueToString(layer) || (mxResources.get('background') || 'Background'); + span.setAttribute('title', title); + mxUtils.write(span, title); + div.appendChild(span); + + mxEvent.addListener(cb, 'click', function() + { + if (cb.getAttribute('checked') != null) + { + cb.removeAttribute('checked'); + } + else + { + cb.setAttribute('checked', 'checked'); + } + + model.setVisible(layer, cb.checked); + }); + })(model.getChildAt(model.root, i))); + } + + return div; +}; + +/** + * Private helper method. + */ +Graph.prototype.replacePlaceholders = function(cell, str) +{ + var result = []; + + if (str != null) + { + var last = 0; + var math = []; + + while (match = this.placeholderPattern.exec(str)) + { + var val = match[0]; + + if (val.length > 2 && val != '%label%' && val != '%tooltip%') + { + var tmp = null; + + if (match.index > last && str.charAt(match.index - 1) == '%') + { + tmp = val.substring(1); + } + else + { + var name = val.substring(1, val.length - 1); + + // Workaround for invalid char for getting attribute in older versions of IE + if (name.indexOf('{') < 0) + { + var current = cell; + + while (tmp == null && current != null) + { + if (current.value != null && typeof(current.value) == 'object') + { + tmp = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? + current.getAttribute(name) : '') : null; + } + + current = this.model.getParent(current); + } + } + + if (tmp == null) + { + tmp = this.getGlobalVariable(name); + } + } + + result.push(str.substring(last, match.index) + ((tmp != null) ? tmp : val)); + last = match.index + val.length; + } + } + + result.push(str.substring(last)); + } + + return result.join(''); +}; + +/** + * Selects cells for connect vertex return value. + */ +Graph.prototype.selectCellsForConnectVertex = function(cells, evt, hoverIcons) +{ + // Selects only target vertex if one exists + if (cells.length == 2 && this.model.isVertex(cells[1])) + { + this.setSelectionCell(cells[1]); + + if (hoverIcons != null) + { + // Adds hover icons to new target vertex for touch devices + if (mxEvent.isTouchEvent(evt)) + { + hoverIcons.update(hoverIcons.getState(this.view.getState(cells[1]))); + } + else + { + // Hides hover icons after click with mouse + hoverIcons.reset(); + } + } + + this.scrollCellToVisible(cells[1]); + } + else + { + this.setSelectionCells(cells); + } +}; + +/** + * Adds a connection to the given vertex. + */ +Graph.prototype.connectVertex = function(source, direction, length, evt, forceClone, ignoreCellAt) +{ + ignoreCellAt = (ignoreCellAt) ? ignoreCellAt : false; + + var pt = (source.geometry.relative && source.parent.geometry != null) ? + new mxPoint(source.parent.geometry.width * source.geometry.x, source.parent.geometry.height * source.geometry.y) : + new mxPoint(source.geometry.x, source.geometry.y); + + if (direction == mxConstants.DIRECTION_NORTH) + { + pt.x += source.geometry.width / 2; + pt.y -= length ; + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + pt.x += source.geometry.width / 2; + pt.y += source.geometry.height + length; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + pt.x -= length; + pt.y += source.geometry.height / 2; + } + else + { + pt.x += source.geometry.width + length; + pt.y += source.geometry.height / 2; + } + + var parentState = this.view.getState(this.model.getParent(source)); + var s = this.view.scale; + var t = this.view.translate; + var dx = t.x * s; + var dy = t.y * s; + + if (this.model.isVertex(parentState.cell)) + { + dx = parentState.x; + dy = parentState.y; + } + + // Workaround for relative child cells + if (this.model.isVertex(source.parent) && source.geometry.relative) + { + pt.x += source.parent.geometry.x; + pt.y += source.parent.geometry.y; + } + + // Checks actual end point of edge for target cell + var target = (ignoreCellAt || (mxEvent.isControlDown(evt) && !forceClone)) ? + null : this.getCellAt(dx + pt.x * s, dy + pt.y * s); + + if (this.model.isAncestor(target, source)) + { + target = null; + } + + // Checks if target or ancestor is locked + var temp = target; + + while (temp != null) + { + if (this.isCellLocked(temp)) + { + target = null; + break; + } + + temp = this.model.getParent(temp); + } + + // Checks if source and target intersect + if (target != null) + { + var sourceState = this.view.getState(source); + var targetState = this.view.getState(target); + + if (sourceState != null && targetState != null && mxUtils.intersects(sourceState, targetState)) + { + target = null; + } + } + + var duplicate = !mxEvent.isShiftDown(evt) || forceClone; + + if (duplicate) + { + if (direction == mxConstants.DIRECTION_NORTH) + { + pt.y -= source.geometry.height / 2; + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + pt.y += source.geometry.height / 2; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + pt.x -= source.geometry.width / 2; + } + else + { + pt.x += source.geometry.width / 2; + } + } + + // Uses connectable parent vertex if one exists + if (target != null && !this.isCellConnectable(target)) + { + var parent = this.getModel().getParent(target); + + if (this.getModel().isVertex(parent) && this.isCellConnectable(parent)) + { + target = parent; + } + } + + if (target == source || this.model.isEdge(target) || !this.isCellConnectable(target)) + { + target = null; + } + + var result = []; + + this.model.beginUpdate(); + try + { + var realTarget = target; + + if (realTarget == null && duplicate) + { + // Handles relative children + var cellToClone = source; + var geo = this.getCellGeometry(source); + + while (geo != null && geo.relative) + { + cellToClone = this.getModel().getParent(cellToClone); + geo = this.getCellGeometry(cellToClone); + } + + // Handle consistuents for cloning + var state = this.view.getState(cellToClone); + var style = (state != null) ? state.style : this.getCellStyle(cellToClone); + + if (mxUtils.getValue(style, 'part', false)) + { + var tmpParent = this.model.getParent(cellToClone); + + if (this.model.isVertex(tmpParent)) + { + cellToClone = tmpParent; + } + } + + realTarget = this.duplicateCells([cellToClone], false)[0]; + + var geo = this.getCellGeometry(realTarget); + + if (geo != null) + { + geo.x = pt.x - geo.width / 2; + geo.y = pt.y - geo.height / 2; + } + } + + // Never connects children in stack layouts + var layout = null; + + if (this.layoutManager != null) + { + layout = this.layoutManager.getLayout(this.model.getParent(source)); + } + + var edge = ((mxEvent.isControlDown(evt) && duplicate) || (target == null && layout != null && layout.constructor == mxStackLayout)) ? null : + this.insertEdge(this.model.getParent(source), null, '', source, realTarget, this.createCurrentEdgeStyle()); + + // Inserts edge before source + if (edge != null && this.connectionHandler.insertBeforeSource) + { + var index = null; + var tmp = source; + + while (tmp.parent != null && tmp.geometry != null && + tmp.geometry.relative && tmp.parent != edge.parent) + { + tmp = this.model.getParent(tmp); + } + + if (tmp != null && tmp.parent != null && tmp.parent == edge.parent) + { + var index = tmp.parent.getIndex(tmp); + this.model.add(tmp.parent, edge, index); + } + } + + // Special case: Click on west icon puts clone before cell + if (target == null && realTarget != null && layout != null && source.parent != null && + layout.constructor == mxStackLayout && direction == mxConstants.DIRECTION_WEST) + { + var index = source.parent.getIndex(source); + this.model.add(source.parent, realTarget, index); + } + + if (edge != null) + { + // Uses elbow edges with vertical or horizontal direction +// var elbowValue = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH) ? 'vertical' : 'horizontal'; +// edge.style = mxUtils.setStyle(edge.style, 'edgeStyle', 'elbowEdgeStyle'); +// edge.style = mxUtils.setStyle(edge.style, 'elbow', elbowValue); +// edge.style = mxUtils.setStyle(edge.style, 'sourcePortConstraint', direction); + result.push(edge); + } + + if (target == null && realTarget != null) + { + result.push(realTarget); + } + + if (realTarget == null && edge != null) + { + edge.geometry.setTerminalPoint(pt, false); + } + + if (edge != null) + { + this.fireEvent(new mxEventObject('cellsInserted', 'cells', [edge])); + } + } + finally + { + this.model.endUpdate(); + } + + return result; +}; + +/** + * Returns all labels in the diagram as a string. + */ +Graph.prototype.getIndexableText = function() +{ + var tmp = document.createElement('div'); + var labels = []; + var label = ''; + + for (var key in this.model.cells) + { + var cell = this.model.cells[key]; + + if (this.model.isVertex(cell) || this.model.isEdge(cell)) + { + if (this.isHtmlLabel(cell)) + { + tmp.innerHTML = this.getLabel(cell); + label = mxUtils.extractTextWithWhitespace([tmp]); + } + else + { + label = this.getLabel(cell); + } + + label = mxUtils.trim(label.replace(/[\x00-\x1F\x7F-\x9F]|\s+/g, ' ')); + + if (label.length > 0) + { + labels.push(label); + } + } + } + + return labels.join(' '); +}; + +/** + * Returns the label for the given cell. + */ +Graph.prototype.convertValueToString = function(cell) +{ + if (cell.value != null && typeof(cell.value) == 'object') + { + if (this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') != null) + { + var name = cell.getAttribute('placeholder'); + var current = cell; + var result = null; + + while (result == null && current != null) + { + if (current.value != null && typeof(current.value) == 'object') + { + result = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? + current.getAttribute(name) : '') : null; + } + + current = this.model.getParent(current); + } + + return result || ''; + } + else + { + return cell.value.getAttribute('label'); + } + } + + return mxGraph.prototype.convertValueToString.apply(this, arguments); +}; + +/** + * Returns the link for the given cell. + */ +Graph.prototype.getLinksForState = function(state) +{ + if (state != null && state.text != null && state.text.node != null) + { + return state.text.node.getElementsByTagName('a'); + } + + return null; +}; + +/** + * Returns the link for the given cell. + */ +Graph.prototype.getLinkForCell = function(cell) +{ + if (cell.value != null && typeof(cell.value) == 'object') + { + var link = cell.value.getAttribute('link'); + + // Removes links with leading javascript: protocol + // TODO: Check more possible attack vectors + if (link != null && link.toLowerCase().substring(0, 11) === 'javascript:') + { + link = link.substring(11); + } + + return link; + } + + return null; +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.getCellStyle = function(cell) +{ + var style = mxGraph.prototype.getCellStyle.apply(this, arguments); + + if (cell != null && this.layoutManager != null) + { + var parent = this.model.getParent(cell); + + if (this.model.isVertex(parent) && this.isCellCollapsed(cell)) + { + var layout = this.layoutManager.getLayout(parent); + + if (layout != null && layout.constructor == mxStackLayout) + { + style[mxConstants.STYLE_HORIZONTAL] = !layout.horizontal; + } + } + } + + return style; +}; + +/** + * Disables alternate width persistence for stack layout parents + */ +Graph.prototype.updateAlternateBounds = function(cell, geo, willCollapse) +{ + if (cell != null && geo != null && this.layoutManager != null && geo.alternateBounds != null) + { + var layout = this.layoutManager.getLayout(this.model.getParent(cell)); + + if (layout != null && layout.constructor == mxStackLayout) + { + if (layout.horizontal) + { + geo.alternateBounds.height = 0; + } + else + { + geo.alternateBounds.width = 0; + } + } + } + + mxGraph.prototype.updateAlternateBounds.apply(this, arguments); +}; + +/** + * Adds Shift+collapse/expand and size management for folding inside stack + */ +Graph.prototype.isMoveCellsEvent = function(evt) +{ + return mxEvent.isShiftDown(evt); +}; + +/** + * Adds Shift+collapse/expand and size management for folding inside stack + */ +Graph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt) +{ + recurse = (recurse != null) ? recurse : false; + + if (cells == null) + { + cells = this.getFoldableCells(this.getSelectionCells(), collapse); + } + + if (cells != null) + { + this.model.beginUpdate(); + + try + { + mxGraph.prototype.foldCells.apply(this, arguments); + + // Resizes all parent stacks if alt is not pressed + if (this.layoutManager != null) + { + for (var i = 0; i < cells.length; i++) + { + var state = this.view.getState(cells[i]); + var geo = this.getCellGeometry(cells[i]); + + if (state != null && geo != null) + { + var dx = Math.round(geo.width - state.width / this.view.scale); + var dy = Math.round(geo.height - state.height / this.view.scale); + + if (dy != 0 || dx != 0) + { + var parent = this.model.getParent(cells[i]); + var layout = this.layoutManager.getLayout(parent); + + if (layout == null) + { + // Moves cells to the right and down after collapse/expand + if (evt != null && this.isMoveCellsEvent(evt)) + { + this.moveSiblings(state, parent, dx, dy); + } + } + else if ((evt == null || !mxEvent.isAltDown(evt)) && layout.constructor == mxStackLayout && !layout.resizeLast) + { + this.resizeParentStacks(parent, layout, dx, dy); + } + } + } + } + } + } + finally + { + this.model.endUpdate(); + } + + // Selects cells after folding + if (this.isEnabled()) + { + this.setSelectionCells(cells); + } + } +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.moveSiblings = function(state, parent, dx, dy) +{ + this.model.beginUpdate(); + try + { + var cells = this.getCellsBeyond(state.x, state.y, parent, true, true); + + for (var i = 0; i < cells.length; i++) + { + if (cells[i] != state.cell) + { + var tmp = this.view.getState(cells[i]); + var geo = this.getCellGeometry(cells[i]); + + if (tmp != null && geo != null) + { + geo = geo.clone(); + geo.translate(Math.round(dx * Math.max(0, Math.min(1, (tmp.x - state.x) / state.width))), + Math.round(dy * Math.max(0, Math.min(1, (tmp.y - state.y) / state.height)))); + this.model.setGeometry(cells[i], geo); + } + } + } + } + finally + { + this.model.endUpdate(); + } +}; + +/** + * Overrides label orientation for collapsed swimlanes inside stack. + */ +Graph.prototype.resizeParentStacks = function(parent, layout, dx, dy) +{ + if (this.layoutManager != null && layout != null && layout.constructor == mxStackLayout && !layout.resizeLast) + { + this.model.beginUpdate(); + try + { + var dir = layout.horizontal; + + // Bubble resize up for all parent stack layouts with same orientation + while (parent != null && layout != null && layout.constructor == mxStackLayout && + layout.horizontal == dir && !layout.resizeLast) + { + var pgeo = this.getCellGeometry(parent); + var pstate = this.view.getState(parent); + + if (pstate != null && pgeo != null) + { + pgeo = pgeo.clone(); + + if (layout.horizontal) + { + pgeo.width += dx + Math.min(0, pstate.width / this.view.scale - pgeo.width); + } + else + { + pgeo.height += dy + Math.min(0, pstate.height / this.view.scale - pgeo.height); + } + + this.model.setGeometry(parent, pgeo); + } + + parent = this.model.getParent(parent); + layout = this.layoutManager.getLayout(parent); + } + } + finally + { + this.model.endUpdate(); + } + } +}; + +/** + * Disables drill-down for non-swimlanes. + */ +Graph.prototype.isContainer = function(cell) +{ + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + if (this.isSwimlane(cell)) + { + return style['container'] != '0'; + } + else + { + return style['container'] == '1'; + } +}; + +/** + * Adds a connectable style. + */ +Graph.prototype.isCellConnectable = function(cell) +{ + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return (style['connectable'] != null) ? style['connectable'] != '0' : + mxGraph.prototype.isCellConnectable.apply(this, arguments); +}; + +/** + * Function: selectAll + * + * Selects all children of the given parent cell or the children of the + * default parent if no parent is specified. To select leaf vertices and/or + * edges use . + * + * Parameters: + * + * parent - Optional whose children should be selected. + * Default is . + */ +Graph.prototype.selectAll = function(parent) +{ + parent = parent || this.getDefaultParent(); + + if (!this.isCellLocked(parent)) + { + mxGraph.prototype.selectAll.apply(this, arguments); + } +}; + +/** + * Function: selectCells + * + * Selects all vertices and/or edges depending on the given boolean + * arguments recursively, starting at the given parent or the default + * parent if no parent is specified. Use to select all cells. + * For vertices, only cells with no children are selected. + * + * Parameters: + * + * vertices - Boolean indicating if vertices should be selected. + * edges - Boolean indicating if edges should be selected. + * parent - Optional that acts as the root of the recursion. + * Default is . + */ +Graph.prototype.selectCells = function(vertices, edges, parent) +{ + parent = parent || this.getDefaultParent(); + + if (!this.isCellLocked(parent)) + { + mxGraph.prototype.selectCells.apply(this, arguments); + } +}; + +/** + * Function: getSwimlaneAt + * + * Returns the bottom-most swimlane that intersects the given point (x, y) + * in the cell hierarchy that starts at the given parent. + * + * Parameters: + * + * x - X-coordinate of the location to be checked. + * y - Y-coordinate of the location to be checked. + * parent - that should be used as the root of the recursion. + * Default is . + */ +Graph.prototype.getSwimlaneAt = function (x, y, parent) +{ + parent = parent || this.getDefaultParent(); + + if (!this.isCellLocked(parent)) + { + return mxGraph.prototype.getSwimlaneAt.apply(this, arguments); + } + + return null; +}; + +/** + * Disables folding for non-swimlanes. + */ +Graph.prototype.isCellFoldable = function(cell) +{ + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return this.foldingEnabled && !this.isCellLocked(cell) && + ((this.isContainer(cell) && style['collapsible'] != '0') || + (!this.isContainer(cell) && style['collapsible'] == '1')); +}; + +/** + * Stops all interactions and clears the selection. + */ +Graph.prototype.reset = function() +{ + if (this.isEditing()) + { + this.stopEditing(true); + } + + this.escape(); + + if (!this.isSelectionEmpty()) + { + this.clearSelection(); + } +}; + +/** + * Overridden to limit zoom to 1% - 16.000%. + */ +Graph.prototype.zoom = function(factor, center) +{ + factor = Math.max(0.01, Math.min(this.view.scale * factor, 160)) / this.view.scale; + + mxGraph.prototype.zoom.apply(this, arguments); +}; + +/** + * Function: zoomIn + * + * Zooms into the graph by . + */ +Graph.prototype.zoomIn = function() +{ + // Switches to 1% zoom steps below 15% + if (this.view.scale < 0.15) + { + this.zoom((this.view.scale + 0.01) / this.view.scale); + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.zoom((Math.round(this.view.scale * this.zoomFactor * 20) / 20) / this.view.scale); + } +}; + +/** + * Function: zoomOut + * + * Zooms out of the graph by . + */ +Graph.prototype.zoomOut = function() +{ + // Switches to 1% zoom steps below 15% + if (this.view.scale <= 0.15) + { + this.zoom((this.view.scale - 0.01) / this.view.scale); + } + else + { + // Uses to 5% zoom steps for better grid rendering in webkit + // and to avoid rounding errors for zoom steps + this.zoom((Math.round(this.view.scale * (1 / this.zoomFactor) * 20) / 20) / this.view.scale); + } +}; + +/** + * Overrides tooltips to show custom tooltip or metadata. + */ +Graph.prototype.getTooltipForCell = function(cell) +{ + var tip = ''; + + if (mxUtils.isNode(cell.value)) + { + var tmp = cell.value.getAttribute('tooltip'); + + if (tmp != null) + { + if (tmp != null && this.isReplacePlaceholders(cell)) + { + tmp = this.replacePlaceholders(cell, tmp); + } + + tip = this.sanitizeHtml(tmp); + } + else + { + var ignored = ['label', 'tooltip', 'placeholders', 'placeholder']; + var attrs = cell.value.attributes; + var temp = []; + + // Hides links in edit mode + if (this.isEnabled()) + { + ignored.push('link'); + } + + for (var i = 0; i < attrs.length; i++) + { + if (mxUtils.indexOf(ignored, attrs[i].nodeName) < 0 && attrs[i].nodeValue.length > 0) + { + temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); + } + } + + // Sorts by name + temp.sort(function(a, b) + { + if (a.name < b.name) + { + return -1; + } + else if (a.name > b.name) + { + return 1; + } + else + { + return 0; + } + }); + + for (var i = 0; i < temp.length; i++) + { + if (temp[i].name != 'link' || !this.isCustomLink(temp[i].value)) + { + tip += ((temp[i].name != 'link') ? '' + temp[i].name + ': ' : '') + + mxUtils.htmlEntities(temp[i].value) + '\n'; + } + } + + if (tip.length > 0) + { + tip = tip.substring(0, tip.length - 1); + + if (mxClient.IS_SVG) + { + tip = '
' + tip + '
'; + } + } + } + } + + return tip; +}; + +/** + * Turns the given string into an array. + */ +Graph.prototype.stringToBytes = function(str) +{ + var arr = new Array(str.length); + + for (var i = 0; i < str.length; i++) + { + arr[i] = str.charCodeAt(i); + } + + return arr; +}; + +/** + * Turns the given array into a string. + */ +Graph.prototype.bytesToString = function(arr) +{ + var result = new Array(arr.length); + + for (var i = 0; i < arr.length; i++) + { + result[i] = String.fromCharCode(arr[i]); + } + + return result.join(''); +}; + +/** + * Returns a base64 encoded version of the compressed string. + */ +Graph.prototype.compress = function(data) +{ + if (data == null || data.length == 0 || typeof(pako) === 'undefined') + { + return data; + } + else + { + var tmp = this.bytesToString(pako.deflateRaw(encodeURIComponent(data))); + + return (window.btoa) ? btoa(tmp) : Base64.encode(tmp, true); + } +}; + +/** + * Returns a decompressed version of the base64 encoded string. + */ +Graph.prototype.decompress = function(data) +{ + if (data == null || data.length == 0 || typeof(pako) === 'undefined') + { + return data; + } + else + { + var tmp = (window.atob) ? atob(data) : Base64.decode(data, true); + + return this.zapGremlins(decodeURIComponent( + this.bytesToString(pako.inflateRaw(tmp)))); + } +}; + +/** + * Removes all illegal control characters with ASCII code <32 except TAB, LF + * and CR. + */ +Graph.prototype.zapGremlins = function(text) +{ + var checked = []; + + for (var i = 0; i < text.length; i++) + { + var code = text.charCodeAt(i); + + // Removes all control chars except TAB, LF and CR + if (code >= 32 || code == 9 || code == 10 || code == 13) + { + checked.push(text.charAt(i)); + } + } + + return checked.join(''); +}; + +/** + * Hover icons are used for hover, vertex handler and drag from sidebar. + */ +HoverIcons = function(graph) +{ + this.graph = graph; + this.init(); +}; + +/** + * Up arrow. + */ +HoverIcons.prototype.arrowSpacing = 6; + +/** + * Delay to switch to another state for overlapping bbox. Default is 500ms. + */ +HoverIcons.prototype.updateDelay = 500; + +/** + * Delay to switch between states. Default is 140ms. + */ +HoverIcons.prototype.activationDelay = 140; + +/** + * Up arrow. + */ +HoverIcons.prototype.currentState = null; + +/** + * Up arrow. + */ +HoverIcons.prototype.activeArrow = null; + +/** + * Up arrow. + */ +HoverIcons.prototype.inactiveOpacity = 15; + +/** + * Up arrow. + */ +HoverIcons.prototype.cssCursor = 'copy'; + +/** + * Whether to hide arrows that collide with vertices. + * LATER: Add keyboard override, touch support. + */ +HoverIcons.prototype.checkCollisions = true; + +/** + * Up arrow. + */ +HoverIcons.prototype.triangleUp = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-up.png', 26, 14) : + Graph.createSvgImage(26, 14, ''); + +/** + * Right arrow. + */ +HoverIcons.prototype.triangleRight = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-right.png', 14, 26) : + Graph.createSvgImage(14, 26, ''); + +/** + * Down arrow. + */ +HoverIcons.prototype.triangleDown = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-down.png', 26, 14) : + Graph.createSvgImage(26, 14, ''); + +/** + * Left arrow. + */ +HoverIcons.prototype.triangleLeft = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-left.png', 14, 26) : + Graph.createSvgImage(14, 26, ''); + +/** + * Round target. + */ +HoverIcons.prototype.roundDrop = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/round-drop.png', 26, 26) : + Graph.createSvgImage(26, 26, ''); + +/** + * Refresh target. + */ +HoverIcons.prototype.refreshTarget = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQxNERDRTU1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQxNERDRTY1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NDE0RENFMzVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NDE0RENFNDVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsvuX50AAANaSURBVHja7FjRZ1tRGD9ZJ1NCyIQSwrivI4Q8hCpjlFDyFEoYfSp9Ko1QWnmo0If+BSXkIfo0QirTMUpeGo2EPfWllFYjZMLKLDJn53d3biU337m5J223bPbxk5t7v+/c3/2+73znO8fDOWezKM/YjMpz68Lj8ejY+QTeCCwLxOS9qPxtyN+6wAeBTwJ31CCO0cJDjXBGBN4LfIepSwykTUT1bgpuib0SONIgo8KRHOtRiCFcvUcgZeGrHPNBxLIyFPyRgTGz0xLbegJCdmzpElue5KlAIMDX19d5uVzm5+fnfDAYmMA17uEZdOx2Yvb/sHlu2S0xwymn5ufneTab5b1ej08S6EAXNrDd2dnhiUTim21MvMtwQ6yiIrWwsMDPzs64rsBmf3/fvM7n89TYlUnEllSkQqEQv7q64g+Vk5MTVXosORErU0Zer5f0FEIlw2N6MxwO82QyaXql2+2SxDqdjopYWUUsqEp45IldqtWq6UWVh/1+P7+8vCTJ4QMUJSRIEXuneoH96w8PDyeWAnhSJfCqwm6NIlaklFdXV0cGhRcQ2mlJQXK5nMq2YPEZbnteU1U2lUqN/D84OGD9fl+5fgnSrFarsUwmw0qlEru4uBjTicViTk3Cr27HSnxR+Doyz0ZE1CAWiUTusbu7y9rttlZv5fP5WDQavYfIMba4uEipfhF8XtqJoZXx/uH+sC/4vPg7OljZZQbsCmLtYzc3N6zRaJhotVrmfx0xDINtbm6athYUeXpHdbBNaqZUKpWxWXV7e2vex+xaWVnhc3NzjrPUXgexyCt0m67LBV7uJMITjqRE4o8tZeg8FPpFitgapYxiOC0poFgsji1jKNo6BZZckrAGUtJsNk1vqAihCBcKhTE7hNWhqw2qFnGy5UFOUYJVIJ1OjzSE+BCEilon0URavRmBqnbbQ00AXbm+vnZc9O1tj72OnQoc2+cwygRkb2+P1et17ZoEm3g87lRmjgWZ00kbXkNuse6/Bu2wlegIxfb2tuvWGroO4bO2c4bbzUh60mxDXm1sbJhhxkQYnhS4h2fUZoRAWnf7lv8N27f8P7Xhnekjgpk+VKGOoQbsiY+hhhtF3YO7twIJ+ULvUGv+GQ2fQEvWxI/THNx5/p/BaspPAQYAqStgiSQwCDoAAAAASUVORK5CYII=' : + IMAGE_PATH + '/refresh.png', 38, 38); + +/** + * Tolerance for hover icon clicks. + */ +HoverIcons.prototype.tolerance = (mxClient.IS_TOUCH) ? 6 : 0; + +/** + * + */ +HoverIcons.prototype.init = function() +{ + this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip')); + this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip')); + this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip')); + this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip')); + + this.elts = [this.arrowUp, this.arrowRight, this.arrowDown, this.arrowLeft]; + + this.repaintHandler = mxUtils.bind(this, function() + { + this.repaint(); + }); + + this.graph.selectionModel.addListener(mxEvent.CHANGE, this.repaintHandler); + this.graph.model.addListener(mxEvent.CHANGE, this.repaintHandler); + this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler); + this.graph.view.addListener(mxEvent.TRANSLATE, this.repaintHandler); + this.graph.view.addListener(mxEvent.SCALE, this.repaintHandler); + this.graph.view.addListener(mxEvent.DOWN, this.repaintHandler); + this.graph.view.addListener(mxEvent.UP, this.repaintHandler); + this.graph.addListener(mxEvent.ROOT, this.repaintHandler); + + // Resets the mouse point on escape + this.graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function() + { + this.mouseDownPoint = null; + })); + + // Removes hover icons if mouse leaves the container + mxEvent.addListener(this.graph.container, 'mouseleave', mxUtils.bind(this, function(evt) + { + // Workaround for IE11 firing mouseleave for touch in diagram + if (evt.relatedTarget != null && mxEvent.getSource(evt) == this.graph.container) + { + this.setDisplay('none'); + } + })); + + // Resets current state when in-place editor starts + this.graph.addListener(mxEvent.START_EDITING, mxUtils.bind(this, function(evt) + { + this.reset(); + })); + + // Resets current state after update of selection state for touch events + var graphClick = this.graph.click; + this.graph.click = mxUtils.bind(this, function(me) + { + graphClick.apply(this.graph, arguments); + + if (this.currentState != null && !this.graph.isCellSelected(this.currentState.cell) && + mxEvent.isTouchEvent(me.getEvent()) && !this.graph.model.isVertex(me.getCell())) + { + this.reset(); + } + }); + + // Checks if connection handler was active in mouse move + // as workaround for possible double connection inserted + var connectionHandlerActive = false; + + // Implements a listener for hover and click handling + this.graph.addMouseListener( + { + mouseDown: mxUtils.bind(this, function(sender, me) + { + connectionHandlerActive = false; + var evt = me.getEvent(); + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (!this.isActive()) + { + var state = this.getState(me.getState()); + + if (state != null || !mxEvent.isTouchEvent(evt)) + { + this.update(state); + } + } + + this.setDisplay('none'); + }), + mouseMove: mxUtils.bind(this, function(sender, me) + { + var evt = me.getEvent(); + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (!this.graph.isMouseDown && !mxEvent.isTouchEvent(evt)) + { + this.update(this.getState(me.getState()), me.getGraphX(), me.getGraphY()); + } + + if (this.graph.connectionHandler != null && this.graph.connectionHandler.shape != null) + { + connectionHandlerActive = true; + } + }), + mouseUp: mxUtils.bind(this, function(sender, me) + { + var evt = me.getEvent(); + + if (this.isResetEvent(evt)) + { + this.reset(); + } + else if (this.isActive() && this.mouseDownPoint != null && + Math.abs(me.getGraphX() - this.mouseDownPoint.x) < this.graph.tolerance && + Math.abs(me.getGraphY() - this.mouseDownPoint.y) < this.graph.tolerance) + { + // Executes click event on highlighted arrow + if (!connectionHandlerActive) + { + this.click(this.currentState, this.getDirection(), me); + } + } + else if (this.isActive()) + { + // Selects target vertex after drag and clone if not only new edge was inserted + if (this.graph.getSelectionCount() != 1 || !this.graph.model.isEdge(this.graph.getSelectionCell())) + { + this.update(this.getState(this.graph.view.getState(this.graph.getCellAt(me.getGraphX(), me.getGraphY())))); + } + else + { + this.reset(); + } + } + else if (mxEvent.isTouchEvent(evt) || (this.bbox != null && mxUtils.contains(this.bbox, me.getGraphX(), me.getGraphY()))) + { + // Shows existing hover icons if inside bounding box + this.setDisplay(''); + this.repaint(); + } + else if (!mxEvent.isTouchEvent(evt)) + { + this.reset(); + } + + connectionHandlerActive = false; + this.resetActiveArrow(); + }) + }); +}; + +/** + * + */ +HoverIcons.prototype.isResetEvent = function(evt, allowShift) +{ + return mxEvent.isAltDown(evt) || (this.activeArrow == null && mxEvent.isShiftDown(evt)) || + mxEvent.isMetaDown(evt) || (mxEvent.isPopupTrigger(evt) && !mxEvent.isControlDown(evt)); +}; + +/** + * + */ +HoverIcons.prototype.createArrow = function(img, tooltip) +{ + var arrow = null; + + if (mxClient.IS_IE && !mxClient.IS_SVG) + { + // Workaround for PNG images in IE6 + if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') + { + arrow = document.createElement(mxClient.VML_PREFIX + ':image'); + arrow.setAttribute('src', img.src); + arrow.style.borderStyle = 'none'; + } + else + { + arrow = document.createElement('div'); + arrow.style.backgroundImage = 'url(' + img.src + ')'; + arrow.style.backgroundPosition = 'center'; + arrow.style.backgroundRepeat = 'no-repeat'; + } + + arrow.style.width = (img.width + 4) + 'px'; + arrow.style.height = (img.height + 4) + 'px'; + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + } + else + { + arrow = mxUtils.createImage(img.src); + arrow.style.width = img.width + 'px'; + arrow.style.height = img.height + 'px'; + arrow.style.padding = this.tolerance + 'px'; + } + + if (tooltip != null) + { + arrow.setAttribute('title', tooltip); + } + + arrow.style.position = 'absolute'; + arrow.style.cursor = this.cssCursor; + + mxEvent.addGestureListeners(arrow, mxUtils.bind(this, function(evt) + { + if (this.currentState != null && !this.isResetEvent(evt)) + { + this.mouseDownPoint = mxUtils.convertPoint(this.graph.container, + mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + this.drag(evt, this.mouseDownPoint.x, this.mouseDownPoint.y); + this.activeArrow = arrow; + this.setDisplay('none'); + mxEvent.consume(evt); + } + })); + + // Captures mouse events as events on graph + mxEvent.redirectMouseEvents(arrow, this.graph, this.currentState); + + mxEvent.addListener(arrow, 'mouseenter', mxUtils.bind(this, function(evt) + { + // Workaround for Firefox firing mouseenter on touchend + if (mxEvent.isMouseEvent(evt)) + { + if (this.activeArrow != null && this.activeArrow != arrow) + { + mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); + } + + this.graph.connectionHandler.constraintHandler.reset(); + mxUtils.setOpacity(arrow, 100); + this.activeArrow = arrow; + } + })); + + mxEvent.addListener(arrow, 'mouseleave', mxUtils.bind(this, function(evt) + { + // Workaround for IE11 firing this event on touch + if (!this.graph.isMouseDown) + { + this.resetActiveArrow(); + } + })); + + return arrow; +}; + +/** + * + */ +HoverIcons.prototype.resetActiveArrow = function() +{ + if (this.activeArrow != null) + { + mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); + this.activeArrow = null; + } +}; + +/** + * + */ +HoverIcons.prototype.getDirection = function() +{ + var dir = mxConstants.DIRECTION_EAST; + + if (this.activeArrow == this.arrowUp) + { + dir = mxConstants.DIRECTION_NORTH; + } + else if (this.activeArrow == this.arrowDown) + { + dir = mxConstants.DIRECTION_SOUTH; + } + else if (this.activeArrow == this.arrowLeft) + { + dir = mxConstants.DIRECTION_WEST; + } + + return dir; +}; + +/** + * + */ +HoverIcons.prototype.visitNodes = function(visitor) +{ + for (var i = 0; i < this.elts.length; i++) + { + if (this.elts[i] != null) + { + visitor(this.elts[i]); + } + } +}; + +/** + * + */ +HoverIcons.prototype.removeNodes = function() +{ + this.visitNodes(function(elt) + { + if (elt.parentNode != null) + { + elt.parentNode.removeChild(elt); + } + }); +}; + +/** + * + */ +HoverIcons.prototype.setDisplay = function(display) +{ + this.visitNodes(function(elt) + { + elt.style.display = display; + }); +}; + +/** + * + */ +HoverIcons.prototype.isActive = function() +{ + return this.activeArrow != null && this.currentState != null; +}; + +/** + * + */ +HoverIcons.prototype.drag = function(evt, x, y) +{ + this.graph.popupMenuHandler.hideMenu(); + this.graph.stopEditing(false); + + // Checks if state was removed in call to stopEditing above + if (this.currentState != null) + { + this.graph.connectionHandler.start(this.currentState, x, y); + this.graph.isMouseTrigger = mxEvent.isMouseEvent(evt); + this.graph.isMouseDown = true; + + // Hides handles for selection cell + var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); + + if (handler != null) + { + handler.setHandlesVisible(false); + } + + // Uses elbow edges with vertical or horizontal direction +// var direction = this.getDirection(); +// var es = this.graph.connectionHandler.edgeState; +// es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction); +// es.style['sourcePortConstraint'] = direction; +// var elbowValue = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH) ? 'vertical' : 'horizontal'; +// +// var es = this.graph.connectionHandler.edgeState; +// es.style['edgeStyle'] = 'elbowEdgeStyle'; +// es.style['elbow'] = elbowValue; +// es.cell.style = mxUtils.setStyle(es.cell.style, 'edgeStyle', es.style['edgeStyle']); +// es.cell.style = mxUtils.setStyle(es.cell.style, 'elbow', es.style['elbow']); + } +}; + +/** + * + */ +HoverIcons.prototype.getStateAt = function(state, x, y) +{ + return this.graph.view.getState(this.graph.getCellAt(x, y)); +}; + +/** + * + */ +HoverIcons.prototype.click = function(state, dir, me) +{ + var evt = me.getEvent(); + var x = me.getGraphX(); + var y = me.getGraphY(); + + var tmp = this.getStateAt(state, x, y); + + if (tmp != null && this.graph.model.isEdge(tmp.cell) && !mxEvent.isControlDown(evt) && + (tmp.getVisibleTerminalState(true) == state || tmp.getVisibleTerminalState(false) == state)) + { + this.graph.setSelectionCell(tmp.cell); + this.reset(); + } + else if (state != null) + { + var cells = this.graph.connectVertex(state.cell, dir, this.graph.defaultEdgeLength, evt); + this.graph.selectCellsForConnectVertex(cells, evt, this); + + // Selects only target vertex if one exists + if (cells.length == 2 && this.graph.model.isVertex(cells[1])) + { + this.graph.setSelectionCell(cells[1]); + + // Adds hover icons to new target vertex for touch devices + if (mxEvent.isTouchEvent(evt)) + { + this.update(this.getState(this.graph.view.getState(cells[1]))); + } + else + { + // Hides hover icons after click with mouse + this.reset(); + } + + this.graph.scrollCellToVisible(cells[1]); + } + else + { + this.graph.setSelectionCells(cells); + } + } + + me.consume(); +}; + +/** + * + */ +HoverIcons.prototype.reset = function(clearTimeout) +{ + clearTimeout = (clearTimeout == null) ? true : clearTimeout; + + if (clearTimeout && this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + this.mouseDownPoint = null; + this.currentState = null; + this.activeArrow = null; + this.removeNodes(); + this.bbox = null; +}; + +/** + * + */ +HoverIcons.prototype.repaint = function() +{ + this.bbox = null; + + if (this.currentState != null) + { + // Checks if cell was deleted + this.currentState = this.getState(this.currentState); + + // Cell was deleted + if (this.currentState != null && + this.graph.model.isVertex(this.currentState.cell) && + this.graph.isCellConnectable(this.currentState.cell)) + { + var bds = mxRectangle.fromRectangle(this.currentState); + + // Uses outer bounding box to take rotation into account + if (this.currentState.shape != null && this.currentState.shape.boundingBox != null) + { + bds = mxRectangle.fromRectangle(this.currentState.shape.boundingBox); + } + + bds.grow(this.graph.tolerance); + bds.grow(this.arrowSpacing); + + var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); + + if (handler != null) + { + bds.x -= handler.horizontalOffset / 2; + bds.y -= handler.verticalOffset / 2; + bds.width += handler.horizontalOffset; + bds.height += handler.verticalOffset; + + // Adds bounding box of rotation handle to avoid overlap + if (handler.rotationShape != null && handler.rotationShape.node != null && + handler.rotationShape.node.style.visibility != 'hidden' && + handler.rotationShape.node.style.display != 'none' && + handler.rotationShape.boundingBox != null) + { + bds.add(handler.rotationShape.boundingBox); + } + } + + this.arrowUp.style.left = Math.round(this.currentState.getCenterX() - this.triangleUp.width / 2 - this.tolerance) + 'px'; + this.arrowUp.style.top = Math.round(bds.y - this.triangleUp.height - this.tolerance) + 'px'; + mxUtils.setOpacity(this.arrowUp, this.inactiveOpacity); + + this.arrowRight.style.left = Math.round(bds.x + bds.width - this.tolerance) + 'px'; + this.arrowRight.style.top = Math.round(this.currentState.getCenterY() - this.triangleRight.height / 2 - this.tolerance) + 'px'; + mxUtils.setOpacity(this.arrowRight, this.inactiveOpacity); + + this.arrowDown.style.left = this.arrowUp.style.left; + this.arrowDown.style.top = Math.round(bds.y + bds.height - this.tolerance) + 'px'; + mxUtils.setOpacity(this.arrowDown, this.inactiveOpacity); + + this.arrowLeft.style.left = Math.round(bds.x - this.triangleLeft.width - this.tolerance) + 'px'; + this.arrowLeft.style.top = this.arrowRight.style.top; + mxUtils.setOpacity(this.arrowLeft, this.inactiveOpacity); + + if (this.checkCollisions) + { + var right = this.graph.getCellAt(bds.x + bds.width + + this.triangleRight.width / 2, this.currentState.getCenterY()); + var left = this.graph.getCellAt(bds.x - this.triangleLeft.width / 2, this.currentState.getCenterY()); + var top = this.graph.getCellAt(this.currentState.getCenterX(), bds.y - this.triangleUp.height / 2); + var bottom = this.graph.getCellAt(this.currentState.getCenterX(), bds.y + bds.height + this.triangleDown.height / 2); + + // Shows hover icons large cell is behind all directions of current cell + if (right != null && right == left && left == top && top == bottom) + { + right = null; + left = null; + top = null; + bottom = null; + } + + var currentGeo = this.graph.getCellGeometry(this.currentState.cell); + + var checkCollision = mxUtils.bind(this, function(cell, arrow) + { + var geo = this.graph.model.isVertex(cell) && this.graph.getCellGeometry(cell); + + // Ignores collision if vertex is more than 3 times the size of this vertex + if (cell != null && !this.graph.model.isAncestor(cell, this.currentState.cell) && + (geo == null || currentGeo == null || (geo.height < 6 * currentGeo.height && + geo.width < 6 * currentGeo.width))) + { + arrow.style.visibility = 'hidden'; + } + else + { + arrow.style.visibility = 'visible'; + } + }); + + checkCollision(right, this.arrowRight); + checkCollision(left, this.arrowLeft); + checkCollision(top, this.arrowUp); + checkCollision(bottom, this.arrowDown); + } + else + { + this.arrowLeft.style.visibility = 'visible'; + this.arrowRight.style.visibility = 'visible'; + this.arrowUp.style.visibility = 'visible'; + this.arrowDown.style.visibility = 'visible'; + } + + if (this.graph.tooltipHandler.isEnabled()) + { + this.arrowLeft.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowRight.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowUp.setAttribute('title', mxResources.get('plusTooltip')); + this.arrowDown.setAttribute('title', mxResources.get('plusTooltip')); + } + else + { + this.arrowLeft.removeAttribute('title'); + this.arrowRight.removeAttribute('title'); + this.arrowUp.removeAttribute('title'); + this.arrowDown.removeAttribute('title'); + } + } + else + { + this.reset(); + } + + // Updates bounding box + if (this.currentState != null) + { + this.bbox = this.computeBoundingBox(); + + // Adds tolerance for hover + if (this.bbox != null) + { + this.bbox.grow(10); + } + } + } +}; + +/** + * + */ +HoverIcons.prototype.computeBoundingBox = function() +{ + var bbox = (!this.graph.model.isEdge(this.currentState.cell)) ? mxRectangle.fromRectangle(this.currentState) : null; + + this.visitNodes(function(elt) + { + if (elt.parentNode != null) + { + var tmp = new mxRectangle(elt.offsetLeft, elt.offsetTop, elt.offsetWidth, elt.offsetHeight); + + if (bbox == null) + { + bbox = tmp; + } + else + { + bbox.add(tmp); + } + } + }); + + return bbox; +}; + +/** + * + */ +HoverIcons.prototype.getState = function(state) +{ + if (state != null) + { + var cell = state.cell; + + // Uses connectable parent vertex if child is not connectable + if (this.graph.getModel().isVertex(cell) && !this.graph.isCellConnectable(cell)) + { + var parent = this.graph.getModel().getParent(cell); + + if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent)) + { + cell = parent; + } + } + + // Ignores locked cells and edges + if (this.graph.isCellLocked(cell) || this.graph.model.isEdge(cell)) + { + cell = null; + } + + state = this.graph.view.getState(cell); + } + + return state; +}; + +/** + * + */ +HoverIcons.prototype.update = function(state, x, y) +{ + if (!this.graph.connectionArrowsEnabled) + { + this.reset(); + } + else + { + var timeOnTarget = null; + + // Time on target + if (this.prev != state || this.isActive()) + { + this.startTime = new Date().getTime(); + this.prev = state; + timeOnTarget = 0; + + if (this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + if (state != null) + { + // Starts timer to update current state with no mouse events + this.updateThread = window.setTimeout(mxUtils.bind(this, function() + { + if (!this.isActive() && !this.graph.isMouseDown && + !this.graph.panningHandler.isActive()) + { + this.prev = state; + this.update(state, x, y); + } + }), this.updateDelay + 10); + } + } + else if (this.startTime != null) + { + timeOnTarget = new Date().getTime() - this.startTime; + } + + this.setDisplay(''); + + if (this.currentState != null && this.currentState != state && timeOnTarget < this.activationDelay && + this.bbox != null && !mxUtils.contains(this.bbox, x, y)) + { + this.reset(false); + } + else if (this.currentState != null || timeOnTarget > this.activationDelay) + { + if (this.currentState != state && ((timeOnTarget > this.updateDelay && state != null) || + this.bbox == null || x == null || y == null || !mxUtils.contains(this.bbox, x, y))) + { + if (state != null && this.graph.isEnabled()) + { + this.removeNodes(); + this.setCurrentState(state); + this.repaint(); + + // Resets connection points on other focused cells + if (this.graph.connectionHandler.constraintHandler.currentFocus != state) + { + this.graph.connectionHandler.constraintHandler.reset(); + } + } + else + { + this.reset(); + } + } + } + } +}; + +/** + * + */ +HoverIcons.prototype.setCurrentState = function(state) +{ + if (state.style['portConstraint'] != 'eastwest') + { + this.graph.container.appendChild(this.arrowUp); + this.graph.container.appendChild(this.arrowDown); + } + + this.graph.container.appendChild(this.arrowRight); + this.graph.container.appendChild(this.arrowLeft); + this.currentState = state; +}; + +(function() +{ + + /** + * Reset the list of processed edges. + */ + var mxGraphViewResetValidationState = mxGraphView.prototype.resetValidationState; + + mxGraphView.prototype.resetValidationState = function() + { + mxGraphViewResetValidationState.apply(this, arguments); + + this.validEdges = []; + }; + + /** + * Updates jumps for valid edges and repaints if needed. + */ + var mxGraphViewValidateCellState = mxGraphView.prototype.validateCellState; + + mxGraphView.prototype.validateCellState = function(cell, recurse) + { + var state = this.getState(cell); + + // Forces repaint if jumps change on a valid edge + if (state != null && this.graph.model.isEdge(state.cell) && + state.style != null && state.style[mxConstants.STYLE_CURVED] != 1 && + !state.invalid && this.updateLineJumps(state)) + { + this.graph.cellRenderer.redraw(state, false, this.isRendering()); + } + + state = mxGraphViewValidateCellState.apply(this, arguments); + + // Adds to the list of edges that may intersect with later edges + if (state != null && this.graph.model.isEdge(state.cell) && + state.style[mxConstants.STYLE_CURVED] != 1) + { + // LATER: Reuse jumps for valid edges + this.validEdges.push(state); + } + + return state; + }; + + /** + * Forces repaint if routed points have changed. + */ + var mxCellRendererIsShapeInvalid = mxCellRenderer.prototype.isShapeInvalid; + + mxCellRenderer.prototype.isShapeInvalid = function(state, shape) + { + return mxCellRendererIsShapeInvalid.apply(this, arguments) || + (state.routedPoints != null && shape.routedPoints != null && + !mxUtils.equalPoints(shape.routedPoints, state.routedPoints)) + }; + + + /** + * Updates jumps for invalid edges. + */ + var mxGraphViewUpdateCellState = mxGraphView.prototype.updateCellState; + + mxGraphView.prototype.updateCellState = function(state) + { + mxGraphViewUpdateCellState.apply(this, arguments); + + // Updates jumps on invalid edge before repaint + if (this.graph.model.isEdge(state.cell) && + state.style[mxConstants.STYLE_CURVED] != 1) + { + this.updateLineJumps(state); + } + }; + + /** + * Updates the jumps between given state and processed edges. + */ + mxGraphView.prototype.updateLineJumps = function(state) + { + var pts = state.absolutePoints; + + if (Graph.lineJumpsEnabled) + { + var changed = state.routedPoints != null; + var actual = null; + + if (pts != null && this.validEdges != null && + mxUtils.getValue(state.style, 'jumpStyle', 'none') !== 'none') + { + var thresh = 0.5 * this.scale; + changed = false; + actual = []; + + // Type 0 means normal waypoint, 1 means jump + function addPoint(type, x, y) + { + var rpt = new mxPoint(x, y); + rpt.type = type; + + actual.push(rpt); + var curr = (state.routedPoints != null) ? state.routedPoints[actual.length - 1] : null; + + return curr == null || curr.type != type || curr.x != x || curr.y != y; + }; + + for (var i = 0; i < pts.length - 1; i++) + { + var p1 = pts[i + 1]; + var p0 = pts[i]; + var list = []; + + // Ignores waypoints on straight segments + var pn = pts[i + 2]; + + while (i < pts.length - 2 && + mxUtils.ptSegDistSq(p0.x, p0.y, pn.x, pn.y, + p1.x, p1.y) < 1 * this.scale * this.scale) + { + p1 = pn; + i++; + pn = pts[i + 2]; + } + + changed = addPoint(0, p0.x, p0.y) || changed; + + // Processes all previous edges + for (var e = 0; e < this.validEdges.length; e++) + { + var state2 = this.validEdges[e]; + var pts2 = state2.absolutePoints; + + if (pts2 != null && mxUtils.intersects(state, state2) && state2.style['noJump'] != '1') + { + // Compares each segment of the edge with the current segment + for (var j = 0; j < pts2.length - 1; j++) + { + var p3 = pts2[j + 1]; + var p2 = pts2[j]; + + // Ignores waypoints on straight segments + pn = pts2[j + 2]; + + while (j < pts2.length - 2 && + mxUtils.ptSegDistSq(p2.x, p2.y, pn.x, pn.y, + p3.x, p3.y) < 1 * this.scale * this.scale) + { + p3 = pn; + j++; + pn = pts2[j + 2]; + } + + var pt = mxUtils.intersection(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); + + // Handles intersection between two segments + if (pt != null && (Math.abs(pt.x - p2.x) > thresh || + Math.abs(pt.y - p2.y) > thresh) && + (Math.abs(pt.x - p3.x) > thresh || + Math.abs(pt.y - p3.y) > thresh)) + { + var dx = pt.x - p0.x; + var dy = pt.y - p0.y; + var temp = {distSq: dx * dx + dy * dy, x: pt.x, y: pt.y}; + + // Intersections must be ordered by distance from start of segment + for (var t = 0; t < list.length; t++) + { + if (list[t].distSq > temp.distSq) + { + list.splice(t, 0, temp); + temp = null; + + break; + } + } + + // Ignores multiple intersections at segment joint + if (temp != null && (list.length == 0 || + list[list.length - 1].x !== temp.x || + list[list.length - 1].y !== temp.y)) + { + list.push(temp); + } + } + } + } + } + + // Adds ordered intersections to routed points + for (var j = 0; j < list.length; j++) + { + changed = addPoint(1, list[j].x, list[j].y) || changed; + } + } + + var pt = pts[pts.length - 1]; + changed = addPoint(0, pt.x, pt.y) || changed; + } + + state.routedPoints = actual; + + return changed; + } + else + { + return false; + } + }; + + /** + * Overrides painting the actual shape for taking into account jump style. + */ + var mxConnectorPaintLine = mxConnector.prototype.paintLine; + + mxConnector.prototype.paintLine = function (c, absPts, rounded) + { + // Required for checking dirty state + this.routedPoints = (this.state != null) ? this.state.routedPoints : null; + + if (this.outline || this.state == null || this.style == null || + this.state.routedPoints == null || this.state.routedPoints.length == 0) + { + mxConnectorPaintLine.apply(this, arguments); + } + else + { + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.LINE_ARCSIZE) / 2; + var size = (parseInt(mxUtils.getValue(this.style, 'jumpSize', + Graph.defaultJumpSize)) - 2) / 2 + this.strokewidth; + var style = mxUtils.getValue(this.style, 'jumpStyle', 'none'); + var f = Editor.jumpSizeRatio; + var moveTo = true; + var last = null; + var len = null; + var pts = []; + var n = null; + c.begin(); + + for (var i = 0; i < this.state.routedPoints.length; i++) + { + var rpt = this.state.routedPoints[i]; + var pt = new mxPoint(rpt.x / this.scale, rpt.y / this.scale); + + // Takes first and last point from passed-in array + if (i == 0) + { + pt = absPts[0]; + } + else if (i == this.state.routedPoints.length - 1) + { + pt = absPts[absPts.length - 1]; + } + + var done = false; + + // Type 1 is an intersection + if (last != null && rpt.type == 1) + { + // Checks if next/previous points are too close + var next = this.state.routedPoints[i + 1]; + var dx = next.x / this.scale - pt.x; + var dy = next.y / this.scale - pt.y; + var dist = dx * dx + dy * dy; + + if (n == null) + { + n = new mxPoint(pt.x - last.x, pt.y - last.y); + len = Math.sqrt(n.x * n.x + n.y * n.y); + n.x = n.x * size / len; + n.y = n.y * size / len; + } + + if (dist > size * size && len > 0) + { + var dx = last.x - pt.x; + var dy = last.y - pt.y; + var dist = dx * dx + dy * dy; + + if (dist > size * size) + { + var p0 = new mxPoint(pt.x - n.x, pt.y - n.y); + var p1 = new mxPoint(pt.x + n.x, pt.y + n.y); + pts.push(p0); + + this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); + + var f = (Math.round(n.x) < 0 || (Math.round(n.x) == 0 + && Math.round(n.y) <= 0)) ? 1 : -1; + moveTo = false; + + if (style == 'sharp') + { + c.lineTo(p0.x - n.y * f, p0.y + n.x * f); + c.lineTo(p1.x - n.y * f, p1.y + n.x * f); + c.lineTo(p1.x, p1.y); + } + else if (style == 'arc') + { + f *= 1.3; + c.curveTo(p0.x - n.y * f, p0.y + n.x * f, + p1.x - n.y * f, p1.y + n.x * f, + p1.x, p1.y); + } + else + { + c.moveTo(p1.x, p1.y); + moveTo = true; + } + + pts = [p1]; + done = true; + } + } + } + else + { + n = null; + } + + if (!done) + { + pts.push(pt); + last = pt; + } + } + + this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); + c.stroke(); + } + }; + + /** + * Adds support for snapToPoint style. + */ + var mxGraphViewUpdateFloatingTerminalPoint = mxGraphView.prototype.updateFloatingTerminalPoint; + + mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source) + { + if (start != null && edge != null && + (start.style['snapToPoint'] == '1' || + edge.style['snapToPoint'] == '1')) + { + start = this.getTerminalPort(edge, start, source); + var next = this.getNextPoint(edge, end, source); + + var orth = this.graph.isOrthogonal(edge); + var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0')); + var center = new mxPoint(start.getCenterX(), start.getCenterY()); + + if (alpha != 0) + { + var cos = Math.cos(-alpha); + var sin = Math.sin(-alpha); + next = mxUtils.getRotatedPoint(next, cos, sin, center); + } + + var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0); + border += parseFloat(edge.style[(source) ? + mxConstants.STYLE_SOURCE_PERIMETER_SPACING : + mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0); + var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border); + + if (alpha != 0) + { + var cos = Math.cos(alpha); + var sin = Math.sin(alpha); + pt = mxUtils.getRotatedPoint(pt, cos, sin, center); + } + + edge.setAbsoluteTerminalPoint(this.snapToAnchorPoint(edge, start, end, source, pt), source); + } + else + { + mxGraphViewUpdateFloatingTerminalPoint.apply(this, arguments); + } + }; + + mxGraphView.prototype.snapToAnchorPoint = function(edge, start, end, source, pt) + { + if (start != null && edge != null) + { + var constraints = this.graph.getAllConnectionConstraints(start) + var nearest = null; + var dist = null; + + if (constraints != null) + { + for (var i = 0; i < constraints.length; i++) + { + var cp = this.graph.getConnectionPoint(start, constraints[i]); + + if (cp != null) + { + var tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y); + + if (dist == null || tmp < dist) + { + nearest = cp; + dist = tmp; + } + } + } + } + + if (nearest != null) + { + pt = nearest; + } + } + + return pt; + }; + + /** + * Adds support for placeholders in text elements of shapes. + */ + var mxStencilEvaluateTextAttribute = mxStencil.prototype.evaluateTextAttribute; + + mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape) + { + var result = mxStencilEvaluateTextAttribute.apply(this, arguments); + var placeholders = node.getAttribute('placeholders'); + + if (placeholders == '1' && shape.state != null) + { + result = shape.state.view.graph.replacePlaceholders(shape.state.cell, result); + } + + return result; + }; + + /** + * Adds custom stencils defined via shape=stencil(value) style. The value is a base64 encoded, compressed and + * URL encoded XML definition of the shape according to the stencil definition language of mxGraph. + * + * Needs to be in this file to make sure its part of the embed client code. Also the check for ZLib is + * different than for the Editor code. + */ + var mxCellRendererCreateShape = mxCellRenderer.prototype.createShape; + mxCellRenderer.prototype.createShape = function(state) + { + if (state.style != null && typeof(pako) !== 'undefined') + { + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + // Extracts and decodes stencil XML if shape has the form shape=stencil(value) + if (shape != null && shape.substring(0, 8) == 'stencil(') + { + try + { + var stencil = shape.substring(8, shape.length - 1); + var doc = mxUtils.parseXml(state.view.graph.decompress(stencil)); + + return new mxShape(new mxStencil(doc.documentElement)); + } + catch (e) + { + if (window.console != null) + { + console.log('Error in shape: ' + e); + } + } + } + } + + return mxCellRendererCreateShape.apply(this, arguments); + }; +})(); + +/** + * Overrides stencil registry for dynamic loading of stencils. + */ +/** + * Maps from library names to an array of Javascript filenames, + * which are synchronously loaded. Currently only stencil files + * (.xml) and JS files (.js) are supported. + * IMPORTANT: For embedded diagrams to work entries must also + * be added in EmbedServlet.java. + */ +mxStencilRegistry.libraries = {}; + +/** + * Global switch to disable dynamic loading. + */ +mxStencilRegistry.dynamicLoading = true; + +/** + * Global switch to disable eval for JS (preload all JS instead). + */ +mxStencilRegistry.allowEval = true; + +/** + * Stores all package names that have been dynamically loaded. + * Each package is only loaded once. + */ +mxStencilRegistry.packages = []; + +// Extends the default stencil registry to add dynamic loading +mxStencilRegistry.getStencil = function(name) +{ + var result = mxStencilRegistry.stencils[name]; + + if (result == null && mxCellRenderer.defaultShapes[name] == null && mxStencilRegistry.dynamicLoading) + { + var basename = mxStencilRegistry.getBasenameForStencil(name); + + // Loads stencil files and tries again + if (basename != null) + { + var libs = mxStencilRegistry.libraries[basename]; + + if (libs != null) + { + if (mxStencilRegistry.packages[basename] == null) + { + for (var i = 0; i < libs.length; i++) + { + var fname = libs[i]; + + if (fname.toLowerCase().substring(fname.length - 4, fname.length) == '.xml') + { + mxStencilRegistry.loadStencilSet(fname, null); + } + else if (fname.toLowerCase().substring(fname.length - 3, fname.length) == '.js') + { + try + { + if (mxStencilRegistry.allowEval) + { + var req = mxUtils.load(fname); + + if (req != null && req.getStatus() >= 200 && req.getStatus() <= 299) + { + eval.call(window, req.getText()); + } + } + } + catch (e) + { + if (window.console != null) + { + console.log('error in getStencil:', fname, e); + } + } + } + else + { + // FIXME: This does not yet work as the loading is triggered after + // the shape was used in the graph, at which point the keys have + // typically been translated in the calling method. + //mxResources.add(fname); + } + } + + mxStencilRegistry.packages[basename] = 1; + } + } + else + { + // Replaces '_-_' with '_' + basename = basename.replace('_-_', '_'); + mxStencilRegistry.loadStencilSet(STENCIL_PATH + '/' + basename + '.xml', null); + } + + result = mxStencilRegistry.stencils[name]; + } + } + + return result; +}; + +// Returns the basename for the given stencil or null if no file must be +// loaded to render the given stencil. +mxStencilRegistry.getBasenameForStencil = function(name) +{ + var tmp = null; + + if (name != null) + { + var parts = name.split('.'); + + if (parts.length > 0 && parts[0] == 'mxgraph') + { + tmp = parts[1]; + + for (var i = 2; i < parts.length - 1; i++) + { + tmp += '/' + parts[i]; + } + } + } + + return tmp; +}; + +// Loads the given stencil set +mxStencilRegistry.loadStencilSet = function(stencilFile, postStencilLoad, force, async) +{ + force = (force != null) ? force : false; + + // Uses additional cache for detecting previous load attempts + var xmlDoc = mxStencilRegistry.packages[stencilFile]; + + if (force || xmlDoc == null) + { + var install = false; + + if (xmlDoc == null) + { + try + { + if (async) + { + mxStencilRegistry.loadStencil(stencilFile, mxUtils.bind(this, function(xmlDoc2) + { + if (xmlDoc2 != null && xmlDoc2.documentElement != null) + { + mxStencilRegistry.packages[stencilFile] = xmlDoc2; + install = true; + mxStencilRegistry.parseStencilSet(xmlDoc2.documentElement, postStencilLoad, install); + } + })); + + return; + } + else + { + xmlDoc = mxStencilRegistry.loadStencil(stencilFile); + mxStencilRegistry.packages[stencilFile] = xmlDoc; + install = true; + } + } + catch (e) + { + if (window.console != null) + { + console.log('error in loadStencilSet:', stencilFile, e); + } + } + } + + if (xmlDoc != null && xmlDoc.documentElement != null) + { + mxStencilRegistry.parseStencilSet(xmlDoc.documentElement, postStencilLoad, install); + } + } +}; + +// Loads the given stencil XML file. +mxStencilRegistry.loadStencil = function(filename, fn) +{ + if (fn != null) + { + var req = mxUtils.get(filename, mxUtils.bind(this, function(req) + { + fn((req.getStatus() >= 200 && req.getStatus() <= 299) ? req.getXml() : null); + })); + } + else + { + return mxUtils.load(filename).getXml(); + } +}; + +// Takes array of strings +mxStencilRegistry.parseStencilSets = function(stencils) +{ + for (var i = 0; i < stencils.length; i++) + { + mxStencilRegistry.parseStencilSet(mxUtils.parseXml(stencils[i]).documentElement); + } +}; + +// Parses the given stencil set +mxStencilRegistry.parseStencilSet = function(root, postStencilLoad, install) +{ + if (root.nodeName == 'stencils') + { + var shapes = root.firstChild; + + while (shapes != null) + { + if (shapes.nodeName == 'shapes') + { + mxStencilRegistry.parseStencilSet(shapes, postStencilLoad, install); + } + + shapes = shapes.nextSibling; + } + } + else + { + install = (install != null) ? install : true; + var shape = root.firstChild; + var packageName = ''; + var name = root.getAttribute('name'); + + if (name != null) + { + packageName = name + '.'; + } + + while (shape != null) + { + if (shape.nodeType == mxConstants.NODETYPE_ELEMENT) + { + name = shape.getAttribute('name'); + + if (name != null) + { + packageName = packageName.toLowerCase(); + var stencilName = name.replace(/ /g,"_"); + + if (install) + { + mxStencilRegistry.addStencil(packageName + stencilName.toLowerCase(), new mxStencil(shape)); + } + + if (postStencilLoad != null) + { + var w = shape.getAttribute('w'); + var h = shape.getAttribute('h'); + + w = (w == null) ? 80 : parseInt(w, 10); + h = (h == null) ? 80 : parseInt(h, 10); + + postStencilLoad(packageName, stencilName, name, w, h); + } + } + } + + shape = shape.nextSibling; + } + } +}; + +/** + * These overrides are only added if mxVertexHandler is defined (ie. not in embedded graph) + */ +if (typeof mxVertexHandler != 'undefined') +{ + (function() + { + // Sets colors for handles + mxConstants.HANDLE_FILLCOLOR = '#99ccff'; + mxConstants.HANDLE_STROKECOLOR = '#0088cf'; + mxConstants.VERTEX_SELECTION_COLOR = '#00a8ff'; + mxConstants.OUTLINE_COLOR = '#00a8ff'; + mxConstants.OUTLINE_HANDLE_FILLCOLOR = '#99ccff'; + mxConstants.OUTLINE_HANDLE_STROKECOLOR = '#00a8ff'; + mxConstants.CONNECT_HANDLE_FILLCOLOR = '#cee7ff'; + mxConstants.EDGE_SELECTION_COLOR = '#00a8ff'; + mxConstants.DEFAULT_VALID_COLOR = '#00a8ff'; + mxConstants.LABEL_HANDLE_FILLCOLOR = '#cee7ff'; + mxConstants.GUIDE_COLOR = '#0088cf'; + mxConstants.HIGHLIGHT_OPACITY = 30; + mxConstants.HIGHLIGHT_SIZE = 8; + + // Enables snapping to off-grid terminals for edge waypoints + mxEdgeHandler.prototype.snapToTerminals = true; + + // Enables guides + mxGraphHandler.prototype.guidesEnabled = true; + + // Enables fading of rubberband + mxRubberband.prototype.fadeOut = true; + + // Alt-move disables guides + mxGuide.prototype.isEnabledForEvent = function(evt) + { + return !mxEvent.isAltDown(evt); + }; + + // Extends connection handler to enable ctrl+drag for cloning source cell + // since copyOnConnect is now disabled by default + var mxConnectionHandlerCreateTarget = mxConnectionHandler.prototype.isCreateTarget; + mxConnectionHandler.prototype.isCreateTarget = function(evt) + { + return mxEvent.isControlDown(evt) || mxConnectionHandlerCreateTarget.apply(this, arguments); + }; + + // Overrides highlight shape for connection points + mxConstraintHandler.prototype.createHighlightShape = function() + { + var hl = new mxEllipse(null, this.highlightColor, this.highlightColor, 0); + hl.opacity = mxConstants.HIGHLIGHT_OPACITY; + + return hl; + }; + + // Overrides edge preview to use current edge shape and default style + mxConnectionHandler.prototype.livePreview = true; + mxConnectionHandler.prototype.cursor = 'crosshair'; + + // Uses current edge style for connect preview + mxConnectionHandler.prototype.createEdgeState = function(me) + { + var style = this.graph.createCurrentEdgeStyle(); + var edge = this.graph.createEdge(null, null, null, null, null, style); + var state = new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge)); + + for (var key in this.graph.currentEdgeStyle) + { + state.style[key] = this.graph.currentEdgeStyle[key]; + } + + return state; + }; + + // Overrides dashed state with current edge style + var connectionHandlerCreateShape = mxConnectionHandler.prototype.createShape; + mxConnectionHandler.prototype.createShape = function() + { + var shape = connectionHandlerCreateShape.apply(this, arguments); + + shape.isDashed = this.graph.currentEdgeStyle[mxConstants.STYLE_DASHED] == '1'; + + return shape; + } + + // Overrides live preview to keep current style + mxConnectionHandler.prototype.updatePreview = function(valid) + { + // do not change color of preview + }; + + // Overrides connection handler to ignore edges instead of not allowing connections + var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; + mxConnectionHandler.prototype.createMarker = function() + { + var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); + + var markerGetCell = marker.getCell; + marker.getCell = mxUtils.bind(this, function(me) + { + var result = markerGetCell.apply(this, arguments); + + this.error = null; + + return result; + }); + + return marker; + }; + + /** + * Function: isCellLocked + * + * Returns true if the given cell does not allow new connections to be created. + * This implementation returns false. + */ + mxConnectionHandler.prototype.isCellEnabled = function(cell) + { + return !this.graph.isCellLocked(cell); + }; + + /** + * + */ + Graph.prototype.defaultVertexStyle = {}; + + /** + * Contains the default style for edges. + */ + Graph.prototype.defaultEdgeStyle = {'edgeStyle': 'orthogonalEdgeStyle', 'rounded': '0', + 'jettySize': 'auto', 'orthogonalLoop': '1'}; + + /** + * Returns the current edge style as a string. + */ + Graph.prototype.createCurrentEdgeStyle = function() + { + var style = 'edgeStyle=' + (this.currentEdgeStyle['edgeStyle'] || 'none') + ';'; + + if (this.currentEdgeStyle['shape'] != null) + { + style += 'shape=' + this.currentEdgeStyle['shape'] + ';'; + } + + if (this.currentEdgeStyle['curved'] != null) + { + style += 'curved=' + this.currentEdgeStyle['curved'] + ';'; + } + + if (this.currentEdgeStyle['rounded'] != null) + { + style += 'rounded=' + this.currentEdgeStyle['rounded'] + ';'; + } + + if (this.currentEdgeStyle['comic'] != null) + { + style += 'comic=' + this.currentEdgeStyle['comic'] + ';'; + } + + if (this.currentEdgeStyle['jumpStyle'] != null) + { + style += 'jumpStyle=' + this.currentEdgeStyle['jumpStyle'] + ';'; + } + + if (this.currentEdgeStyle['jumpSize'] != null) + { + style += 'jumpSize=' + this.currentEdgeStyle['jumpSize'] + ';'; + } + + // Special logic for custom property of elbowEdgeStyle + if (this.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle' && this.currentEdgeStyle['elbow'] != null) + { + style += 'elbow=' + this.currentEdgeStyle['elbow'] + ';'; + } + + if (this.currentEdgeStyle['html'] != null) + { + style += 'html=' + this.currentEdgeStyle['html'] + ';'; + } + else + { + style += 'html=1;'; + } + + return style; + }; + + /** + * Hook for subclassers. + */ + Graph.prototype.getPagePadding = function() + { + return new mxPoint(0, 0); + }; + + /** + * Loads the stylesheet for this graph. + */ + Graph.prototype.loadStylesheet = function() + { + var node = (this.themes != null) ? this.themes[this.defaultThemeName] : + (!mxStyleRegistry.dynamicLoading) ? null : + mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement(); + + if (node != null) + { + var dec = new mxCodec(node.ownerDocument); + dec.decode(node, this.getStylesheet()); + } + }; + + /** + * + */ + Graph.prototype.importGraphModel = function(node, dx, dy, crop) + { + dx = (dx != null) ? dx : 0; + dy = (dy != null) ? dy : 0; + + var cells = [] + var model = new mxGraphModel(); + var codec = new mxCodec(node.ownerDocument); + codec.decode(node, model); + + var childCount = model.getChildCount(model.getRoot()); + var targetChildCount = this.model.getChildCount(this.model.getRoot()); + + // Merges into active layer if one layer is pasted + this.model.beginUpdate(); + try + { + // Mapping for multiple calls to cloneCells with the same set of cells + var mapping = new Object(); + + for (var i = 0; i < childCount; i++) + { + var parent = model.getChildAt(model.getRoot(), i); + + // Adds cells to existing layer if not locked + if (childCount == 1 && !this.isCellLocked(this.getDefaultParent())) + { + var children = model.getChildren(parent); + cells = cells.concat(this.importCells(children, dx, dy, this.getDefaultParent(), null, mapping)); + } + else + { + // Delta is non cascading, needs separate move for layers + parent = this.importCells([parent], 0, 0, this.model.getRoot(), null, mapping)[0]; + var children = this.model.getChildren(parent); + this.moveCells(children, dx, dy); + cells = cells.concat(children); + } + } + + if (crop) + { + if (this.isGridEnabled()) + { + dx = this.snap(dx); + dy = this.snap(dy); + } + + var bounds = this.getBoundingBoxFromGeometry(cells, true); + + if (bounds != null) + { + this.moveCells(cells, dx - bounds.x, dy - bounds.y); + } + } + } + finally + { + this.model.endUpdate(); + } + + return cells; + } + + /** + * Overrides method to provide connection constraints for shapes. + */ + Graph.prototype.getAllConnectionConstraints = function(terminal, source) + { + if (terminal != null) + { + var constraints = mxUtils.getValue(terminal.style, 'points', null); + + if (constraints != null) + { + // Requires an array of arrays with x, y (0..1) and an optional + // perimeter (0 or 1), eg. points=[[0,0,1],[0,1,0],[1,1]] + var result = []; + + try + { + var c = JSON.parse(constraints); + + for (var i = 0; i < c.length; i++) + { + var tmp = c[i]; + result.push(new mxConnectionConstraint(new mxPoint(tmp[0], tmp[1]), (tmp.length > 2) ? tmp[2] != '0' : true)); + } + } + catch (e) + { + // ignore + } + + return result; + } + else + { + if (terminal.shape != null) + { + if (terminal.shape.stencil != null) + { + if (terminal.shape.stencil != null) + { + return terminal.shape.stencil.constraints; + } + } + else if (terminal.shape.constraints != null) + { + return terminal.shape.constraints; + } + } + } + } + + return null; + }; + + /** + * Inverts the elbow edge style without removing existing styles. + */ + Graph.prototype.flipEdge = function(edge) + { + if (edge != null) + { + var state = this.view.getState(edge); + var style = (state != null) ? state.style : this.getCellStyle(edge); + + if (style != null) + { + var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW, + mxConstants.ELBOW_HORIZONTAL); + var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ? + mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL; + this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]); + } + } + }; + + /** + * Disables drill-down for non-swimlanes. + */ + Graph.prototype.isValidRoot = function(cell) + { + // Counts non-relative children + var childCount = this.model.getChildCount(cell); + var realChildCount = 0; + + for (var i = 0; i < childCount; i++) + { + var child = this.model.getChildAt(cell, i); + + if (this.model.isVertex(child)) + { + var geometry = this.getCellGeometry(child); + + if (geometry != null && !geometry.relative) + { + realChildCount++; + } + } + } + + return realChildCount > 0 || this.isContainer(cell); + }; + + /** + * Disables drill-down for non-swimlanes. + */ + Graph.prototype.isValidDropTarget = function(cell) + { + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return mxUtils.getValue(style, 'part', '0') != '1' && (this.isContainer(cell) || + (mxGraph.prototype.isValidDropTarget.apply(this, arguments) && + mxUtils.getValue(style, 'dropTarget', '1') != '0')); + }; + + /** + * Overrides createGroupCell to set the group style for new groups to 'group'. + */ + Graph.prototype.createGroupCell = function() + { + var group = mxGraph.prototype.createGroupCell.apply(this, arguments); + group.setStyle('group'); + + return group; + }; + + /** + * Disables extending parents with stack layouts on add + */ + Graph.prototype.isExtendParentsOnAdd = function(cell) + { + var result = mxGraph.prototype.isExtendParentsOnAdd.apply(this, arguments); + + if (result && cell != null && this.layoutManager != null) + { + var parent = this.model.getParent(cell); + + if (parent != null) + { + var layout = this.layoutManager.getLayout(parent); + + if (layout != null && layout.constructor == mxStackLayout) + { + result = false; + } + } + } + + return result; + }; + + /** + * Overrides autosize to add a border. + */ + Graph.prototype.getPreferredSizeForCell = function(cell) + { + var result = mxGraph.prototype.getPreferredSizeForCell.apply(this, arguments); + + // Adds buffer + if (result != null) + { + result.width += 10; + result.height += 4; + + if (this.gridEnabled) + { + result.width = this.snap(result.width); + result.height = this.snap(result.height); + } + } + + return result; + } + + /** + * Turns the given cells and returns the changed cells. + */ + Graph.prototype.turnShapes = function(cells) + { + var model = this.getModel(); + var select = []; + + model.beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (model.isEdge(cell)) + { + var src = model.getTerminal(cell, true); + var trg = model.getTerminal(cell, false); + + model.setTerminal(cell, trg, true); + model.setTerminal(cell, src, false); + + var geo = model.getGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + + if (geo.points != null) + { + geo.points.reverse(); + } + + var sp = geo.getTerminalPoint(true); + var tp = geo.getTerminalPoint(false) + + geo.setTerminalPoint(sp, false); + geo.setTerminalPoint(tp, true); + model.setGeometry(cell, geo); + + // Inverts constraints + var edgeState = this.view.getState(cell); + var sourceState = this.view.getState(src); + var targetState = this.view.getState(trg); + + if (edgeState != null) + { + var sc = (sourceState != null) ? this.getConnectionConstraint(edgeState, sourceState, true) : null; + var tc = (targetState != null) ? this.getConnectionConstraint(edgeState, targetState, false) : null; + + this.setConnectionConstraint(cell, src, true, tc); + this.setConnectionConstraint(cell, trg, false, sc); + } + + select.push(cell); + } + } + else if (model.isVertex(cell)) + { + var geo = this.getCellGeometry(cell); + + if (geo != null) + { + // Rotates the size and position in the geometry + geo = geo.clone(); + geo.x += geo.width / 2 - geo.height / 2; + geo.y += geo.height / 2 - geo.width / 2; + var tmp = geo.width; + geo.width = geo.height; + geo.height = tmp; + model.setGeometry(cell, geo); + + // Reads the current direction and advances by 90 degrees + var state = this.view.getState(cell); + + if (state != null) + { + var dir = state.style[mxConstants.STYLE_DIRECTION] || 'east'/*default*/; + + if (dir == 'east') + { + dir = 'south'; + } + else if (dir == 'south') + { + dir = 'west'; + } + else if (dir == 'west') + { + dir = 'north'; + } + else if (dir == 'north') + { + dir = 'east'; + } + + this.setCellStyles(mxConstants.STYLE_DIRECTION, dir, [cell]); + } + + select.push(cell); + } + } + } + } + finally + { + model.endUpdate(); + } + + return select; + }; + + /** + * Updates the child cells with placeholders if metadata of a cell has changed. + */ + Graph.prototype.processChange = function(change) + { + mxGraph.prototype.processChange.apply(this, arguments); + + if (change instanceof mxValueChange && change.cell != null && + change.cell.value != null && typeof(change.cell.value) == 'object') + { + // Invalidates all descendants with placeholders + var desc = this.model.getDescendants(change.cell); + + // LATER: Check if only label or tooltip have changed + if (desc.length > 0) + { + for (var i = 0; i < desc.length; i++) + { + if (this.isReplacePlaceholders(desc[i])) + { + this.view.invalidate(desc[i], false, false); + } + } + } + } + }; + + /** + * Replaces the given element with a span. + */ + Graph.prototype.replaceElement = function(elt, tagName) + { + var span = elt.ownerDocument.createElement((tagName != null) ? tagName : 'span'); + var attributes = Array.prototype.slice.call(elt.attributes); + + while (attr = attributes.pop()) + { + span.setAttribute(attr.nodeName, attr.nodeValue); + } + + span.innerHTML = elt.innerHTML; + elt.parentNode.replaceChild(span, elt); + }; + + /** + * Handles label changes for XML user objects. + */ + Graph.prototype.updateLabelElements = function(cells, fn, tagName) + { + cells = (cells != null) ? cells : this.getSelectionCells(); + var div = document.createElement('div'); + + for (var i = 0; i < cells.length; i++) + { + // Changes font tags inside HTML labels + if (this.isHtmlLabel(cells[i])) + { + var label = this.convertValueToString(cells[i]); + + if (label != null && label.length > 0) + { + div.innerHTML = label; + var elts = div.getElementsByTagName((tagName != null) ? tagName : '*'); + + for (var j = 0; j < elts.length; j++) + { + fn(elts[j]); + } + + if (div.innerHTML != label) + { + this.cellLabelChanged(cells[i], div.innerHTML); + } + } + } + } + }; + + /** + * Handles label changes for XML user objects. + */ + Graph.prototype.cellLabelChanged = function(cell, value, autoSize) + { + // Removes all illegal control characters in user input + value = this.zapGremlins(value); + + this.model.beginUpdate(); + try + { + if (cell.value != null && typeof cell.value == 'object') + { + if (this.isReplacePlaceholders(cell) && + cell.getAttribute('placeholder') != null) + { + // LATER: Handle delete, name change + var name = cell.getAttribute('placeholder'); + var current = cell; + + while (current != null) + { + if (current == this.model.getRoot() || (current.value != null && + typeof(current.value) == 'object' && current.hasAttribute(name))) + { + this.setAttributeForCell(current, name, value); + + break; + } + + current = this.model.getParent(current); + } + } + + var tmp = cell.value.cloneNode(true); + tmp.setAttribute('label', value); + value = tmp; + } + + mxGraph.prototype.cellLabelChanged.apply(this, arguments); + } + finally + { + this.model.endUpdate(); + } + }; + + /** + * Removes transparent empty groups if all children are removed. + */ + Graph.prototype.cellsRemoved = function(cells) + { + if (cells != null) + { + var dict = new mxDictionary(); + + for (var i = 0; i < cells.length; i++) + { + dict.put(cells[i], true); + } + + // LATER: Recurse up the cell hierarchy + var parents = []; + + for (var i = 0; i < cells.length; i++) + { + var parent = this.model.getParent(cells[i]); + + if (parent != null && !dict.get(parent)) + { + dict.put(parent, true); + parents.push(parent); + } + } + + for (var i = 0; i < parents.length; i++) + { + var state = this.view.getState(parents[i]); + + if (state != null && (this.model.isEdge(state.cell) || this.model.isVertex(state.cell)) && this.isCellDeletable(state.cell)) + { + var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); + var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); + + if (stroke == mxConstants.NONE && fill == mxConstants.NONE) + { + var allChildren = true; + + for (var j = 0; j < this.model.getChildCount(state.cell) && allChildren; j++) + { + if (!dict.get(this.model.getChildAt(state.cell, j))) + { + allChildren = false; + } + } + + if (allChildren) + { + cells.push(state.cell); + } + } + } + } + } + + mxGraph.prototype.cellsRemoved.apply(this, arguments); + }; + + /** + * Overrides ungroup to check if group should be removed. + */ + Graph.prototype.removeCellsAfterUngroup = function(cells) + { + var cellsToRemove = []; + + for (var i = 0; i < cells.length; i++) + { + if (this.isCellDeletable(cells[i])) + { + var state = this.view.getState(cells[i]); + + if (state != null) + { + var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); + var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); + + if (stroke == mxConstants.NONE && fill == mxConstants.NONE) + { + cellsToRemove.push(cells[i]); + } + } + } + } + + cells = cellsToRemove; + + mxGraph.prototype.removeCellsAfterUngroup.apply(this, arguments); + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setLinkForCell = function(cell, link) + { + this.setAttributeForCell(cell, 'link', link); + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setTooltipForCell = function(cell, link) + { + this.setAttributeForCell(cell, 'tooltip', link); + }; + + /** + * Sets the link for the given cell. + */ + Graph.prototype.setAttributeForCell = function(cell, attributeName, attributeValue) + { + var value = null; + + if (cell.value != null && typeof(cell.value) == 'object') + { + value = cell.value.cloneNode(true); + } + else + { + var doc = mxUtils.createXmlDocument(); + + value = doc.createElement('UserObject'); + value.setAttribute('label', cell.value || ''); + } + + if (attributeValue != null && attributeValue.length > 0) + { + value.setAttribute(attributeName, attributeValue); + } + else + { + value.removeAttribute(attributeName); + } + + this.model.setValue(cell, value); + }; + + /** + * Overridden to stop moving edge labels between cells. + */ + Graph.prototype.getDropTarget = function(cells, evt, cell, clone) + { + var model = this.getModel(); + + // Disables drop into group if alt is pressed + if (mxEvent.isAltDown(evt)) + { + return null; + } + + // Disables dragging edge labels out of edges + for (var i = 0; i < cells.length; i++) + { + if (this.model.isEdge(this.model.getParent(cells[i]))) + { + return null; + } + } + + return mxGraph.prototype.getDropTarget.apply(this, arguments); + }; + + /** + * Overrides double click handling to avoid accidental inserts of new labels in dblClick below. + */ + Graph.prototype.click = function(me) + { + mxGraph.prototype.click.call(this, me); + + // Stores state and source for checking in dblClick + this.firstClickState = me.getState(); + this.firstClickSource = me.getSource(); + }; + + /** + * Overrides double click handling to add the tolerance and inserting text. + */ + Graph.prototype.dblClick = function(evt, cell) + { + if (this.isEnabled()) + { + var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + + // Automatically adds new child cells to edges on double click + if (evt != null && !this.model.isVertex(cell)) + { + var state = (this.model.isEdge(cell)) ? this.view.getState(cell) : null; + var src = mxEvent.getSource(evt); + + if (this.firstClickState == state && this.firstClickSource == src) + { + if (state == null || (state.text == null || state.text.node == null || + (!mxUtils.contains(state.text.boundingBox, pt.x, pt.y) && + !mxUtils.isAncestorNode(state.text.node, mxEvent.getSource(evt))))) + { + if ((state == null && !this.isCellLocked(this.getDefaultParent())) || + (state != null && !this.isCellLocked(state.cell))) + { + // Avoids accidental inserts on background + if (state != null || (mxClient.IS_VML && src == this.view.getCanvas()) || + (mxClient.IS_SVG && src == this.view.getCanvas().ownerSVGElement)) + { + cell = this.addText(pt.x, pt.y, state); + } + } + } + } + } + + mxGraph.prototype.dblClick.call(this, evt, cell); + } + }; + + /** + * Returns a point that specifies the location for inserting cells. + */ + Graph.prototype.getInsertPoint = function() + { + var gs = this.getGridSize(); + var dx = this.container.scrollLeft / this.view.scale - this.view.translate.x; + var dy = this.container.scrollTop / this.view.scale - this.view.translate.y; + + if (this.pageVisible) + { + var layout = this.getPageLayout(); + var page = this.getPageSize(); + dx = Math.max(dx, layout.x * page.width); + dy = Math.max(dy, layout.y * page.height); + } + + return new mxPoint(this.snap(dx + gs), this.snap(dy + gs)); + }; + + /** + * + */ + Graph.prototype.getFreeInsertPoint = function() + { + var view = this.view; + var bds = this.getGraphBounds(); + var pt = this.getInsertPoint(); + + // Places at same x-coord and 2 grid sizes below existing graph + var x = this.snap(Math.round(Math.max(pt.x, bds.x / view.scale - view.translate.x + + ((bds.width == 0) ? 2 * this.gridSize : 0)))); + var y = this.snap(Math.round(Math.max(pt.y, (bds.y + bds.height) / view.scale - view.translate.y + + 2 * this.gridSize))); + + return new mxPoint(x, y); + }; + + /** + * Hook for subclassers to return true if the current insert point was defined + * using a mouse hover event. + */ + Graph.prototype.isMouseInsertPoint = function() + { + return false; + }; + + /** + * Adds a new label at the given position and returns the new cell. State is + * an optional edge state to be used as the parent for the label. Vertices + * are not allowed currently as states. + */ + Graph.prototype.addText = function(x, y, state) + { + // Creates a new edge label with a predefined text + var label = new mxCell(); + label.value = 'Text'; + label.style = 'text;html=1;resizable=0;points=[];' + label.geometry = new mxGeometry(0, 0, 0, 0); + label.vertex = true; + + if (state != null) + { + label.style += 'align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;' + label.geometry.relative = true; + label.connectable = false; + + // Resets the relative location stored inside the geometry + var pt2 = this.view.getRelativePoint(state, x, y); + label.geometry.x = Math.round(pt2.x * 10000) / 10000; + label.geometry.y = Math.round(pt2.y); + + // Resets the offset inside the geometry to find the offset from the resulting point + label.geometry.offset = new mxPoint(0, 0); + pt2 = this.view.getPoint(state, label.geometry); + + var scale = this.view.scale; + label.geometry.offset = new mxPoint(Math.round((x - pt2.x) / scale), Math.round((y - pt2.y) / scale)); + } + else + { + label.style += 'autosize=1;align=left;verticalAlign=top;spacingTop=-4;' + + var tr = this.view.translate; + label.geometry.width = 40; + label.geometry.height = 20; + label.geometry.x = Math.round(x / this.view.scale) - tr.x; + label.geometry.y = Math.round(y / this.view.scale) - tr.y; + } + + this.getModel().beginUpdate(); + try + { + this.addCells([label], (state != null) ? state.cell : null); + this.fireEvent(new mxEventObject('textInserted', 'cells', [label])); + // Updates size of text after possible change of style via event + this.autoSizeCell(label); + } + finally + { + this.getModel().endUpdate(); + } + + return label; + }; + + /** + * + */ + Graph.prototype.getAbsoluteUrl = function(url) + { + if (url != null && this.isRelativeUrl(url)) + { + if (url.charAt(0) == '#') + { + url = this.baseUrl + url; + } + else if (url.charAt(0) == '/') + { + url = this.domainUrl + url; + } + else + { + url = this.domainPathUrl + url; + } + } + + return url; + }; + + /** + * Adds a handler for clicking on shapes with links. This replaces all links in labels. + */ + Graph.prototype.addClickHandler = function(highlight, beforeClick, onClick) + { + // Replaces links in labels for consistent right-clicks + var checkLinks = mxUtils.bind(this, function() + { + var links = this.container.getElementsByTagName('a'); + + if (links != null) + { + for (var i = 0; i < links.length; i++) + { + var href = this.getAbsoluteUrl(links[i].getAttribute('href')); + + if (href != null) + { + links[i].setAttribute('href', href); + + if (beforeClick != null) + { + mxEvent.addGestureListeners(links[i], null, null, beforeClick); + } + } + } + } + }); + + this.model.addListener(mxEvent.CHANGE, checkLinks); + checkLinks(); + + var cursor = this.container.style.cursor; + var tol = this.getTolerance(); + var graph = this; + + var mouseListener = + { + currentState: null, + currentLink: null, + highlight: (highlight != null && highlight != '' && highlight != mxConstants.NONE) ? + new mxCellHighlight(graph, highlight, 4) : null, + startX: 0, + startY: 0, + scrollLeft: 0, + scrollTop: 0, + updateCurrentState: function(me) + { + var tmp = me.sourceState; + + // Gets topmost intersecting cell with link + if (tmp == null || graph.getLinkForCell(tmp.cell) == null) + { + var cell = graph.getCellAt(me.getGraphX(), me.getGraphY(), null, null, null, function(state, x, y) + { + return graph.getLinkForCell(state.cell) == null; + }); + + tmp = graph.view.getState(cell); + } + + if (tmp != this.currentState) + { + if (this.currentState != null) + { + this.clear(); + } + + this.currentState = tmp; + + if (this.currentState != null) + { + this.activate(this.currentState); + } + } + }, + mouseDown: function(sender, me) + { + this.startX = me.getGraphX(); + this.startY = me.getGraphY(); + this.scrollLeft = graph.container.scrollLeft; + this.scrollTop = graph.container.scrollTop; + + if (this.currentLink == null && graph.container.style.overflow == 'auto') + { + graph.container.style.cursor = 'move'; + } + + this.updateCurrentState(me); + }, + mouseMove: function(sender, me) + { + if (graph.isMouseDown) + { + if (this.currentLink != null) + { + var dx = Math.abs(this.startX - me.getGraphX()); + var dy = Math.abs(this.startY - me.getGraphY()); + + if (dx > tol || dy > tol) + { + this.clear(); + } + } + } + else + { + // Checks for parent link + var linkNode = me.getSource(); + + while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') + { + linkNode = linkNode.parentNode; + } + + if (linkNode != null) + { + this.clear(); + } + else + { + if (graph.tooltipHandler != null && this.currentLink != null && this.currentState != null) + { + graph.tooltipHandler.reset(me, true, this.currentState); + } + + if (this.currentState != null && (me.getState() == this.currentState || me.sourceState == null) && + graph.intersects(this.currentState, me.getGraphX(), me.getGraphY())) + { + return; + } + + this.updateCurrentState(me); + } + } + }, + mouseUp: function(sender, me) + { + var source = me.getSource(); + var evt = me.getEvent(); + + // Checks for parent link + var linkNode = source; + + while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') + { + linkNode = linkNode.parentNode; + } + + // Ignores clicks on links and collapse/expand icon + if (linkNode == null && + (((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (me.sourceState == null || !me.isSource(me.sourceState.control))) && + (((mxEvent.isLeftMouseButton(evt) || mxEvent.isMiddleMouseButton(evt)) && + !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)))) + { + if (this.currentLink != null) + { + var blank = graph.isBlankLink(this.currentLink); + + if ((this.currentLink.substring(0, 5) === 'data:' || + !blank) && beforeClick != null) + { + beforeClick(evt, this.currentLink); + } + + if (!mxEvent.isConsumed(evt)) + { + var target = (mxEvent.isMiddleMouseButton(evt)) ? '_blank' : + ((blank) ? graph.linkTarget : '_top'); + graph.openLink(this.currentLink, target); + me.consume(); + } + } + else if (onClick != null && !me.isConsumed() && + (Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && + Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && + (Math.abs(this.startX - me.getGraphX()) < tol && + Math.abs(this.startY - me.getGraphY()) < tol)) + { + onClick(me.getEvent()); + } + } + + this.clear(); + }, + activate: function(state) + { + this.currentLink = graph.getAbsoluteUrl(graph.getLinkForCell(state.cell)); + + if (this.currentLink != null) + { + graph.container.style.cursor = 'pointer'; + + if (this.highlight != null) + { + this.highlight.highlight(state); + } + } + }, + clear: function() + { + if (graph.container != null) + { + graph.container.style.cursor = cursor; + } + + this.currentState = null; + this.currentLink = null; + + if (this.highlight != null) + { + this.highlight.hide(); + } + + if (graph.tooltipHandler != null) + { + graph.tooltipHandler.hide(); + } + } + }; + + // Ignores built-in click handling + graph.click = function(me) {}; + graph.addMouseListener(mouseListener); + + mxEvent.addListener(document, 'mouseleave', function(evt) + { + mouseListener.clear(); + }); + }; + + /** + * Duplicates the given cells and returns the duplicates. + */ + Graph.prototype.duplicateCells = function(cells, append) + { + cells = (cells != null) ? cells : this.getSelectionCells(); + append = (append != null) ? append : true; + + cells = this.model.getTopmostCells(cells); + + var model = this.getModel(); + var s = this.gridSize; + var select = []; + + model.beginUpdate(); + try + { + var clones = this.cloneCells(cells, false); + + for (var i = 0; i < cells.length; i++) + { + var parent = model.getParent(cells[i]); + var child = this.moveCells([clones[i]], s, s, false)[0]; + select.push(child); + + if (append) + { + model.add(parent, clones[i]); + } + else + { + // Maintains child index by inserting after clone in parent + var index = parent.getIndex(cells[i]); + model.add(parent, clones[i], index + 1); + } + } + } + finally + { + model.endUpdate(); + } + + return select; + }; + + /** + * Inserts the given image at the cursor in a content editable text box using + * the insertimage command on the document instance. + */ + Graph.prototype.insertImage = function(newValue, w, h) + { + // To find the new image, we create a list of all existing links first + if (newValue != null) + { + var tmp = this.cellEditor.textarea.getElementsByTagName('img'); + var oldImages = []; + + for (var i = 0; i < tmp.length; i++) + { + oldImages.push(tmp[i]); + } + + // LATER: Fix inserting link/image in IE8/quirks after focus lost + document.execCommand('insertimage', false, newValue); + + // Sets size of new image + var newImages = this.cellEditor.textarea.getElementsByTagName('img'); + + if (newImages.length == oldImages.length + 1) + { + // Inverse order in favor of appended images + for (var i = newImages.length - 1; i >= 0; i--) + { + if (i == 0 || newImages[i] != oldImages[i - 1]) + { + // Workaround for lost styles during undo and redo is using attributes + newImages[i].setAttribute('width', w); + newImages[i].setAttribute('height', h); + + break; + } + } + } + } + }; + + /** + * Inserts the given image at the cursor in a content editable text box using + * the insertimage command on the document instance. + */ + Graph.prototype.insertLink = function(value) + { + if (value.length == 0) + { + document.execCommand('unlink', false); + } + else + { + // LATER: Fix inserting link/image in IE8/quirks after focus lost + document.execCommand('createlink', false, mxUtils.trim(value)); + } + }; + + /** + * + * @param cell + * @returns {Boolean} + */ + Graph.prototype.isCellResizable = function(cell) + { + var result = mxGraph.prototype.isCellResizable.apply(this, arguments); + + var state = this.view.getState(cell); + var style = (state != null) ? state.style : this.getCellStyle(cell); + + return result || (mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0' && + style[mxConstants.STYLE_WHITE_SPACE] == 'wrap'); + }; + + /** + * Function: distributeCells + * + * Distribuets the centers of the given cells equally along the available + * horizontal or vertical space. + * + * Parameters: + * + * horizontal - Boolean that specifies the direction of the distribution. + * cells - Optional array of to be distributed. Edges are ignored. + */ + Graph.prototype.distributeCells = function(horizontal, cells) + { + if (cells == null) + { + cells = this.getSelectionCells(); + } + + if (cells != null && cells.length > 1) + { + var vertices = []; + var max = null; + var min = null; + + for (var i = 0; i < cells.length; i++) + { + if (this.getModel().isVertex(cells[i])) + { + var state = this.view.getState(cells[i]); + + if (state != null) + { + var tmp = (horizontal) ? state.getCenterX() : state.getCenterY(); + max = (max != null) ? Math.max(max, tmp) : tmp; + min = (min != null) ? Math.min(min, tmp) : tmp; + + vertices.push(state); + } + } + } + + if (vertices.length > 2) + { + vertices.sort(function(a, b) + { + return (horizontal) ? a.x - b.x : a.y - b.y; + }); + + var t = this.view.translate; + var s = this.view.scale; + + min = min / s - ((horizontal) ? t.x : t.y); + max = max / s - ((horizontal) ? t.x : t.y); + + this.getModel().beginUpdate(); + try + { + var dt = (max - min) / (vertices.length - 1); + var t0 = min; + + for (var i = 1; i < vertices.length - 1; i++) + { + var pstate = this.view.getState(this.model.getParent(vertices[i].cell)); + var geo = this.getCellGeometry(vertices[i].cell); + t0 += dt; + + if (geo != null && pstate != null) + { + geo = geo.clone(); + + if (horizontal) + { + geo.x = Math.round(t0 - geo.width / 2) - pstate.origin.x; + } + else + { + geo.y = Math.round(t0 - geo.height / 2) - pstate.origin.y; + } + + this.getModel().setGeometry(vertices[i].cell, geo); + } + } + } + finally + { + this.getModel().endUpdate(); + } + } + } + + return cells; + }; + + /** + * Adds meta-drag an Mac. + * @param evt + * @returns + */ + Graph.prototype.isCloneEvent = function(evt) + { + return (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || mxEvent.isControlDown(evt); + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.encodeCells = function(cells) + { + var clones = this.cloneCells(cells); + + // Creates a dictionary for fast lookups + var dict = new mxDictionary(); + + for (var i = 0; i < cells.length; i++) + { + dict.put(cells[i], true); + } + + // Checks for orphaned relative children and makes absolute + for (var i = 0; i < clones.length; i++) + { + var state = this.view.getState(cells[i]); + + if (state != null) + { + var geo = this.getCellGeometry(clones[i]); + + if (geo != null && geo.relative && !this.model.isEdge(cells[i]) && + !dict.get(this.model.getParent(cells[i]))) + { + geo.relative = false; + geo.x = state.x / state.view.scale - state.view.translate.x; + geo.y = state.y / state.view.scale - state.view.translate.y; + } + } + } + + var codec = new mxCodec(); + var model = new mxGraphModel(); + var parent = model.getChildAt(model.getRoot(), 0); + + for (var i = 0; i < cells.length; i++) + { + model.add(parent, clones[i]); + } + + return codec.encode(model); + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.createSvgImageExport = function() + { + var exp = new mxImageExport(); + + // Adds hyperlinks (experimental) + exp.getLinkForCellState = mxUtils.bind(this, function(state, canvas) + { + return this.getLinkForCell(state.cell); + }); + + return exp; + }; + + /** + * Translates this point by the given vector. + * + * @param {number} dx X-coordinate of the translation. + * @param {number} dy Y-coordinate of the translation. + */ + Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp, ignoreSelection, showText, imgExport) + { + //Disable Css Transforms if it is used + var origUseCssTrans = this.useCssTransforms; + + if (origUseCssTrans) + { + this.useCssTransforms = false; + this.view.revalidate(); + this.sizeDidChange(); + } + + try + { + scale = (scale != null) ? scale : 1; + border = (border != null) ? border : 0; + crisp = (crisp != null) ? crisp : true; + ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; + showText = (showText != null) ? showText : true; + + var bounds = (ignoreSelection || nocrop) ? + this.getGraphBounds() : this.getBoundingBox(this.getSelectionCells()); + + if (bounds == null) + { + throw Error(mxResources.get('drawingEmpty')); + } + + var vs = this.view.scale; + + // Prepares SVG document that holds the output + var svgDoc = mxUtils.createXmlDocument(); + var root = (svgDoc.createElementNS != null) ? + svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg'); + + if (background != null) + { + if (root.style != null) + { + root.style.backgroundColor = background; + } + else + { + root.setAttribute('style', 'background-color:' + background); + } + } + + if (svgDoc.createElementNS == null) + { + root.setAttribute('xmlns', mxConstants.NS_SVG); + root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK); + } + else + { + // KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround. + root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK); + } + + var s = scale / vs; + root.setAttribute('width', Math.max(1, Math.ceil(bounds.width * s) + 2 * border) + 'px'); + root.setAttribute('height', Math.max(1, Math.ceil(bounds.height * s) + 2 * border) + 'px'); + root.setAttribute('version', '1.1'); + + // Adds group for anti-aliasing via transform + var node = root; + + if (crisp) + { + var group = (svgDoc.createElementNS != null) ? + svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g'); + group.setAttribute('transform', 'translate(0.5,0.5)'); + root.appendChild(group); + svgDoc.appendChild(root); + node = group; + } + else + { + svgDoc.appendChild(root); + } + + // Renders graph. Offset will be multiplied with state's scale when painting state. + // TextOffset only seems to affect FF output but used everywhere for consistency. + var svgCanvas = this.createSvgCanvas(node); + svgCanvas.foOffset = (crisp) ? -0.5 : 0; + svgCanvas.textOffset = (crisp) ? -0.5 : 0; + svgCanvas.imageOffset = (crisp) ? -0.5 : 0; + svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs), Math.floor((border / scale - bounds.y) / vs)); + + // Convert HTML entities + var htmlConverter = document.createElement('textarea'); + + // Adds simple text fallback for viewers with no support for foreignObjects + var createAlternateContent = svgCanvas.createAlternateContent; + svgCanvas.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation) + { + var s = this.state; + + // Assumes a max character width of 0.2em + if (this.foAltText != null && (w == 0 || (s.fontSize != 0 && str.length < (w * 5) / s.fontSize))) + { + var alt = this.createElement('text'); + alt.setAttribute('x', Math.round(w / 2)); + alt.setAttribute('y', Math.round((h + s.fontSize) / 2)); + alt.setAttribute('fill', s.fontColor || 'black'); + alt.setAttribute('text-anchor', 'middle'); + alt.setAttribute('font-size', Math.round(s.fontSize) + 'px'); + alt.setAttribute('font-family', s.fontFamily); + + if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) + { + alt.setAttribute('font-weight', 'bold'); + } + + if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) + { + alt.setAttribute('font-style', 'italic'); + } + + if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) + { + alt.setAttribute('text-decoration', 'underline'); + } + + try + { + htmlConverter.innerHTML = str; + alt.textContent = htmlConverter.value; + + return alt; + } + catch (e) + { + return createAlternateContent.apply(this, arguments); + } + } + else + { + return createAlternateContent.apply(this, arguments); + } + }; + + // Paints background image + var bgImg = this.backgroundImage; + + if (bgImg != null) + { + var s2 = vs / scale; + var tr = this.view.translate; + var tmp = new mxRectangle(tr.x * s2, tr.y * s2, bgImg.width * s2, bgImg.height * s2); + + // Checks if visible + if (mxUtils.intersects(bounds, tmp)) + { + svgCanvas.image(tr.x, tr.y, bgImg.width, bgImg.height, bgImg.src, true); + } + } + + svgCanvas.scale(s); + svgCanvas.textEnabled = showText; + + imgExport = (imgExport != null) ? imgExport : this.createSvgImageExport(); + var imgExportDrawCellState = imgExport.drawCellState; + + // Implements ignoreSelection flag + imgExport.drawCellState = function(state, canvas) + { + var graph = state.view.graph; + var selected = graph.isCellSelected(state.cell); + var parent = graph.model.getParent(state.cell); + + // Checks if parent cell is selected + while (!ignoreSelection && !selected && parent != null) + { + selected = graph.isCellSelected(parent); + parent = graph.model.getParent(parent); + } + + if (ignoreSelection || selected) + { + imgExportDrawCellState.apply(this, arguments); + } + }; + + imgExport.drawState(this.getView().getState(this.model.root), svgCanvas); + + return root; + } + finally + { + if (origUseCssTrans) + { + this.useCssTransforms = true; + this.view.revalidate(); + this.sizeDidChange(); + } + } + }; + + /** + * Hook for creating the canvas used in getSvg. + */ + Graph.prototype.createSvgCanvas = function(node) + { + return new mxSvgCanvas2D(node); + }; + + /** + * Returns the first ancestor of the current selection with the given name. + */ + Graph.prototype.getSelectedElement = function() + { + var node = null; + + if (window.getSelection) + { + var sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var range = sel.getRangeAt(0); + node = range.commonAncestorContainer; + } + } + else if (document.selection) + { + node = document.selection.createRange().parentElement(); + } + + return node; + }; + + /** + * Returns the first ancestor of the current selection with the given name. + */ + Graph.prototype.getParentByName = function(node, name, stopAt) + { + while (node != null) + { + if (node.nodeName == name) + { + return node; + } + + if (node == stopAt) + { + return null; + } + + node = node.parentNode; + } + + return node; + }; + + /** + * Selects the given node. + */ + Graph.prototype.selectNode = function(node) + { + var sel = null; + + // IE9 and non-IE + if (window.getSelection) + { + sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var range = document.createRange(); + range.selectNode(node); + sel.removeAllRanges(); + sel.addRange(range); + } + } + // IE < 9 + else if ((sel = document.selection) && sel.type != 'Control') + { + var originalRange = sel.createRange(); + originalRange.collapse(true); + var range = sel.createRange(); + range.setEndPoint('StartToStart', originalRange); + range.select(); + } + }; + + /** + * Inserts a new row into the given table. + */ + Graph.prototype.insertRow = function(table, index) + { + var bd = table.tBodies[0]; + var cells = bd.rows[0].cells; + var cols = 0; + + // Counts columns including colspans + for (var i = 0; i < cells.length; i++) + { + var colspan = cells[i].getAttribute('colspan'); + cols = (colspan != null) ? parseInt(colspan) : 1; + } + + var row = bd.insertRow(index); + + for (var i = 0; i < cols; i++) + { + mxUtils.br(row.insertCell(-1)); + } + + return row.cells[0]; + }; + + /** + * Deletes the given column. + */ + Graph.prototype.deleteRow = function(table, index) + { + table.tBodies[0].deleteRow(index); + }; + + /** + * Deletes the given column. + */ + Graph.prototype.insertColumn = function(table, index) + { + var hd = table.tHead; + + if (hd != null) + { + // TODO: use colIndex + for (var h = 0; h < hd.rows.length; h++) + { + var th = document.createElement('th'); + hd.rows[h].appendChild(th); + mxUtils.br(th); + } + } + + var bd = table.tBodies[0]; + + for (var i = 0; i < bd.rows.length; i++) + { + var cell = bd.rows[i].insertCell(index); + mxUtils.br(cell); + } + + return bd.rows[0].cells[(index >= 0) ? index : bd.rows[0].cells.length - 1]; + }; + + /** + * Deletes the given column. + */ + Graph.prototype.deleteColumn = function(table, index) + { + if (index >= 0) + { + var bd = table.tBodies[0]; + var rows = bd.rows; + + for (var i = 0; i < rows.length; i++) + { + if (rows[i].cells.length > index) + { + rows[i].deleteCell(index); + } + } + } + }; + + /** + * Inserts the given HTML at the caret position (no undo). + */ + Graph.prototype.pasteHtmlAtCaret = function(html) + { + var sel, range; + + // IE9 and non-IE + if (window.getSelection) + { + sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + range = sel.getRangeAt(0); + range.deleteContents(); + + // Range.createContextualFragment() would be useful here but is + // only relatively recently standardized and is not supported in + // some browsers (IE9, for one) + var el = document.createElement("div"); + el.innerHTML = html; + var frag = document.createDocumentFragment(), node; + + while ((node = el.firstChild)) + { + lastNode = frag.appendChild(node); + } + + range.insertNode(frag); + } + } + // IE < 9 + else if ((sel = document.selection) && sel.type != "Control") + { + // FIXME: Does not work if selection is empty + sel.createRange().pasteHTML(html); + } + }; + + /** + * Creates an anchor elements for handling the given link in the + * hint that is shown when the cell is selected. + */ + Graph.prototype.createLinkForHint = function(link, label) + { + label = (label != null) ? label : link; + + var a = document.createElement('a'); + a.setAttribute('href', this.getAbsoluteUrl(link)); + + if (link != null && !this.isCustomLink(link)) + { + a.setAttribute('title', link); + } + + if (this.linkTarget != null) + { + a.setAttribute('target', this.linkTarget); + } + + // Adds shortened label to link + var max = 40; + var head = 26; + var tail = 10; + + if (label.length > max) + { + label = label.substring(0, head) + '...' + label.substring(label.length - tail); + } + + mxUtils.write(a, label); + + return a; + }; + + /** + * Customized graph for touch devices. + */ + Graph.prototype.initTouch = function() + { + // Disables new connections via "hotspot" + this.connectionHandler.marker.isEnabled = function() + { + return this.graph.connectionHandler.first != null; + }; + + // Hides menu when editing starts + this.addListener(mxEvent.START_EDITING, function(sender, evt) + { + this.popupMenuHandler.hideMenu(); + }); + + // Adds custom hit detection if native hit detection found no cell + var graphUpdateMouseEvent = this.updateMouseEvent; + this.updateMouseEvent = function(me) + { + me = graphUpdateMouseEvent.apply(this, arguments); + + if (mxEvent.isTouchEvent(me.getEvent()) && me.getState() == null) + { + var cell = this.getCellAt(me.graphX, me.graphY); + + if (cell != null && this.isSwimlane(cell) && this.hitsSwimlaneContent(cell, me.graphX, me.graphY)) + { + cell = null; + } + else + { + me.state = this.view.getState(cell); + + if (me.state != null && me.state.shape != null) + { + this.container.style.cursor = me.state.shape.node.style.cursor; + } + } + } + + if (me.getState() == null && this.isEnabled()) + { + this.container.style.cursor = 'default'; + } + + return me; + }; + + // Context menu trigger implementation depending on current selection state + // combined with support for normal popup trigger. + var cellSelected = false; + var selectionEmpty = false; + var menuShowing = false; + + var oldFireMouseEvent = this.fireMouseEvent; + + this.fireMouseEvent = function(evtName, me, sender) + { + if (evtName == mxEvent.MOUSE_DOWN) + { + // For hit detection on edges + me = this.updateMouseEvent(me); + + cellSelected = this.isCellSelected(me.getCell()); + selectionEmpty = this.isSelectionEmpty(); + menuShowing = this.popupMenuHandler.isMenuShowing(); + } + + oldFireMouseEvent.apply(this, arguments); + }; + + // Shows popup menu if cell was selected or selection was empty and background was clicked + // FIXME: Conflicts with mxPopupMenuHandler.prototype.getCellForPopupEvent in Editor.js by + // selecting parent for selected children in groups before this check can be made. + this.popupMenuHandler.mouseUp = mxUtils.bind(this, function(sender, me) + { + this.popupMenuHandler.popupTrigger = !this.isEditing() && this.isEnabled() && + (me.getState() == null || !me.isSource(me.getState().control)) && + (this.popupMenuHandler.popupTrigger || (!menuShowing && !mxEvent.isMouseEvent(me.getEvent()) && + ((selectionEmpty && me.getCell() == null && this.isSelectionEmpty()) || + (cellSelected && this.isCellSelected(me.getCell()))))); + mxPopupMenuHandler.prototype.mouseUp.apply(this.popupMenuHandler, arguments); + }); + }; + + /** + * HTML in-place editor + */ + mxCellEditor.prototype.isContentEditing = function() + { + var state = this.graph.view.getState(this.editingCell); + + return state != null && state.style['html'] == 1; + }; + + /** + * Creates the keyboard event handler for the current graph and history. + */ + mxCellEditor.prototype.saveSelection = function() + { + if (window.getSelection) + { + var sel = window.getSelection(); + + if (sel.getRangeAt && sel.rangeCount) + { + var ranges = []; + + for (var i = 0, len = sel.rangeCount; i < len; ++i) + { + ranges.push(sel.getRangeAt(i)); + } + + return ranges; + } + } + else if (document.selection && document.selection.createRange) + { + return document.selection.createRange(); + } + + return null; + }; + + /** + * Creates the keyboard event handler for the current graph and history. + */ + mxCellEditor.prototype.restoreSelection = function(savedSel) + { + try + { + if (savedSel) + { + if (window.getSelection) + { + sel = window.getSelection(); + sel.removeAllRanges(); + + for (var i = 0, len = savedSel.length; i < len; ++i) + { + sel.addRange(savedSel[i]); + } + } + else if (document.selection && savedSel.select) + { + savedSel.select(); + } + } + } + catch (e) + { + // ignore + } + }; + + /** + * Handling of special nl2Br style for not converting newlines to breaks in HTML labels. + * NOTE: Since it's easier to set this when the label is created we assume that it does + * not change during the lifetime of the mxText instance. + */ + var mxCellRendererInitializeLabel = mxCellRenderer.prototype.initializeLabel; + mxCellRenderer.prototype.initializeLabel = function(state) + { + if (state.text != null) + { + state.text.replaceLinefeeds = mxUtils.getValue(state.style, 'nl2Br', '1') != '0'; + } + + mxCellRendererInitializeLabel.apply(this, arguments); + }; + + var mxConstraintHandlerUpdate = mxConstraintHandler.prototype.update; + mxConstraintHandler.prototype.update = function(me, source) + { + if (this.isKeepFocusEvent(me) || !mxEvent.isAltDown(me.getEvent())) + { + mxConstraintHandlerUpdate.apply(this, arguments); + } + else + { + this.reset(); + } + }; + + /** + * No dashed shapes. + */ + mxGuide.prototype.createGuideShape = function(horizontal) + { + var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); + + return guide; + }; + + /** + * HTML in-place editor + */ + mxCellEditor.prototype.escapeCancelsEditing = false; + + var mxCellEditorStartEditing = mxCellEditor.prototype.startEditing; + mxCellEditor.prototype.startEditing = function(cell, trigger) + { + mxCellEditorStartEditing.apply(this, arguments); + + // Overrides class in case of HTML content to add + // dashed borders for divs and table cells + var state = this.graph.view.getState(cell); + + if (state != null && state.style['html'] == 1) + { + this.textarea.className = 'mxCellEditor geContentEditable'; + } + else + { + this.textarea.className = 'mxCellEditor mxPlainTextEditor'; + } + + // Toggles markup vs wysiwyg mode + this.codeViewMode = false; + + // Stores current selection range when switching between markup and code + this.switchSelectionState = null; + + // Selects editing cell + this.graph.setSelectionCell(cell); + + // Enables focus outline for edges and edge labels + var parent = this.graph.getModel().getParent(cell); + var geo = this.graph.getCellGeometry(cell); + + if ((this.graph.getModel().isEdge(parent) && geo != null && geo.relative) || + this.graph.getModel().isEdge(cell)) + { + // Quirks does not support outline at all so use border instead + if (mxClient.IS_QUIRKS) + { + this.textarea.style.border = 'gray dotted 1px'; + } + // IE>8 and FF on Windows uses outline default of none + else if (mxClient.IS_IE || mxClient.IS_IE11 || (mxClient.IS_FF && mxClient.IS_WIN)) + { + this.textarea.style.outline = 'gray dotted 1px'; + } + else + { + this.textarea.style.outline = ''; + } + } + else if (mxClient.IS_QUIRKS) + { + this.textarea.style.outline = 'none'; + this.textarea.style.border = ''; + } + } + + /** + * HTML in-place editor + */ + var cellEditorInstallListeners = mxCellEditor.prototype.installListeners; + mxCellEditor.prototype.installListeners = function(elt) + { + cellEditorInstallListeners.apply(this, arguments); + + // Adds a reference from the clone to the original node, recursively + function reference(node, clone) + { + clone.originalNode = node; + + node = node.firstChild; + var child = clone.firstChild; + + while (node != null && child != null) + { + reference(node, child); + node = node.nextSibling; + child = child.nextSibling; + } + + return clone; + }; + + // Checks the given node for new nodes, recursively + function checkNode(node, clone) + { + if (node != null) + { + if (clone.originalNode != node) + { + cleanNode(node); + } + else + { + node = node.firstChild; + clone = clone.firstChild; + + while (node != null) + { + var nextNode = node.nextSibling; + + if (clone == null) + { + cleanNode(node); + } + else + { + checkNode(node, clone); + clone = clone.nextSibling; + } + + node = nextNode; + } + } + } + }; + + // Removes unused DOM nodes and attributes, recursively + function cleanNode(node) + { + var child = node.firstChild; + + while (child != null) + { + var next = child.nextSibling; + cleanNode(child); + child = next; + } + + if ((node.nodeType != 1 || (node.nodeName !== 'BR' && node.firstChild == null)) && + (node.nodeType != 3 || mxUtils.trim(mxUtils.getTextContent(node)).length == 0)) + { + node.parentNode.removeChild(node); + } + else + { + // Removes linefeeds + if (node.nodeType == 3) + { + mxUtils.setTextContent(node, mxUtils.getTextContent(node).replace(/\n|\r/g, '')); + } + + // Removes CSS classes and styles (for Word and Excel) + if (node.nodeType == 1) + { + node.removeAttribute('style'); + node.removeAttribute('class'); + node.removeAttribute('width'); + node.removeAttribute('cellpadding'); + node.removeAttribute('cellspacing'); + node.removeAttribute('border'); + } + } + }; + + // Handles paste from Word, Excel etc by removing styles, classnames and unused nodes + // LATER: Fix undo/redo for paste + if (!mxClient.IS_QUIRKS && document.documentMode !== 7 && document.documentMode !== 8) + { + mxEvent.addListener(this.textarea, 'paste', mxUtils.bind(this, function(evt) + { + var clone = reference(this.textarea, this.textarea.cloneNode(true)); + + window.setTimeout(mxUtils.bind(this, function() + { + checkNode(this.textarea, clone); + }), 0); + })); + } + }; + + mxCellEditor.prototype.toggleViewMode = function() + { + var state = this.graph.view.getState(this.editingCell); + var nl2Br = state != null && mxUtils.getValue(state.style, 'nl2Br', '1') != '0'; + var tmp = this.saveSelection(); + + if (!this.codeViewMode) + { + // Clears the initial empty label on the first keystroke + if (this.clearOnChange && this.textarea.innerHTML == this.getEmptyLabelText()) + { + this.clearOnChange = false; + this.textarea.innerHTML = ''; + } + + // Removes newlines from HTML and converts breaks to newlines + // to match the HTML output in plain text + var content = mxUtils.htmlEntities(this.textarea.innerHTML); + + // Workaround for trailing line breaks being ignored in the editor + if (!mxClient.IS_QUIRKS && document.documentMode != 8) + { + content = mxUtils.replaceTrailingNewlines(content, '

'); + } + + content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '').replace(/<br\s*.?>/g, '
') : content, true); + this.textarea.className = 'mxCellEditor mxPlainTextEditor'; + + var size = mxConstants.DEFAULT_FONTSIZE; + + this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; + this.textarea.style.fontSize = Math.round(size) + 'px'; + this.textarea.style.textDecoration = ''; + this.textarea.style.fontWeight = 'normal'; + this.textarea.style.fontStyle = ''; + this.textarea.style.fontFamily = mxConstants.DEFAULT_FONTFAMILY; + this.textarea.style.textAlign = 'left'; + + // Adds padding to make cursor visible with borders + this.textarea.style.padding = '2px'; + + if (this.textarea.innerHTML != content) + { + this.textarea.innerHTML = content; + } + + this.codeViewMode = true; + } + else + { + var content = mxUtils.extractTextWithWhitespace(this.textarea.childNodes); + + // Strips trailing line break + if (content.length > 0 && content.charAt(content.length - 1) == '\n') + { + content = content.substring(0, content.length - 1); + } + + content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '
') : content, true) + this.textarea.className = 'mxCellEditor geContentEditable'; + + var size = mxUtils.getValue(state.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE); + var family = mxUtils.getValue(state.style, mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY); + var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT); + var bold = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & + mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD; + var italic = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & + mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC; + var uline = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & + mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE; + + this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; + this.textarea.style.fontSize = Math.round(size) + 'px'; + this.textarea.style.textDecoration = (uline) ? 'underline' : ''; + this.textarea.style.fontWeight = (bold) ? 'bold' : 'normal'; + this.textarea.style.fontStyle = (italic) ? 'italic' : ''; + this.textarea.style.fontFamily = family; + this.textarea.style.textAlign = align; + this.textarea.style.padding = '0px'; + + if (this.textarea.innerHTML != content) + { + this.textarea.innerHTML = content; + + if (this.textarea.innerHTML.length == 0) + { + this.textarea.innerHTML = this.getEmptyLabelText(); + this.clearOnChange = this.textarea.innerHTML.length > 0; + } + } + + this.codeViewMode = false; + } + + this.textarea.focus(); + + if (this.switchSelectionState != null) + { + this.restoreSelection(this.switchSelectionState); + } + + this.switchSelectionState = tmp; + this.resize(); + }; + + var mxCellEditorResize = mxCellEditor.prototype.resize; + mxCellEditor.prototype.resize = function(state, trigger) + { + if (this.textarea != null) + { + var state = this.graph.getView().getState(this.editingCell); + + if (this.codeViewMode && state != null) + { + var scale = state.view.scale; + this.bounds = mxRectangle.fromRectangle(state); + + // General placement of code editor if cell has no size + // LATER: Fix HTML editor bounds for edge labels + if (this.bounds.width == 0 && this.bounds.height == 0) + { + this.bounds.width = 160 * scale; + this.bounds.height = 60 * scale; + + var m = (state.text != null) ? state.text.margin : null; + + if (m == null) + { + m = mxUtils.getAlignmentAsPoint(mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER), + mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE)); + } + + this.bounds.x += m.x * this.bounds.width; + this.bounds.y += m.y * this.bounds.height; + } + + this.textarea.style.width = Math.round((this.bounds.width - 4) / scale) + 'px'; + this.textarea.style.height = Math.round((this.bounds.height - 4) / scale) + 'px'; + this.textarea.style.overflow = 'auto'; + + // Adds scrollbar offset if visible + if (this.textarea.clientHeight < this.textarea.offsetHeight) + { + this.textarea.style.height = Math.round((this.bounds.height / scale)) + (this.textarea.offsetHeight - this.textarea.clientHeight) + 'px'; + this.bounds.height = parseInt(this.textarea.style.height) * scale; + } + + if (this.textarea.clientWidth < this.textarea.offsetWidth) + { + this.textarea.style.width = Math.round((this.bounds.width / scale)) + (this.textarea.offsetWidth - this.textarea.clientWidth) + 'px'; + this.bounds.width = parseInt(this.textarea.style.width) * scale; + } + + this.textarea.style.left = Math.round(this.bounds.x) + 'px'; + this.textarea.style.top = Math.round(this.bounds.y) + 'px'; + + if (mxClient.IS_VML) + { + this.textarea.style.zoom = scale; + } + else + { + mxUtils.setPrefixedStyle(this.textarea.style, 'transform', 'scale(' + scale + ',' + scale + ')'); + } + } + else + { + this.textarea.style.height = ''; + this.textarea.style.overflow = ''; + mxCellEditorResize.apply(this, arguments); + } + } + }; + + mxCellEditorGetInitialValue = mxCellEditor.prototype.getInitialValue; + mxCellEditor.prototype.getInitialValue = function(state, trigger) + { + if (mxUtils.getValue(state.style, 'html', '0') == '0') + { + return mxCellEditorGetInitialValue.apply(this, arguments); + } + else + { + var result = this.graph.getEditingValue(state.cell, trigger) + + if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1') + { + result = result.replace(/\n/g, '
'); + } + + result = this.graph.sanitizeHtml(result, true); + + return result; + } + }; + + mxCellEditorGetCurrentValue = mxCellEditor.prototype.getCurrentValue; + mxCellEditor.prototype.getCurrentValue = function(state) + { + if (mxUtils.getValue(state.style, 'html', '0') == '0') + { + return mxCellEditorGetCurrentValue.apply(this, arguments); + } + else + { + var result = this.graph.sanitizeHtml(this.textarea.innerHTML, true); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1') + { + result = result.replace(/\r\n/g, '
').replace(/\n/g, '
'); + } + else + { + result = result.replace(/\r\n/g, '').replace(/\n/g, ''); + } + + return result; + } + }; + + var mxCellEditorStopEditing = mxCellEditor.prototype.stopEditing; + mxCellEditor.prototype.stopEditing = function(cancel) + { + // Restores default view mode before applying value + if (this.codeViewMode) + { + this.toggleViewMode(); + } + + mxCellEditorStopEditing.apply(this, arguments); + + // Tries to move focus back to container after editing if possible + this.focusContainer(); + }; + + mxCellEditor.prototype.focusContainer = function() + { + try + { + this.graph.container.focus(); + } + catch (e) + { + // ignore + } + }; + + var mxCellEditorApplyValue = mxCellEditor.prototype.applyValue; + mxCellEditor.prototype.applyValue = function(state, value) + { + // Removes empty relative child labels in edges + this.graph.getModel().beginUpdate(); + + try + { + mxCellEditorApplyValue.apply(this, arguments); + + if (this.graph.isCellDeletable(state.cell) && this.graph.model.getChildCount(state.cell) == 0) + { + var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); + var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); + + if (value == '' && stroke == mxConstants.NONE && fill == mxConstants.NONE) + { + this.graph.removeCells([state.cell], false); + } + } + } + finally + { + this.graph.getModel().endUpdate(); + } + }; + + /** + * Returns the background color to be used for the editing box. This returns + * the label background for edge labels and null for all other cases. + */ + mxCellEditor.prototype.getBackgroundColor = function(state) + { + var color = null; + + if (this.graph.getModel().isEdge(state.cell) || this.graph.getModel().isEdge(this.graph.getModel().getParent(state.cell))) + { + var color = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, null); + + if (color == mxConstants.NONE) + { + color = null; + } + } + + return color; + }; + + mxCellEditor.prototype.getMinimumSize = function(state) + { + var scale = this.graph.getView().scale; + + return new mxRectangle(0, 0, (state.text == null) ? 30 : state.text.size * scale + 20, 30); + }; + + // Hold alt to ignore drop target + var mxGraphHandlerMoveCells = mxGraphHandler.prototype.moveCells; + + mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt) + { + if (mxEvent.isAltDown(evt)) + { + target = null; + } + + mxGraphHandlerMoveCells.apply(this, arguments); + }; + + /** + * Hints on handlers + */ + function createHint() + { + var hint = document.createElement('div'); + hint.className = 'geHint'; + hint.style.whiteSpace = 'nowrap'; + hint.style.position = 'absolute'; + + return hint; + }; + + /** + * Updates the hint for the current operation. + */ + mxGraphHandler.prototype.updateHint = function(me) + { + if (this.shape != null) + { + if (this.hint == null) + { + this.hint = createHint(); + this.graph.container.appendChild(this.hint); + } + + var t = this.graph.view.translate; + var s = this.graph.view.scale; + var x = this.roundLength((this.bounds.x + this.currentDx) / s - t.x); + var y = this.roundLength((this.bounds.y + this.currentDy) / s - t.y); + + this.hint.innerHTML = x + ', ' + y; + + this.hint.style.left = (this.shape.bounds.x + Math.round((this.shape.bounds.width - this.hint.clientWidth) / 2)) + 'px'; + this.hint.style.top = (this.shape.bounds.y + this.shape.bounds.height + 12) + 'px'; + } + }; + + /** + * Updates the hint for the current operation. + */ + mxGraphHandler.prototype.removeHint = function() + { + if (this.hint != null) + { + this.hint.parentNode.removeChild(this.hint); + this.hint = null; + } + }; + + /** + * Enables recursive resize for groups. + */ + mxVertexHandler.prototype.isRecursiveResize = function(state, me) + { + return !this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 && + !mxEvent.isControlDown(me.getEvent()) && !this.graph.isCellCollapsed(state.cell) && + mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' && + mxUtils.getValue(state.style, 'childLayout', null) == null; + }; + + /** + * Enables centered resize events. + */ + mxVertexHandler.prototype.isCenteredEvent = function(state, me) + { + return (!(!this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 && + !this.graph.isCellCollapsed(state.cell) && + mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' && + mxUtils.getValue(state.style, 'childLayout', null) == null) && + mxEvent.isControlDown(me.getEvent())) || + mxEvent.isMetaDown(me.getEvent()); + }; + + var vertexHandlerGetHandlePadding = mxVertexHandler.prototype.getHandlePadding; + mxVertexHandler.prototype.getHandlePadding = function() + { + var result = new mxPoint(0, 0); + var tol = this.tolerance; + + if (this.graph.cellEditor.getEditingCell() == this.state.cell && + this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null) + { + tol /= 2; + + result.x = this.sizers[0].bounds.width + tol; + result.y = this.sizers[0].bounds.height + tol; + } + else + { + result = vertexHandlerGetHandlePadding.apply(this, arguments); + } + + return result; + }; + + /** + * Updates the hint for the current operation. + */ + mxVertexHandler.prototype.updateHint = function(me) + { + if (this.index != mxEvent.LABEL_HANDLE) + { + if (this.hint == null) + { + this.hint = createHint(); + this.state.view.graph.container.appendChild(this.hint); + } + + if (this.index == mxEvent.ROTATION_HANDLE) + { + this.hint.innerHTML = this.currentAlpha + '°'; + } + else + { + var s = this.state.view.scale; + this.hint.innerHTML = this.roundLength(this.bounds.width / s) + ' x ' + this.roundLength(this.bounds.height / s); + } + + var rot = (this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0'; + var bb = mxUtils.getBoundingBox(this.bounds, rot); + + if (bb == null) + { + bb = this.bounds; + } + + this.hint.style.left = bb.x + Math.round((bb.width - this.hint.clientWidth) / 2) + 'px'; + this.hint.style.top = (bb.y + bb.height + 12) + 'px'; + + if (this.linkHint != null) + { + this.linkHint.style.display = 'none'; + } + } + }; + + /** + * Updates the hint for the current operation. + */ + mxVertexHandler.prototype.removeHint = function() + { + mxGraphHandler.prototype.removeHint.apply(this, arguments); + + if (this.linkHint != null) + { + this.linkHint.style.display = ''; + } + }; + + /** + * Updates the hint for the current operation. + */ + mxEdgeHandler.prototype.updateHint = function(me, point) + { + if (this.hint == null) + { + this.hint = createHint(); + this.state.view.graph.container.appendChild(this.hint); + } + + var t = this.graph.view.translate; + var s = this.graph.view.scale; + var x = this.roundLength(point.x / s - t.x); + var y = this.roundLength(point.y / s - t.y); + + this.hint.innerHTML = x + ', ' + y; + this.hint.style.visibility = 'visible'; + + if (this.isSource || this.isTarget) + { + if (this.constraintHandler.currentConstraint != null && + this.constraintHandler.currentFocus != null) + { + var pt = this.constraintHandler.currentConstraint.point; + this.hint.innerHTML = '[' + Math.round(pt.x * 100) + '%, '+ Math.round(pt.y * 100) + '%]'; + } + else if (this.marker.hasValidState()) + { + this.hint.style.visibility = 'hidden'; + } + } + + this.hint.style.left = Math.round(me.getGraphX() - this.hint.clientWidth / 2) + 'px'; + this.hint.style.top = (Math.max(me.getGraphY(), point.y) + this.state.view.graph.gridSize) + 'px'; + + if (this.linkHint != null) + { + this.linkHint.style.display = 'none'; + } + }; + + /** + * Updates the hint for the current operation. + */ + mxEdgeHandler.prototype.removeHint = mxVertexHandler.prototype.removeHint; + + /** + * Defines the handles for the UI. Uses data-URIs to speed-up loading time where supported. + */ + // TODO: Increase handle padding + HoverIcons.prototype.mainHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-main.png', 17, 17) : + Graph.createSvgImage(18, 18, ''); + HoverIcons.prototype.secondaryHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-secondary.png', 17, 17) : + Graph.createSvgImage(16, 16, ''); + HoverIcons.prototype.fixedHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-fixed.png', 17, 17) : + Graph.createSvgImage(18, 18, ''); + HoverIcons.prototype.terminalHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-terminal.png', 17, 17) : + Graph.createSvgImage(18, 18, ''); + HoverIcons.prototype.rotationHandle = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAVCAYAAACkCdXRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA6ZJREFUeNqM001IY1cUB/D/fYmm2sbR2lC1zYlgoRG6MpEyBlpxM9iFIGKFIm3s0lCKjOByhCLZCFqLBF1YFVJdSRbdFHRhBbULtRuFVBTzYRpJgo2mY5OX5N9Fo2TG+eiFA/dd3vvd8+65ByTxshARTdf1JySp6/oTEdFe9T5eg5lIcnBwkCSZyWS+exX40oyur68/KxaLf5Okw+H4X+A9JBaLfUySZ2dnnJqaosPhIAACeC34DJRKpb7IZrMcHx+nwWCgUopGo/EOKwf9fn/1CzERUevr6+9ls1mOjIwQAH0+H4PBIKPR6D2ofAQCgToRUeVYJUkuLy8TANfW1kiS8/PzCy84Mw4MDBAAZ2dnmc/nub+/X0MSEBF1cHDwMJVKsaGhgV6vl+l0mqOjo1+KyKfl1dze3l4NBoM/PZ+diFSLiIKIGBOJxA9bW1sEwNXVVSaTyQMRaRaRxrOzs+9J8ujoaE5EPhQRq67rcZ/PRwD0+/3Udf03EdEgIqZisZibnJykwWDg4eEhd3Z2xkXELCJvPpdBrYjUiEhL+Xo4HH4sIhUaAKNSqiIcDsNkMqG+vh6RSOQQQM7tdhsAQCkFAHC73UUATxcWFqypVApmsxnDw8OwWq2TADQNgAYAFosF+XweyWQSdru9BUBxcXFRB/4rEgDcPouIIx6P4+bmBi0tLSCpAzBqAIqnp6c/dnZ2IpfLYXNzE62traMADACKNputpr+/v8lms9UAKAAwiMjXe3t7KBQKqKurQy6Xi6K0i2l6evpROp1mbW0t29vbGY/Hb8/IVIqq2zlJXl1dsaOjg2azmefn5wwEAl+JSBVExCgi75PkzMwMlVJsbGxkIpFgPp8PX15ePopEIs3JZPITXdf/iEajbGpqolKKExMT1HWdHo/nIxGpgIgoEXnQ3d39kCTHxsYIgC6Xi3NzcwyHw8xkMozFYlxaWmJbWxuVUuzt7WUul6PX6/1cRN4WEe2uA0SkaWVl5XGpRVhdXU0A1DSNlZWVdz3qdDrZ09PDWCzG4+Pjn0XEWvp9KJKw2WwKwBsA3gHQHAqFfr24uMDGxgZ2d3cRiUQAAHa7HU6nE319fTg5Ofmlq6vrGwB/AngaCoWK6rbsNptNA1AJoA7Aux6Pp3NoaMhjsVg+QNmIRqO/u1yubwFEASRKUAEA7rASqABUAKgC8KAUb5XWCOAfAFcA/gJwDSB7C93DylCtdM8qABhLc5TumV6KQigUeubjfwcAHkQJ94ndWeYAAAAASUVORK5CYII=' : + IMAGE_PATH + '/handle-rotate.png', 19, 21); + + if (mxClient.IS_SVG) + { + mxConstraintHandler.prototype.pointImage = Graph.createSvgImage(5, 5, ''); + } + + mxVertexHandler.prototype.handleImage = HoverIcons.prototype.mainHandle; + mxVertexHandler.prototype.secondaryHandleImage = HoverIcons.prototype.secondaryHandle; + mxEdgeHandler.prototype.handleImage = HoverIcons.prototype.mainHandle; + mxEdgeHandler.prototype.terminalHandleImage = HoverIcons.prototype.terminalHandle; + mxEdgeHandler.prototype.fixedHandleImage = HoverIcons.prototype.fixedHandle; + mxEdgeHandler.prototype.labelHandleImage = HoverIcons.prototype.secondaryHandle; + mxOutline.prototype.sizerImage = HoverIcons.prototype.mainHandle; + + if (window.Sidebar != null) + { + Sidebar.prototype.triangleUp = HoverIcons.prototype.triangleUp; + Sidebar.prototype.triangleRight = HoverIcons.prototype.triangleRight; + Sidebar.prototype.triangleDown = HoverIcons.prototype.triangleDown; + Sidebar.prototype.triangleLeft = HoverIcons.prototype.triangleLeft; + Sidebar.prototype.refreshTarget = HoverIcons.prototype.refreshTarget; + Sidebar.prototype.roundDrop = HoverIcons.prototype.roundDrop; + } + + // Pre-fetches images (only needed for non data-uris) + if (!mxClient.IS_SVG) + { + new Image().src = HoverIcons.prototype.mainHandle.src; + new Image().src = HoverIcons.prototype.fixedHandle.src; + new Image().src = HoverIcons.prototype.terminalHandle.src; + new Image().src = HoverIcons.prototype.secondaryHandle.src; + new Image().src = HoverIcons.prototype.rotationHandle.src; + + new Image().src = HoverIcons.prototype.triangleUp.src; + new Image().src = HoverIcons.prototype.triangleRight.src; + new Image().src = HoverIcons.prototype.triangleDown.src; + new Image().src = HoverIcons.prototype.triangleLeft.src; + new Image().src = HoverIcons.prototype.refreshTarget.src; + new Image().src = HoverIcons.prototype.roundDrop.src; + } + + // Adds rotation handle and live preview + mxVertexHandler.prototype.rotationEnabled = true; + mxVertexHandler.prototype.manageSizers = true; + mxVertexHandler.prototype.livePreview = true; + + // Increases default rubberband opacity (default is 20) + mxRubberband.prototype.defaultOpacity = 30; + + // Enables connections along the outline, virtual waypoints, parent highlight etc + mxConnectionHandler.prototype.outlineConnect = true; + mxCellHighlight.prototype.keepOnTop = true; + mxVertexHandler.prototype.parentHighlightEnabled = true; + mxVertexHandler.prototype.rotationHandleVSpacing = -20; + + mxEdgeHandler.prototype.parentHighlightEnabled = true; + mxEdgeHandler.prototype.dblClickRemoveEnabled = true; + mxEdgeHandler.prototype.straightRemoveEnabled = true; + mxEdgeHandler.prototype.virtualBendsEnabled = true; + mxEdgeHandler.prototype.mergeRemoveEnabled = true; + mxEdgeHandler.prototype.manageLabelHandle = true; + mxEdgeHandler.prototype.outlineConnect = true; + + // Disables adding waypoints if shift is pressed + mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me) + { + return !mxEvent.isShiftDown(me.getEvent()); + }; + + // Disables custom handles if shift is pressed + mxEdgeHandler.prototype.isCustomHandleEvent = function(me) + { + return !mxEvent.isShiftDown(me.getEvent()); + }; + + /** + * Implements touch style + */ + if (Graph.touchStyle) + { + // Larger tolerance for real touch devices + if (mxClient.IS_TOUCH || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) + { + mxShape.prototype.svgStrokeTolerance = 18; + mxVertexHandler.prototype.tolerance = 12; + mxEdgeHandler.prototype.tolerance = 12; + Graph.prototype.tolerance = 12; + + mxVertexHandler.prototype.rotationHandleVSpacing = -24; + + // Implements a smaller tolerance for mouse events and a larger tolerance for touch + // events on touch devices. The default tolerance (4px) is used for mouse events. + mxConstraintHandler.prototype.getTolerance = function(me) + { + return (mxEvent.isMouseEvent(me.getEvent())) ? 4 : this.graph.getTolerance(); + }; + } + + // One finger pans (no rubberband selection) must start regardless of mouse button + mxPanningHandler.prototype.isPanningTrigger = function(me) + { + var evt = me.getEvent(); + + return (me.getState() == null && !mxEvent.isMouseEvent(evt)) || + (mxEvent.isPopupTrigger(evt) && (me.getState() == null || + mxEvent.isControlDown(evt) || mxEvent.isShiftDown(evt))); + }; + + // Don't clear selection if multiple cells selected + var graphHandlerMouseDown = mxGraphHandler.prototype.mouseDown; + mxGraphHandler.prototype.mouseDown = function(sender, me) + { + graphHandlerMouseDown.apply(this, arguments); + + if (mxEvent.isTouchEvent(me.getEvent()) && this.graph.isCellSelected(me.getCell()) && + this.graph.getSelectionCount() > 1) + { + this.delayedSelection = false; + } + }; + } + else + { + // Removes ctrl+shift as panning trigger for space splitting + mxPanningHandler.prototype.isPanningTrigger = function(me) + { + var evt = me.getEvent(); + + return (mxEvent.isLeftMouseButton(evt) && ((this.useLeftButtonForPanning && + me.getState() == null) || (mxEvent.isControlDown(evt) && + !mxEvent.isShiftDown(evt)))) || (this.usePopupTrigger && + mxEvent.isPopupTrigger(evt)); + }; + } + + // Overrides/extends rubberband for space handling with Ctrl+Shift(+Alt) drag ("scissors tool") + mxRubberband.prototype.isSpaceEvent = function(me) + { + return this.graph.isEnabled() && !this.graph.isCellLocked(this.graph.getDefaultParent()) && + mxEvent.isControlDown(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()); + }; + + // Handles moving of cells in both half panes + mxRubberband.prototype.mouseUp = function(sender, me) + { + var execute = this.div != null && this.div.style.display != 'none'; + + var x0 = null; + var y0 = null; + var dx = null; + var dy = null; + + if (this.first != null && this.currentX != null && this.currentY != null) + { + x0 = this.first.x; + y0 = this.first.y; + dx = (this.currentX - x0) / this.graph.view.scale; + dy = (this.currentY - y0) / this.graph.view.scale; + + if (!mxEvent.isAltDown(me.getEvent())) + { + dx = this.graph.snap(dx); + dy = this.graph.snap(dy); + + if (!this.graph.isGridEnabled()) + { + if (Math.abs(dx) < this.graph.tolerance) + { + dx = 0; + } + + if (Math.abs(dy) < this.graph.tolerance) + { + dy = 0; + } + } + } + } + + this.reset(); + + if (execute) + { + if (mxEvent.isAltDown(me.getEvent()) && this.graph.isToggleEvent(me.getEvent())) + { + var rect = new mxRectangle(this.x, this.y, this.width, this.height); + var cells = this.graph.getCells(rect.x, rect.y, rect.width, rect.height); + + this.graph.removeSelectionCells(cells); + } + else if (this.isSpaceEvent(me)) + { + this.graph.model.beginUpdate(); + try + { + var cells = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), true, true); + + for (var i = 0; i < cells.length; i++) + { + if (this.graph.isCellMovable(cells[i])) + { + var tmp = this.graph.view.getState(cells[i]); + var geo = this.graph.getCellGeometry(cells[i]); + + if (tmp != null && geo != null) + { + geo = geo.clone(); + geo.translate(dx, dy); + this.graph.model.setGeometry(cells[i], geo); + } + } + } + } + finally + { + this.graph.model.endUpdate(); + } + } + else + { + var rect = new mxRectangle(this.x, this.y, this.width, this.height); + this.graph.selectRegion(rect, me.getEvent()); + } + + me.consume(); + } + }; + + // Handles preview for creating/removing space in diagram + mxRubberband.prototype.mouseMove = function(sender, me) + { + if (!me.isConsumed() && this.first != null) + { + var origin = mxUtils.getScrollOrigin(this.graph.container); + var offset = mxUtils.getOffset(this.graph.container); + origin.x -= offset.x; + origin.y -= offset.y; + var x = me.getX() + origin.x; + var y = me.getY() + origin.y; + var dx = this.first.x - x; + var dy = this.first.y - y; + var tol = this.graph.tolerance; + + if (this.div != null || Math.abs(dx) > tol || Math.abs(dy) > tol) + { + if (this.div == null) + { + this.div = this.createShape(); + } + + // Clears selection while rubberbanding. This is required because + // the event is not consumed in mouseDown. + mxUtils.clearSelection(); + this.update(x, y); + + if (this.isSpaceEvent(me)) + { + var right = this.x + this.width; + var bottom = this.y + this.height; + var scale = this.graph.view.scale; + + if (!mxEvent.isAltDown(me.getEvent())) + { + this.width = this.graph.snap(this.width / scale) * scale; + this.height = this.graph.snap(this.height / scale) * scale; + + if (!this.graph.isGridEnabled()) + { + if (this.width < this.graph.tolerance) + { + this.width = 0; + } + + if (this.height < this.graph.tolerance) + { + this.height = 0; + } + } + + if (this.x < this.first.x) + { + this.x = right - this.width; + } + + if (this.y < this.first.y) + { + this.y = bottom - this.height; + } + } + + this.div.style.borderStyle = 'dashed'; + this.div.style.backgroundColor = 'white'; + this.div.style.left = this.x + 'px'; + this.div.style.top = this.y + 'px'; + this.div.style.width = Math.max(0, this.width) + 'px'; + this.div.style.height = this.graph.container.clientHeight + 'px'; + this.div.style.borderWidth = (this.width <= 0) ? '0px 1px 0px 0px' : '0px 1px 0px 1px'; + + if (this.secondDiv == null) + { + this.secondDiv = this.div.cloneNode(true); + this.div.parentNode.appendChild(this.secondDiv); + } + + this.secondDiv.style.left = this.x + 'px'; + this.secondDiv.style.top = this.y + 'px'; + this.secondDiv.style.width = this.graph.container.clientWidth + 'px'; + this.secondDiv.style.height = Math.max(0, this.height) + 'px'; + this.secondDiv.style.borderWidth = (this.height <= 0) ? '1px 0px 0px 0px' : '1px 0px 1px 0px'; + } + else + { + // Hides second div and restores style + this.div.style.backgroundColor = ''; + this.div.style.borderWidth = ''; + this.div.style.borderStyle = ''; + + if (this.secondDiv != null) + { + this.secondDiv.parentNode.removeChild(this.secondDiv); + this.secondDiv = null; + } + } + + me.consume(); + } + } + }; + + // Removes preview + var mxRubberbandReset = mxRubberband.prototype.reset; + mxRubberband.prototype.reset = function() + { + if (this.secondDiv != null) + { + this.secondDiv.parentNode.removeChild(this.secondDiv); + this.secondDiv = null; + } + + mxRubberbandReset.apply(this, arguments); + }; + + // Timer-based activation of outline connect in connection handler + var startTime = new Date().getTime(); + var timeOnTarget = 0; + + var mxEdgeHandlerUpdatePreviewState = mxEdgeHandler.prototype.updatePreviewState; + + mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me) + { + mxEdgeHandlerUpdatePreviewState.apply(this, arguments); + + if (terminalState != this.currentTerminalState) + { + startTime = new Date().getTime(); + timeOnTarget = 0; + } + else + { + timeOnTarget = new Date().getTime() - startTime; + } + + this.currentTerminalState = terminalState; + }; + + // Timer-based outline connect + var mxEdgeHandlerIsOutlineConnectEvent = mxEdgeHandler.prototype.isOutlineConnectEvent; + + mxEdgeHandler.prototype.isOutlineConnectEvent = function(me) + { + return (this.currentTerminalState != null && me.getState() == this.currentTerminalState && timeOnTarget > 2000) || + ((this.currentTerminalState == null || mxUtils.getValue(this.currentTerminalState.style, 'outlineConnect', '1') != '0') && + mxEdgeHandlerIsOutlineConnectEvent.apply(this, arguments)); + }; + + // Disables custom handles if shift is pressed + mxVertexHandler.prototype.isCustomHandleEvent = function(me) + { + return !mxEvent.isShiftDown(me.getEvent()); + }; + + // Shows secondary handle for fixed connection points + mxEdgeHandler.prototype.createHandleShape = function(index, virtual) + { + var source = index != null && index == 0; + var terminalState = this.state.getVisibleTerminalState(source); + var c = (index != null && (index == 0 || index >= this.state.absolutePoints.length - 1 || + (this.constructor == mxElbowEdgeHandler && index == 2))) ? + this.graph.getConnectionConstraint(this.state, terminalState, source) : null; + var pt = (c != null) ? this.graph.getConnectionPoint(this.state.getVisibleTerminalState(source), c) : null; + var img = (pt != null) ? this.fixedHandleImage : ((c != null && terminalState != null) ? + this.terminalHandleImage : this.handleImage); + + if (img != null) + { + var shape = new mxImageShape(new mxRectangle(0, 0, img.width, img.height), img.src); + + // Allows HTML rendering of the images + shape.preserveImageAspect = false; + + return shape; + } + else + { + var s = mxConstants.HANDLE_SIZE; + + if (this.preferHtml) + { + s -= 1; + } + + return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR); + } + }; + + var vertexHandlerCreateSizerShape = mxVertexHandler.prototype.createSizerShape; + mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor) + { + this.handleImage = (index == mxEvent.ROTATION_HANDLE) ? HoverIcons.prototype.rotationHandle : (index == mxEvent.LABEL_HANDLE) ? this.secondaryHandleImage : this.handleImage; + + return vertexHandlerCreateSizerShape.apply(this, arguments); + }; + + // Special case for single edge label handle moving in which case the text bounding box is used + var mxGraphHandlerGetBoundingBox = mxGraphHandler.prototype.getBoundingBox; + mxGraphHandler.prototype.getBoundingBox = function(cells) + { + if (cells != null && cells.length == 1) + { + var model = this.graph.getModel(); + var parent = model.getParent(cells[0]); + var geo = this.graph.getCellGeometry(cells[0]); + + if (model.isEdge(parent) && geo != null && geo.relative) + { + var state = this.graph.view.getState(cells[0]); + + if (state != null && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null) + { + return mxRectangle.fromRectangle(state.text.boundingBox); + } + } + } + + return mxGraphHandlerGetBoundingBox.apply(this, arguments); + }; + + // Uses text bounding box for edge labels + var mxVertexHandlerGetSelectionBounds = mxVertexHandler.prototype.getSelectionBounds; + mxVertexHandler.prototype.getSelectionBounds = function(state) + { + var model = this.graph.getModel(); + var parent = model.getParent(state.cell); + var geo = this.graph.getCellGeometry(state.cell); + + if (model.isEdge(parent) && geo != null && geo.relative && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null) + { + var bbox = state.text.unrotatedBoundingBox || state.text.boundingBox; + + return new mxRectangle(Math.round(bbox.x), Math.round(bbox.y), Math.round(bbox.width), Math.round(bbox.height)); + } + else + { + return mxVertexHandlerGetSelectionBounds.apply(this, arguments); + } + }; + + // Redirects moving of edge labels to mxGraphHandler by not starting here. + // This will use the move preview of mxGraphHandler (see above). + var mxVertexHandlerMouseDown = mxVertexHandler.prototype.mouseDown; + mxVertexHandler.prototype.mouseDown = function(sender, me) + { + var model = this.graph.getModel(); + var parent = model.getParent(this.state.cell); + var geo = this.graph.getCellGeometry(this.state.cell); + + // Lets rotation events through + var handle = this.getHandleForEvent(me); + + if (handle == mxEvent.ROTATION_HANDLE || !model.isEdge(parent) || geo == null || !geo.relative || + this.state == null || this.state.width >= 2 || this.state.height >= 2) + { + mxVertexHandlerMouseDown.apply(this, arguments); + } + }; + + // Shows rotation handle for edge labels. + mxVertexHandler.prototype.isRotationHandleVisible = function() + { + return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) && + (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells); + }; + + // Invokes turn on single click on rotation handle + mxVertexHandler.prototype.rotateClick = function() + { + this.state.view.graph.turnShapes([this.state.cell]); + }; + + var vertexHandlerMouseMove = mxVertexHandler.prototype.mouseMove; + + // Workaround for "isConsumed not defined" in MS Edge is to use arguments + mxVertexHandler.prototype.mouseMove = function(sender, me) + { + vertexHandlerMouseMove.apply(this, arguments); + + if (this.graph.graphHandler.first != null) + { + if (this.rotationShape != null && this.rotationShape.node != null) + { + this.rotationShape.node.style.display = 'none'; + } + } + }; + + var vertexHandlerMouseUp = mxVertexHandler.prototype.mouseUp; + mxVertexHandler.prototype.mouseUp = function(sender, me) + { + vertexHandlerMouseUp.apply(this, arguments); + + // Shows rotation handle only if one vertex is selected + if (this.rotationShape != null && this.rotationShape.node != null) + { + this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; + } + }; + + var vertexHandlerInit = mxVertexHandler.prototype.init; + mxVertexHandler.prototype.init = function() + { + vertexHandlerInit.apply(this, arguments); + var redraw = false; + + if (this.rotationShape != null) + { + this.rotationShape.node.setAttribute('title', mxResources.get('rotateTooltip')); + } + + var update = mxUtils.bind(this, function() + { + // Shows rotation handle only if one vertex is selected + if (this.rotationShape != null && this.rotationShape.node != null) + { + this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; + } + + if (this.specialHandle != null) + { + this.specialHandle.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none'; + } + + this.redrawHandles(); + }); + + this.selectionHandler = mxUtils.bind(this, function(sender, evt) + { + update(); + }); + + this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler); + + this.changeHandler = mxUtils.bind(this, function(sender, evt) + { + this.updateLinkHint(this.graph.getLinkForCell(this.state.cell), + this.graph.getLinksForState(this.state)); + update(); + }); + + this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler); + + // Repaint needed when editing stops and no change event is fired + this.editingHandler = mxUtils.bind(this, function(sender, evt) + { + this.redrawHandles(); + }); + + this.graph.addListener(mxEvent.EDITING_STOPPED, this.editingHandler); + + var link = this.graph.getLinkForCell(this.state.cell); + var links = this.graph.getLinksForState(this.state); + this.updateLinkHint(link, links); + + if (link != null || (links != null && links.length > 0)) + { + redraw = true; + } + + if (redraw) + { + this.redrawHandles(); + } + }; + + mxVertexHandler.prototype.updateLinkHint = function(link, links) + { + if ((link == null && (links == null || links.length == 0)) || + this.graph.getSelectionCount() > 1) + { + if (this.linkHint != null) + { + this.linkHint.parentNode.removeChild(this.linkHint); + this.linkHint = null; + } + } + else if (link != null || (links != null && links.length > 0)) + { + if (this.linkHint == null) + { + this.linkHint = createHint(); + this.linkHint.style.padding = '6px 8px 6px 8px'; + this.linkHint.style.fontSize = '90%'; + this.linkHint.style.opacity = '1'; + this.linkHint.style.filter = ''; + + this.graph.container.appendChild(this.linkHint); + } + + this.linkHint.innerHTML = ''; + + if (link != null) + { + this.linkHint.appendChild(this.graph.createLinkForHint(link)); + + if (this.graph.isEnabled() && typeof this.graph.editLink === 'function') + { + var changeLink = document.createElement('img'); + changeLink.setAttribute('src', Editor.editImage); + changeLink.setAttribute('title', mxResources.get('editLink')); + changeLink.setAttribute('width', '11'); + changeLink.setAttribute('height', '11'); + changeLink.style.marginLeft = '10px'; + changeLink.style.marginBottom = '-1px'; + changeLink.style.cursor = 'pointer'; + this.linkHint.appendChild(changeLink); + + mxEvent.addListener(changeLink, 'click', mxUtils.bind(this, function(evt) + { + this.graph.setSelectionCell(this.state.cell); + this.graph.editLink(); + mxEvent.consume(evt); + })); + + var removeLink = document.createElement('img'); + removeLink.setAttribute('src', Dialog.prototype.clearImage); + removeLink.setAttribute('title', mxResources.get('removeIt', [mxResources.get('link')])); + removeLink.setAttribute('width', '13'); + removeLink.setAttribute('height', '10'); + removeLink.style.marginLeft = '4px'; + removeLink.style.marginBottom = '-1px'; + removeLink.style.cursor = 'pointer'; + this.linkHint.appendChild(removeLink); + + mxEvent.addListener(removeLink, 'click', mxUtils.bind(this, function(evt) + { + this.graph.setLinkForCell(this.state.cell, null); + mxEvent.consume(evt); + })); + } + } + + if (links != null) + { + for (var i = 0; i < links.length; i++) + { + var div = document.createElement('div'); + div.style.marginTop = (link != null || i > 0) ? '6px' : '0px'; + div.appendChild(this.graph.createLinkForHint( + links[i].getAttribute('href'), + mxUtils.getTextContent(links[i]))); + + this.linkHint.appendChild(div); + } + } + } + }; + + mxEdgeHandler.prototype.updateLinkHint = mxVertexHandler.prototype.updateLinkHint; + + var edgeHandlerInit = mxEdgeHandler.prototype.init; + mxEdgeHandler.prototype.init = function() + { + edgeHandlerInit.apply(this, arguments); + + // Disables connection points + this.constraintHandler.isEnabled = mxUtils.bind(this, function() + { + return this.state.view.graph.connectionHandler.isEnabled(); + }); + + var update = mxUtils.bind(this, function() + { + if (this.linkHint != null) + { + this.linkHint.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; + } + + if (this.labelShape != null) + { + this.labelShape.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none'; + } + }); + + this.selectionHandler = mxUtils.bind(this, function(sender, evt) + { + update(); + }); + + this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler); + + this.changeHandler = mxUtils.bind(this, function(sender, evt) + { + this.updateLinkHint(this.graph.getLinkForCell(this.state.cell), + this.graph.getLinksForState(this.state)); + update(); + this.redrawHandles(); + }); + + this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler); + + var link = this.graph.getLinkForCell(this.state.cell); + var links = this.graph.getLinksForState(this.state); + + if (link != null || (links != null && links.length > 0)) + { + this.updateLinkHint(link, links); + this.redrawHandles(); + } + }; + + // Disables connection points + var connectionHandlerInit = mxConnectionHandler.prototype.init; + + mxConnectionHandler.prototype.init = function() + { + connectionHandlerInit.apply(this, arguments); + + this.constraintHandler.isEnabled = mxUtils.bind(this, function() + { + return this.graph.connectionHandler.isEnabled(); + }); + }; + + var vertexHandlerRedrawHandles = mxVertexHandler.prototype.redrawHandles; + mxVertexHandler.prototype.redrawHandles = function() + { + vertexHandlerRedrawHandles.apply(this); + + if (this.state != null && this.linkHint != null) + { + var c = new mxPoint(this.state.getCenterX(), this.state.getCenterY()); + var tmp = new mxRectangle(this.state.x, this.state.y - 22, this.state.width + 24, this.state.height + 22); + var bb = mxUtils.getBoundingBox(tmp, this.state.style[mxConstants.STYLE_ROTATION] || '0', c); + var rs = (bb != null) ? mxUtils.getBoundingBox(this.state, + this.state.style[mxConstants.STYLE_ROTATION] || '0') : this.state; + var tb = (this.state.text != null) ? this.state.text.boundingBox : null; + + if (bb == null) + { + bb = this.state; + } + + var b = bb.y + bb.height; + + if (tb != null) + { + b = Math.max(b, tb.y + tb.height); + } + + this.linkHint.style.left = Math.max(0, Math.round(rs.x + (rs.width - this.linkHint.clientWidth) / 2)) + 'px'; + this.linkHint.style.top = Math.round(b + this.verticalOffset / 2 + 6 + + this.state.view.graph.tolerance) + 'px'; + } + }; + + + var vertexHandlerReset = mxVertexHandler.prototype.reset; + mxVertexHandler.prototype.reset = function() + { + vertexHandlerReset.apply(this, arguments); + + // Shows rotation handle only if one vertex is selected + if (this.rotationShape != null && this.rotationShape.node != null) + { + this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; + } + }; + + var vertexHandlerDestroy = mxVertexHandler.prototype.destroy; + mxVertexHandler.prototype.destroy = function() + { + vertexHandlerDestroy.apply(this, arguments); + + if (this.linkHint != null) + { + this.linkHint.parentNode.removeChild(this.linkHint); + this.linkHint = null; + } + + if (this.selectionHandler != null) + { + this.graph.getSelectionModel().removeListener(this.selectionHandler); + this.selectionHandler = null; + } + + if (this.changeHandler != null) + { + this.graph.getModel().removeListener(this.changeHandler); + this.changeHandler = null; + } + + if (this.editingHandler != null) + { + this.graph.removeListener(this.editingHandler); + this.editingHandler = null; + } + }; + + var edgeHandlerRedrawHandles = mxEdgeHandler.prototype.redrawHandles; + mxEdgeHandler.prototype.redrawHandles = function() + { + // Workaround for special case where handler + // is reset before this which leads to a NPE + if (this.marker != null) + { + edgeHandlerRedrawHandles.apply(this); + + if (this.state != null && this.linkHint != null) + { + var b = this.state; + + if (this.state.text != null && this.state.text.bounds != null) + { + b = new mxRectangle(b.x, b.y, b.width, b.height); + b.add(this.state.text.bounds); + } + + this.linkHint.style.left = Math.max(0, Math.round(b.x + (b.width - this.linkHint.clientWidth) / 2)) + 'px'; + this.linkHint.style.top = Math.round(b.y + b.height + 6 + this.state.view.graph.tolerance) + 'px'; + } + } + }; + + var edgeHandlerReset = mxEdgeHandler.prototype.reset; + mxEdgeHandler.prototype.reset = function() + { + edgeHandlerReset.apply(this, arguments); + + if (this.linkHint != null) + { + this.linkHint.style.visibility = ''; + } + }; + + var edgeHandlerDestroy = mxEdgeHandler.prototype.destroy; + mxEdgeHandler.prototype.destroy = function() + { + edgeHandlerDestroy.apply(this, arguments); + + if (this.linkHint != null) + { + this.linkHint.parentNode.removeChild(this.linkHint); + this.linkHint = null; + } + + if (this.selectionHandler != null) + { + this.graph.getSelectionModel().removeListener(this.selectionHandler); + this.selectionHandler = null; + } + + if (this.changeHandler != null) + { + this.graph.getModel().removeListener(this.changeHandler); + this.changeHandler = null; + } + }; + })(); +} diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Init.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Init.js new file mode 100644 index 00000000..5de5872f --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Init.js @@ -0,0 +1,30 @@ +// urlParams is null when used for embedding +window.urlParams = window.urlParams || {}; + +// Public global variables +window.MAX_REQUEST_SIZE = window.MAX_REQUEST_SIZE || 10485760; +window.MAX_AREA = window.MAX_AREA || 15000 * 15000; + +// URLs for save and export +window.EXPORT_URL = window.EXPORT_URL || '/export'; +window.SAVE_URL = window.SAVE_URL || '/save'; +window.OPEN_URL = window.OPEN_URL || '/open'; +window.RESOURCES_PATH = window.RESOURCES_PATH || 'resources'; +window.RESOURCE_BASE = window.RESOURCE_BASE || window.RESOURCES_PATH + '/grapheditor'; +window.STENCIL_PATH = window.STENCIL_PATH || 'stencils'; +window.IMAGE_PATH = window.IMAGE_PATH || 'images'; +window.STYLE_PATH = window.STYLE_PATH || 'styles'; +window.CSS_PATH = window.CSS_PATH || 'styles'; +window.OPEN_FORM = window.OPEN_FORM || 'open.html'; + +// Sets the base path, the UI language via URL param and configures the +// supported languages to avoid 404s. The loading of all core language +// resources is disabled as all required resources are in grapheditor. +// properties. Note that in this example the loading of two resource +// files (the special bundle and the default bundle) is disabled to +// save a GET request. This requires that all resources be present in +// each properties file since only one file is loaded. + +window.mxBasePath = window.mxBasePath || '../../../src'; +window.mxLanguage = window.mxLanguage || urlParams['lang']; +window.mxLanguages = window.mxLanguages || ['de','zh']; \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Menus.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Menus.js new file mode 100644 index 00000000..9b0ac24e --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Menus.js @@ -0,0 +1,1315 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new graph editor + */ +Menus = function(editorUi) +{ + this.editorUi = editorUi; + this.menus = new Object(); + this.init(); + + // Pre-fetches checkmark image + if (!mxClient.IS_SVG) + { + new Image().src = this.checkmarkImage; + } +}; + +/** + * Sets the default font family. + */ +Menus.prototype.defaultFont = 'Helvetica'; + +/** + * Sets the default font size. + */ +Menus.prototype.defaultFontSize = '12'; + +/** + * Sets the default font size. + */ +Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras', 'help']; + +/** + * Adds the label menu items to the given menu and parent. + */ +Menus.prototype.defaultFonts = ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond', 'Comic Sans MS', + 'Courier New', 'Georgia', 'Lucida Console', 'Tahoma']; + +/** + * Adds the label menu items to the given menu and parent. + */ +Menus.prototype.init = function() +{ + var graph = this.editorUi.editor.graph; + var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled); + + this.customFonts = []; + this.customFontSizes = []; + + this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent) + { + var addItem = mxUtils.bind(this, function(fontname) + { + var tr = this.styleChange(menu, fontname, [mxConstants.STYLE_FONTFAMILY], [fontname], null, parent, function() + { + document.execCommand('fontname', false, fontname); + }, function() + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.removeAttribute('face'); + elt.style.fontFamily = null; + + if (elt.nodeName == 'PRE') + { + graph.replaceElement(elt, 'div'); + } + }); + }); + tr.firstChild.nextSibling.style.fontFamily = fontname; + }); + + for (var i = 0; i < this.defaultFonts.length; i++) + { + addItem(this.defaultFonts[i]); + } + + menu.addSeparator(parent); + + if (this.customFonts.length > 0) + { + for (var i = 0; i < this.customFonts.length; i++) + { + addItem(this.customFonts[i]); + } + + menu.addSeparator(parent); + + menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() + { + this.customFonts = []; + }), parent); + + menu.addSeparator(parent); + } + + this.promptChange(menu, mxResources.get('custom') + '...', '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent, true, mxUtils.bind(this, function(newValue) + { + this.customFonts.push(newValue); + })); + }))); + this.put('formatBlock', new Menu(mxUtils.bind(this, function(menu, parent) + { + function addItem(label, tag) + { + return menu.addItem(label, null, mxUtils.bind(this, function() + { + // TODO: Check if visible + graph.cellEditor.textarea.focus(); + document.execCommand('formatBlock', false, '<' + tag + '>'); + }), parent); + }; + + addItem(mxResources.get('normal'), 'p'); + + addItem('', 'h1').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 1

'; + addItem('', 'h2').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 2

'; + addItem('', 'h3').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 3

'; + addItem('', 'h4').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 4

'; + addItem('', 'h5').firstChild.nextSibling.innerHTML = '
' + mxResources.get('heading') + ' 5
'; + addItem('', 'h6').firstChild.nextSibling.innerHTML = '
' + mxResources.get('heading') + ' 6
'; + + addItem('', 'pre').firstChild.nextSibling.innerHTML = '
' + mxResources.get('formatted') + '
'; + addItem('', 'blockquote').firstChild.nextSibling.innerHTML = '
' + mxResources.get('blockquote') + '
'; + }))); + this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent) + { + var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72]; + + var addItem = mxUtils.bind(this, function(fontsize) + { + this.styleChange(menu, fontsize, [mxConstants.STYLE_FONTSIZE], [fontsize], null, parent, function() + { + // Creates an element with arbitrary size 3 + document.execCommand('fontSize', false, '3'); + + // Changes the css font size of the first font element inside the in-place editor with size 3 + // hopefully the above element that we've just created. LATER: Check for new element using + // previous result of getElementsByTagName (see other actions) + var elts = graph.cellEditor.textarea.getElementsByTagName('font'); + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].getAttribute('size') == '3') + { + elts[i].removeAttribute('size'); + elts[i].style.fontSize = fontsize + 'px'; + + break; + } + } + }); + }); + + for (var i = 0; i < sizes.length; i++) + { + addItem(sizes[i]); + } + + menu.addSeparator(parent); + + if (this.customFontSizes.length > 0) + { + for (var i = 0; i < this.customFontSizes.length; i++) + { + addItem(this.customFontSizes[i]); + } + + menu.addSeparator(parent); + + menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() + { + this.customFontSizes = []; + }), parent); + + menu.addSeparator(parent); + } + + this.promptChange(menu, mxResources.get('custom') + '...', '(pt)', '12', mxConstants.STYLE_FONTSIZE, parent, true, mxUtils.bind(this, function(newValue) + { + this.customFontSizes.push(newValue); + })); + }))); + this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent) + { + menu.addItem(mxResources.get('flipH'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); }, parent); + menu.addItem(mxResources.get('flipV'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); }, parent); + this.addMenuItems(menu, ['-', 'rotation'], parent); + }))); + this.put('align', new Menu(mxUtils.bind(this, function(menu, parent) + { + menu.addItem(mxResources.get('leftAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, parent); + menu.addItem(mxResources.get('center'), null, function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, parent); + menu.addItem(mxResources.get('rightAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, parent); + menu.addSeparator(parent); + menu.addItem(mxResources.get('topAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_TOP); }, parent); + menu.addItem(mxResources.get('middle'), null, function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, parent); + menu.addItem(mxResources.get('bottomAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, parent); + }))); + this.put('distribute', new Menu(mxUtils.bind(this, function(menu, parent) + { + menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true); }, parent); + menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false); }, parent); + }))); + this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent) + { + var promptSpacing = mxUtils.bind(this, function(defaultValue, fn) + { + var dlg = new FilenameDialog(this.editorUi, defaultValue, mxResources.get('apply'), function(newValue) + { + fn(parseFloat(newValue)); + }, mxResources.get('spacing')); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }); + + menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function() + { + var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST); + + this.editorUi.executeLayout(function() + { + var selectionCells = graph.getSelectionCells(); + layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells); + }, true); + }), parent); + menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function() + { + var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH); + + this.editorUi.executeLayout(function() + { + var selectionCells = graph.getSelectionCells(); + layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells); + }, true); + }), parent); + menu.addSeparator(parent); + menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function() + { + var tmp = graph.getSelectionCell(); + var roots = null; + + if (tmp == null || graph.getModel().getChildCount(tmp) == 0) + { + if (graph.getModel().getEdgeCount(tmp) == 0) + { + roots = graph.findTreeRoots(graph.getDefaultParent()); + } + } + else + { + roots = graph.findTreeRoots(tmp); + } + + if (roots != null && roots.length > 0) + { + tmp = roots[0]; + } + + if (tmp != null) + { + var layout = new mxCompactTreeLayout(graph, true); + layout.edgeRouting = false; + layout.levelDistance = 30; + + promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) + { + layout.levelDistance = newValue; + + this.editorUi.executeLayout(function() + { + layout.execute(graph.getDefaultParent(), tmp); + }, true); + })); + } + }), parent); + menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function() + { + var tmp = graph.getSelectionCell(); + var roots = null; + + if (tmp == null || graph.getModel().getChildCount(tmp) == 0) + { + if (graph.getModel().getEdgeCount(tmp) == 0) + { + roots = graph.findTreeRoots(graph.getDefaultParent()); + } + } + else + { + roots = graph.findTreeRoots(tmp); + } + + if (roots != null && roots.length > 0) + { + tmp = roots[0]; + } + + if (tmp != null) + { + var layout = new mxCompactTreeLayout(graph, false); + layout.edgeRouting = false; + layout.levelDistance = 30; + + promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) + { + layout.levelDistance = newValue; + + this.editorUi.executeLayout(function() + { + layout.execute(graph.getDefaultParent(), tmp); + }, true); + })); + } + }), parent); + menu.addItem(mxResources.get('radialTree'), null, mxUtils.bind(this, function() + { + var tmp = graph.getSelectionCell(); + var roots = null; + + if (tmp == null || graph.getModel().getChildCount(tmp) == 0) + { + if (graph.getModel().getEdgeCount(tmp) == 0) + { + roots = graph.findTreeRoots(graph.getDefaultParent()); + } + } + else + { + roots = graph.findTreeRoots(tmp); + } + + if (roots != null && roots.length > 0) + { + tmp = roots[0]; + } + + if (tmp != null) + { + var layout = new mxRadialTreeLayout(graph, false); + layout.levelDistance = 80; + layout.autoRadius = true; + + promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) + { + layout.levelDistance = newValue; + + this.editorUi.executeLayout(function() + { + layout.execute(graph.getDefaultParent(), tmp); + + if (!graph.isSelectionEmpty()) + { + tmp = graph.getModel().getParent(tmp); + + if (graph.getModel().isVertex(tmp)) + { + graph.updateGroupBounds([tmp], graph.gridSize * 2, true); + } + } + }, true); + })); + } + }), parent); + menu.addSeparator(parent); + menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function() + { + var layout = new mxFastOrganicLayout(graph); + + promptSpacing(layout.forceConstant, mxUtils.bind(this, function(newValue) + { + layout.forceConstant = newValue; + + this.editorUi.executeLayout(function() + { + var tmp = graph.getSelectionCell(); + + if (tmp == null || graph.getModel().getChildCount(tmp) == 0) + { + tmp = graph.getDefaultParent(); + } + + layout.execute(tmp); + + if (graph.getModel().isVertex(tmp)) + { + graph.updateGroupBounds([tmp], graph.gridSize * 2, true); + } + }, true); + })); + }), parent); + menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function() + { + var layout = new mxCircleLayout(graph); + + this.editorUi.executeLayout(function() + { + var tmp = graph.getSelectionCell(); + + if (tmp == null || graph.getModel().getChildCount(tmp) == 0) + { + tmp = graph.getDefaultParent(); + } + + layout.execute(tmp); + + if (graph.getModel().isVertex(tmp)) + { + graph.updateGroupBounds([tmp], graph.gridSize * 2, true); + } + }, true); + }), parent); + }))); + this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse', '-', 'collapsible'], parent); + }))); + this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['toFront', 'toBack', '-'], parent); + this.addSubmenu('direction', menu, parent); + this.addMenuItems(menu, ['turn', '-'], parent); + this.addSubmenu('align', menu, parent); + this.addSubmenu('distribute', menu, parent); + menu.addSeparator(parent); + this.addSubmenu('navigation', menu, parent); + this.addSubmenu('insert', menu, parent); + this.addSubmenu('layout', menu, parent); + this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'clearWaypoints', 'autosize'], parent); + }))).isEnabled = isGraphEnabled; + this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['insertLink', 'insertImage'], parent); + }))); + this.put('view', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []). + concat(['outline', 'layers', '-', 'pageView', 'pageScale', '-', 'scrollbars', 'tooltips', '-', + 'grid', 'guides', '-', 'connectionArrows', 'connectionPoints', '-', + 'resetView', 'zoomIn', 'zoomOut'], parent)); + }))); + // Two special dropdowns that are only used in the toolbar + this.put('viewPanels', new Menu(mxUtils.bind(this, function(menu, parent) + { + if (this.editorUi.format != null) + { + this.addMenuItems(menu, ['formatPanel'], parent); + } + + this.addMenuItems(menu, ['outline', 'layers'], parent); + }))); + this.put('viewZoom', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['resetView', '-'], parent); + var scales = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4]; + + for (var i = 0; i < scales.length; i++) + { + (function(scale) + { + menu.addItem((scale * 100) + '%', null, function() + { + graph.zoomTo(scale); + }, parent); + })(scales[i]); + } + + this.addMenuItems(menu, ['-', 'fitWindow', 'fitPageWidth', 'fitPage', 'fitTwoPages', '-', 'customZoom'], parent); + }))); + this.put('file', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', 'export', '-', 'pageSetup', 'print'], parent); + }))); + this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'delete', '-', 'duplicate', '-', + 'editData', 'editTooltip', 'editStyle', '-', 'edit', '-', 'editLink', 'openLink', '-', + 'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']); + }))); + this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'editDiagram']); + }))); + this.put('help', new Menu(mxUtils.bind(this, function(menu, parent) + { + this.addMenuItems(menu, ['help', '-', 'about']); + }))); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Menus.prototype.put = function(name, menu) +{ + this.menus[name] = menu; + + return menu; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Menus.prototype.get = function(name) +{ + return this.menus[name]; +}; + +/** + * Adds the given submenu. + */ +Menus.prototype.addSubmenu = function(name, menu, parent, label) +{ + var entry = this.get(name); + + if (entry != null) + { + var enabled = entry.isEnabled(); + + if (menu.showDisabled || enabled) + { + var submenu = menu.addItem(label || mxResources.get(name), null, null, parent, null, enabled); + this.addMenu(name, menu, submenu); + } + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Menus.prototype.addMenu = function(name, popupMenu, parent) +{ + var menu = this.get(name); + + if (menu != null && (popupMenu.showDisabled || menu.isEnabled())) + { + this.get(name).execute(popupMenu, parent); + } +}; + +/** + * Adds a menu item to insert a table. + */ +Menus.prototype.addInsertTableItem = function(menu) +{ + // KNOWN: Does not work in IE8 standards and quirks + var graph = this.editorUi.editor.graph; + + function createTable(rows, cols) + { + var html = ['']; + + for (var i = 0; i < rows; i++) + { + html.push(''); + + for (var j = 0; j < cols; j++) + { + html.push(''); + } + + html.push(''); + } + + html.push('

'); + + return html.join(''); + }; + + // Show table size dialog + var elt2 = menu.addItem('', null, mxUtils.bind(this, function(evt) + { + var td = graph.getParentByName(mxEvent.getSource(evt), 'TD'); + + if (td != null) + { + var row2 = graph.getParentByName(td, 'TR'); + + // To find the new link, we create a list of all existing links first + // LATER: Refactor for reuse with code for finding inserted image below + var tmp = graph.cellEditor.textarea.getElementsByTagName('table'); + var oldTables = []; + + for (var i = 0; i < tmp.length; i++) + { + oldTables.push(tmp[i]); + } + + // Finding the new table will work with insertHTML, but IE does not support that + graph.container.focus(); + graph.pasteHtmlAtCaret(createTable(row2.sectionRowIndex + 1, td.cellIndex + 1)); + + // Moves cursor to first table cell + var newTables = graph.cellEditor.textarea.getElementsByTagName('table'); + + if (newTables.length == oldTables.length + 1) + { + // Inverse order in favor of appended tables + for (var i = newTables.length - 1; i >= 0; i--) + { + if (i == 0 || newTables[i] != oldTables[i - 1]) + { + graph.selectNode(newTables[i].rows[0].cells[0]); + break; + } + } + } + } + })); + + // Quirks mode does not add cell padding if cell is empty, needs good old spacer solution + var quirksCellHtml = ''; + + function createPicker(rows, cols) + { + var table2 = document.createElement('table'); + table2.setAttribute('border', '1'); + table2.style.borderCollapse = 'collapse'; + + if (!mxClient.IS_QUIRKS) + { + table2.setAttribute('cellPadding', '8'); + } + + for (var i = 0; i < rows; i++) + { + var row = table2.insertRow(i); + + for (var j = 0; j < cols; j++) + { + var cell = row.insertCell(-1); + + if (mxClient.IS_QUIRKS) + { + cell.innerHTML = quirksCellHtml; + } + } + } + + return table2; + }; + + function extendPicker(picker, rows, cols) + { + for (var i = picker.rows.length; i < rows; i++) + { + var row = picker.insertRow(i); + + for (var j = 0; j < picker.rows[0].cells.length; j++) + { + var cell = row.insertCell(-1); + + if (mxClient.IS_QUIRKS) + { + cell.innerHTML = quirksCellHtml; + } + } + } + + for (var i = 0; i < picker.rows.length; i++) + { + var row = picker.rows[i]; + + for (var j = row.cells.length; j < cols; j++) + { + var cell = row.insertCell(-1); + + if (mxClient.IS_QUIRKS) + { + cell.innerHTML = quirksCellHtml; + } + } + } + }; + + elt2.firstChild.innerHTML = ''; + var picker = createPicker(5, 5); + elt2.firstChild.appendChild(picker); + + var label = document.createElement('div'); + label.style.padding = '4px'; + label.style.fontSize = Menus.prototype.defaultFontSize + 'px'; + label.innerHTML = '1x1'; + elt2.firstChild.appendChild(label); + + mxEvent.addListener(picker, 'mouseover', function(e) + { + var td = graph.getParentByName(mxEvent.getSource(e), 'TD'); + + if (td != null) + { + var row2 = graph.getParentByName(td, 'TR'); + extendPicker(picker, Math.min(20, row2.sectionRowIndex + 2), Math.min(20, td.cellIndex + 2)); + label.innerHTML = (td.cellIndex + 1) + 'x' + (row2.sectionRowIndex + 1); + + for (var i = 0; i < picker.rows.length; i++) + { + var r = picker.rows[i]; + + for (var j = 0; j < r.cells.length; j++) + { + var cell = r.cells[j]; + + if (i <= row2.sectionRowIndex && j <= td.cellIndex) + { + cell.style.backgroundColor = 'blue'; + } + else + { + cell.style.backgroundColor = 'white'; + } + } + } + + mxEvent.consume(e); + } + }); +}; + +/** + * Adds a style change item to the given menu. + */ +Menus.prototype.edgeStyleChange = function(menu, label, keys, values, sprite, parent, reset) +{ + return menu.addItem(label, null, mxUtils.bind(this, function() + { + var graph = this.editorUi.editor.graph; + graph.stopEditing(false); + + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + var edges = []; + + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + if (reset) + { + var geo = graph.getCellGeometry(cell); + + // Resets all edge points + if (geo != null) + { + geo = geo.clone(); + geo.points = null; + graph.getModel().setGeometry(cell, geo); + } + } + + for (var j = 0; j < keys.length; j++) + { + graph.setCellStyles(keys[j], values[j], [cell]); + } + + edges.push(cell); + } + } + + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys, + 'values', values, 'cells', edges)); + } + finally + { + graph.getModel().endUpdate(); + } + }), parent, sprite); +}; + +/** + * Adds a style change item to the given menu. + */ +Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent, fn, post) +{ + var apply = this.createStyleChangeFunction(keys, values); + + return menu.addItem(label, null, mxUtils.bind(this, function() + { + var graph = this.editorUi.editor.graph; + + if (fn != null && graph.cellEditor.isContentEditing()) + { + fn(); + } + else + { + apply(post); + } + }), parent, sprite); +}; + +/** + * + */ +Menus.prototype.createStyleChangeFunction = function(keys, values) +{ + return mxUtils.bind(this, function(post) + { + var graph = this.editorUi.editor.graph; + graph.stopEditing(false); + + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < keys.length; i++) + { + graph.setCellStyles(keys[i], values[i]); + } + + if (post != null) + { + post(); + } + + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values, + 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); +}; + +/** + * Adds a style change item with a prompt to the given menu. + */ +Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled, fn, sprite) +{ + return menu.addItem(label, null, mxUtils.bind(this, function() + { + var graph = this.editorUi.editor.graph; + var value = defaultValue; + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + value = state.style[key] || value; + } + + var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + if (newValue != null && newValue.length > 0) + { + graph.getModel().beginUpdate(); + try + { + graph.stopEditing(false); + graph.setCellStyles(key, newValue); + } + finally + { + graph.getModel().endUpdate(); + } + + if (fn != null) + { + fn(newValue); + } + } + }), mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : '')); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }), parent, sprite, enabled); +}; + +/** + * Adds a handler for showing a menu in the given element. + */ +Menus.prototype.pickColor = function(key, cmd, defaultValue) +{ + var graph = this.editorUi.editor.graph; + + if (cmd != null && graph.cellEditor.isContentEditing()) + { + // Saves and restores text selection for in-place editor + var selState = graph.cellEditor.saveSelection(); + + var dlg = new ColorDialog(this.editorUi, defaultValue || '000000', mxUtils.bind(this, function(color) + { + graph.cellEditor.restoreSelection(selState); + document.execCommand(cmd, false, (color != mxConstants.NONE) ? color : 'transparent'); + }), function() + { + graph.cellEditor.restoreSelection(selState); + }); + this.editorUi.showDialog(dlg.container, 230, 430, true, true); + dlg.init(); + } + else + { + if (this.colorDialog == null) + { + this.colorDialog = new ColorDialog(this.editorUi); + } + + this.colorDialog.currentColorKey = key; + var state = graph.getView().getState(graph.getSelectionCell()); + var color = 'none'; + + if (state != null) + { + color = state.style[key] || color; + } + + if (color == 'none') + { + color = 'ffffff'; + this.colorDialog.picker.fromString('ffffff'); + this.colorDialog.colorInput.value = 'none'; + } + else + { + this.colorDialog.picker.fromString(color); + } + + this.editorUi.showDialog(this.colorDialog.container, 230, 430, true, true); + this.colorDialog.init(); + } +}; + +/** + * Adds a handler for showing a menu in the given element. + */ +Menus.prototype.toggleStyle = function(key, defaultValue) +{ + var graph = this.editorUi.editor.graph; + var value = graph.toggleCellStyles(key, defaultValue); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], 'values', [value], + 'cells', graph.getSelectionCells())); +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label) +{ + var action = this.editorUi.actions.get(key); + + if (action != null && (menu.showDisabled || action.isEnabled()) && action.visible) + { + var item = menu.addItem(label || action.label, null, function() + { + action.funct(trigger); + }, parent, sprite, action.isEnabled()); + + // Adds checkmark image + if (action.toggleAction && action.isSelected()) + { + menu.addCheckmark(item, Editor.checkmarkImage); + } + + this.addShortcut(item, action); + + return item; + } + + return null; +}; + +/** + * Adds a checkmark to the given menuitem. + */ +Menus.prototype.addShortcut = function(item, action) +{ + if (action.shortcut != null) + { + var td = item.firstChild.nextSibling.nextSibling; + var span = document.createElement('span'); + span.style.color = 'gray'; + mxUtils.write(span, action.shortcut); + td.appendChild(span); + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menus.prototype.addMenuItems = function(menu, keys, parent, trigger, sprites) +{ + for (var i = 0; i < keys.length; i++) + { + if (keys[i] == '-') + { + menu.addSeparator(parent); + } + else + { + this.addMenuItem(menu, keys[i], parent, trigger, (sprites != null) ? sprites[i] : null); + } + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menus.prototype.createPopupMenu = function(menu, cell, evt) +{ + var graph = this.editorUi.editor.graph; + menu.smartSeparators = true; + + if (graph.isSelectionEmpty()) + { + this.addMenuItems(menu, ['undo', 'redo', 'pasteHere'], null, evt); + } + else + { + this.addMenuItems(menu, ['delete', '-', 'cut', 'copy', '-', 'duplicate'], null, evt); + } + + if (!graph.isSelectionEmpty()) + { + if (graph.getSelectionCount() == 1) + { + this.addMenuItems(menu, ['setAsDefaultStyle'], null, evt); + } + + menu.addSeparator(); + + cell = graph.getSelectionCell(); + var state = graph.view.getState(cell); + + if (state != null) + { + var hasWaypoints = false; + this.addMenuItems(menu, ['toFront', 'toBack', '-'], null, evt); + + if (graph.getModel().isEdge(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) != 'entityRelationEdgeStyle' && + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) != 'arrow') + { + var handler = graph.selectionCellsHandler.getHandler(cell); + var isWaypoint = false; + + if (handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2) + { + var index = handler.getHandleForEvent(graph.updateMouseEvent(new mxMouseEvent(evt))); + + // Configures removeWaypoint action before execution + // Using trigger parameter is cleaner but have to find waypoint here anyway. + var rmWaypointAction = this.editorUi.actions.get('removeWaypoint'); + rmWaypointAction.handler = handler; + rmWaypointAction.index = index; + + isWaypoint = index > 0 && index < handler.bends.length - 1; + } + + menu.addSeparator(); + this.addMenuItem(menu, 'turn', null, evt, null, mxResources.get('reverse')); + this.addMenuItems(menu, [(isWaypoint) ? 'removeWaypoint' : 'addWaypoint'], null, evt); + + // Adds reset waypoints option if waypoints exist + var geo = graph.getModel().getGeometry(cell); + hasWaypoints = geo != null && geo.points != null && geo.points.length > 0; + } + + if (graph.getSelectionCount() == 1 && (hasWaypoints || (graph.getModel().isVertex(cell) && + graph.getModel().getEdgeCount(cell) > 0))) + { + this.addMenuItems(menu, ['clearWaypoints'], null, evt); + } + + if (graph.getSelectionCount() > 1) + { + menu.addSeparator(); + this.addMenuItems(menu, ['group'], null, evt); + } + else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && + graph.getModel().getChildCount(cell) > 0) + { + menu.addSeparator(); + this.addMenuItems(menu, ['ungroup'], null, evt); + } + + if (graph.getSelectionCount() == 1) + { + menu.addSeparator(); + this.addMenuItems(menu, ['editData', 'editLink'], null, evt); + + // Shows edit image action if there is an image in the style + if (graph.getModel().isVertex(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_IMAGE, null) != null) + { + menu.addSeparator(); + this.addMenuItem(menu, 'image', null, evt).firstChild.nextSibling.innerHTML = mxResources.get('editImage') + '...'; + } + } + } + } + else + { + this.addMenuItems(menu, ['-', 'selectVertices', 'selectEdges', + 'selectAll', '-', 'clearDefaultStyle'], null, evt); + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menus.prototype.createMenubar = function(container) +{ + var menubar = new Menubar(this.editorUi, container); + var menus = this.defaultMenuItems; + + for (var i = 0; i < menus.length; i++) + { + (mxUtils.bind(this, function(menu) + { + var elt = menubar.addMenu(mxResources.get(menus[i]), mxUtils.bind(this, function() + { + // Allows extensions of menu.funct + menu.funct.apply(this, arguments); + })); + + this.menuCreated(menu, elt); + }))(this.get(menus[i])); + } + + return menubar; +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menus.prototype.menuCreated = function(menu, elt, className) +{ + if (elt != null) + { + className = (className != null) ? className : 'geItem'; + + menu.addListener('stateChanged', function() + { + elt.enabled = menu.enabled; + + if (!menu.enabled) + { + elt.className = className + ' mxDisabled'; + + if (document.documentMode == 8) + { + elt.style.color = '#c3c3c3'; + } + } + else + { + elt.className = className; + + if (document.documentMode == 8) + { + elt.style.color = ''; + } + } + }); + } +}; + +/** + * Construcs a new menubar for the given editor. + */ +function Menubar(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; +}; + +/** + * Adds the menubar elements. + */ +Menubar.prototype.hideMenu = function() +{ + this.editorUi.hideCurrentMenu(); +}; + +/** + * Adds a submenu to this menubar. + */ +Menubar.prototype.addMenu = function(label, funct, before) +{ + var elt = document.createElement('a'); + elt.setAttribute('href', 'javascript:void(0);'); + elt.className = 'geItem'; + mxUtils.write(elt, label); + this.addMenuHandler(elt, funct); + + if (before != null) + { + this.container.insertBefore(elt, before); + } + else + { + this.container.appendChild(elt); + } + + return elt; +}; + +/** + * Adds a handler for showing a menu in the given element. + */ +Menubar.prototype.addMenuHandler = function(elt, funct) +{ + if (funct != null) + { + var show = true; + + var clickHandler = mxUtils.bind(this, function(evt) + { + if (show && elt.enabled == null || elt.enabled) + { + this.editorUi.editor.graph.popupMenuHandler.hideMenu(); + var menu = new mxPopupMenu(funct); + menu.div.className += ' geMenubarMenu'; + menu.smartSeparators = true; + menu.showDisabled = true; + menu.autoExpand = true; + + // Disables autoexpand and destroys menu when hidden + menu.hideMenu = mxUtils.bind(this, function() + { + mxPopupMenu.prototype.hideMenu.apply(menu, arguments); + this.editorUi.resetCurrentMenu(); + menu.destroy(); + }); + + var offset = mxUtils.getOffset(elt); + menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt); + this.editorUi.setCurrentMenu(menu, elt); + } + + mxEvent.consume(evt); + }); + + // Shows menu automatically while in expanded state + mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt) + { + if (this.editorUi.currentMenu != null && this.editorUi.currentMenuElt != elt) + { + this.editorUi.hideCurrentMenu(); + clickHandler(evt); + } + })); + + // Hides menu if already showing + mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function() + { + show = this.currentElt != elt; + })); + + mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) + { + clickHandler(evt); + show = true; + })); + } +}; + +/** + * Creates the keyboard event handler for the current graph and history. + */ +Menubar.prototype.destroy = function() +{ + // do nothing +}; + +/** + * Constructs a new action for the given parameters. + */ +function Menu(funct, enabled) +{ + mxEventSource.call(this); + this.funct = funct; + this.enabled = (enabled != null) ? enabled : true; +}; + +// Menu inherits from mxEventSource +mxUtils.extend(Menu, mxEventSource); + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Menu.prototype.isEnabled = function() +{ + return this.enabled; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Menu.prototype.setEnabled = function(value) +{ + if (this.enabled != value) + { + this.enabled = value; + this.fireEvent(new mxEventObject('stateChanged')); + } +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Menu.prototype.execute = function(menu, parent) +{ + this.funct(menu, parent); +}; + +/** + * "Installs" menus in EditorUi. + */ +EditorUi.prototype.createMenus = function() +{ + return new Menus(this); +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Shapes.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Shapes.js new file mode 100644 index 00000000..03706891 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Shapes.js @@ -0,0 +1,3879 @@ +/** + * Copyright (c) 2006-2015, JGraph Ltd + */ + +/** + * Registers shapes. + */ +(function() +{ + // Cube Shape, supports size style + function CubeShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(CubeShape, mxCylinder); + CubeShape.prototype.size = 20; + CubeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); + + if (isForeground) + { + path.moveTo(s, h); + path.lineTo(s, s); + path.lineTo(0, 0); + path.moveTo(s, s); + path.lineTo(w, s); + path.end(); + } + else + { + path.moveTo(0, 0); + path.lineTo(w - s, 0); + path.lineTo(w, s); + path.lineTo(w, h); + path.lineTo(s, h); + path.lineTo(0, h - s); + path.lineTo(0, 0); + path.close(); + path.end(); + } + }; + CubeShape.prototype.getLabelMargins = function(rect) + { + if (mxUtils.getValue(this.style, 'boundedLbl', false)) + { + var s = parseFloat(mxUtils.getValue(this.style, 'size', this.size)) * this.scale; + + return new mxRectangle(s, s, 0, 0); + } + + return null; + }; + + mxCellRenderer.registerShape('cube', CubeShape); + + var tan30 = Math.tan(mxUtils.toRadians(30)); + var tan30Dx = (0.5 - tan30) / 2; + + // Cube Shape, supports size style + function IsoRectangleShape() + { + mxActor.call(this); + }; + mxUtils.extend(IsoRectangleShape, mxActor); + IsoRectangleShape.prototype.size = 20; + IsoRectangleShape.prototype.redrawPath = function(path, x, y, w, h) + { + var m = Math.min(w, h / tan30); + + path.translate((w - m) / 2, (h - m) / 2 + m / 4); + path.moveTo(0, 0.25 * m); + path.lineTo(0.5 * m, m * tan30Dx); + path.lineTo(m, 0.25 * m); + path.lineTo(0.5 * m, (0.5 - tan30Dx) * m); + path.lineTo(0, 0.25 * m); + path.close(); + path.end(); + }; + + mxCellRenderer.registerShape('isoRectangle', IsoRectangleShape); + + // Cube Shape, supports size style + function IsoCubeShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(IsoCubeShape, mxCylinder); + IsoCubeShape.prototype.size = 20; + IsoCubeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + var m = Math.min(w, h / (0.5 + tan30)); + + if (isForeground) + { + path.moveTo(0, 0.25 * m); + path.lineTo(0.5 * m, (0.5 - tan30Dx) * m); + path.lineTo(m, 0.25 * m); + path.moveTo(0.5 * m, (0.5 - tan30Dx) * m); + path.lineTo(0.5 * m, (1 - tan30Dx) * m); + path.end(); + } + else + { + path.translate((w - m) / 2, (h - m) / 2); + path.moveTo(0, 0.25 * m); + path.lineTo(0.5 * m, m * tan30Dx); + path.lineTo(m, 0.25 * m); + path.lineTo(m, 0.75 * m); + path.lineTo(0.5 * m, (1 - tan30Dx) * m); + path.lineTo(0, 0.75 * m); + path.close(); + path.end(); + } + }; + + mxCellRenderer.registerShape('isoCube', IsoCubeShape); + + // DataStore Shape, supports size style + function DataStoreShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(DataStoreShape, mxCylinder); + + DataStoreShape.prototype.redrawPath = function(c, x, y, w, h, isForeground) + { + var dy = Math.min(h / 2, Math.round(h / 8) + this.strokewidth - 1); + + if ((isForeground && this.fill != null) || (!isForeground && this.fill == null)) + { + c.moveTo(0, dy); + c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); + + // Needs separate shapes for correct hit-detection + if (!isForeground) + { + c.stroke(); + c.begin(); + } + + c.translate(0, dy / 2); + c.moveTo(0, dy); + c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); + + // Needs separate shapes for correct hit-detection + if (!isForeground) + { + c.stroke(); + c.begin(); + } + + c.translate(0, dy / 2); + c.moveTo(0, dy); + c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); + + // Needs separate shapes for correct hit-detection + if (!isForeground) + { + c.stroke(); + c.begin(); + } + + c.translate(0, -dy); + } + + if (!isForeground) + { + c.moveTo(0, dy); + c.curveTo(0, -dy / 3, w, -dy / 3, w, dy); + c.lineTo(w, h - dy); + c.curveTo(w, h + dy / 3, 0, h + dy / 3, 0, h - dy); + c.close(); + } + }; + DataStoreShape.prototype.getLabelMargins = function(rect) + { + return new mxRectangle(0, 2.5 * Math.min(rect.height / 2, Math.round(rect.height / 8) + + this.strokewidth - 1) * this.scale, 0, 0); + } + + mxCellRenderer.registerShape('datastore', DataStoreShape); + + // Note Shape, supports size style + function NoteShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(NoteShape, mxCylinder); + NoteShape.prototype.size = 30; + NoteShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); + + if (isForeground) + { + path.moveTo(w - s, 0); + path.lineTo(w - s, s); + path.lineTo(w, s); + path.end(); + } + else + { + path.moveTo(0, 0); + path.lineTo(w - s, 0); + path.lineTo(w, s); + path.lineTo(w, h); + path.lineTo(0, h); + path.lineTo(0, 0); + path.close(); + path.end(); + } + }; + + mxCellRenderer.registerShape('note', NoteShape); + + // Note Shape, supports size style + function SwitchShape() + { + mxActor.call(this); + }; + mxUtils.extend(SwitchShape, mxActor); + SwitchShape.prototype.redrawPath = function(c, x, y, w, h) + { + var curve = 0.5; + c.moveTo(0, 0); + c.quadTo(w / 2, h * curve, w, 0); + c.quadTo(w * (1 - curve), h / 2, w, h); + c.quadTo(w / 2, h * (1 - curve), 0, h); + c.quadTo(w * curve, h / 2, 0, 0); + c.end(); + }; + + mxCellRenderer.registerShape('switch', SwitchShape); + + // Folder Shape, supports tabWidth, tabHeight styles + function FolderShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(FolderShape, mxCylinder); + FolderShape.prototype.tabWidth = 60; + FolderShape.prototype.tabHeight = 20; + FolderShape.prototype.tabPosition = 'right'; + FolderShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'tabWidth', this.tabWidth)))); + var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'tabHeight', this.tabHeight)))); + var tp = mxUtils.getValue(this.style, 'tabPosition', this.tabPosition); + + if (isForeground) + { + if (tp == 'left') + { + path.moveTo(0, dy); + path.lineTo(dx, dy); + } + // Right is default + else + { + path.moveTo(w - dx, dy); + path.lineTo(w, dy); + } + + path.end(); + } + else + { + if (tp == 'left') + { + path.moveTo(0, 0); + path.lineTo(dx, 0); + path.lineTo(dx, dy); + path.lineTo(w, dy); + } + // Right is default + else + { + path.moveTo(0, dy); + path.lineTo(w - dx, dy); + path.lineTo(w - dx, 0); + path.lineTo(w, 0); + } + + path.lineTo(w, h); + path.lineTo(0, h); + path.lineTo(0, dy); + path.close(); + path.end(); + } + }; + + mxCellRenderer.registerShape('folder', FolderShape); + + // Card shape + function CardShape() + { + mxActor.call(this); + }; + mxUtils.extend(CardShape, mxActor); + CardShape.prototype.size = 30; + CardShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w, 0), new mxPoint(w, h), new mxPoint(0, h), new mxPoint(0, s)], + this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('card', CardShape); + + // Tape shape + function TapeShape() + { + mxActor.call(this); + }; + mxUtils.extend(TapeShape, mxActor); + TapeShape.prototype.size = 0.4; + TapeShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dy = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var fy = 1.4; + + c.moveTo(0, dy / 2); + c.quadTo(w / 4, dy * fy, w / 2, dy / 2); + c.quadTo(w * 3 / 4, dy * (1 - fy), w, dy / 2); + c.lineTo(w, h - dy / 2); + c.quadTo(w * 3 / 4, h - dy * fy, w / 2, h - dy / 2); + c.quadTo(w / 4, h - dy * (1 - fy), 0, h - dy / 2); + c.lineTo(0, dy / 2); + c.close(); + c.end(); + }; + + TapeShape.prototype.getLabelBounds = function(rect) + { + if (mxUtils.getValue(this.style, 'boundedLbl', false)) + { + var size = mxUtils.getValue(this.style, 'size', this.size); + var w = rect.width; + var h = rect.height; + + if (this.direction == null || + this.direction == mxConstants.DIRECTION_EAST || + this.direction == mxConstants.DIRECTION_WEST) + { + var dy = h * size; + + return new mxRectangle(rect.x, rect.y + dy, w, h - 2 * dy); + } + else + { + var dx = w * size; + + return new mxRectangle(rect.x + dx, rect.y, w - 2 * dx, h); + } + } + + return rect; + }; + + mxCellRenderer.registerShape('tape', TapeShape); + + // Document shape + function DocumentShape() + { + mxActor.call(this); + }; + mxUtils.extend(DocumentShape, mxActor); + DocumentShape.prototype.size = 0.3; + DocumentShape.prototype.getLabelMargins = function(rect) + { + if (mxUtils.getValue(this.style, 'boundedLbl', false)) + { + return new mxRectangle(0, 0, 0, parseFloat(mxUtils.getValue( + this.style, 'size', this.size)) * rect.height); + } + + return null; + }; + DocumentShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dy = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var fy = 1.4; + + c.moveTo(0, 0); + c.lineTo(w, 0); + c.lineTo(w, h - dy / 2); + c.quadTo(w * 3 / 4, h - dy * fy, w / 2, h - dy / 2); + c.quadTo(w / 4, h - dy * (1 - fy), 0, h - dy / 2); + c.lineTo(0, dy / 2); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('document', DocumentShape); + + var cylinderGetCylinderSize = mxCylinder.prototype.getCylinderSize; + + mxCylinder.prototype.getCylinderSize = function(x, y, w, h) + { + var size = mxUtils.getValue(this.style, 'size'); + + if (size != null) + { + return h * Math.max(0, Math.min(1, size)); + } + else + { + return cylinderGetCylinderSize.apply(this, arguments); + } + }; + + mxCylinder.prototype.getLabelMargins = function(rect) + { + if (mxUtils.getValue(this.style, 'boundedLbl', false)) + { + var size = mxUtils.getValue(this.style, 'size', 0.15) * 2; + + return new mxRectangle(0, Math.min(this.maxHeight * this.scale, rect.height * size), 0, 0); + } + + return null; + }; + + // Parallelogram shape + function ParallelogramShape() + { + mxActor.call(this); + }; + mxUtils.extend(ParallelogramShape, mxActor); + ParallelogramShape.prototype.size = 0.2; + ParallelogramShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, h), new mxPoint(dx, 0), new mxPoint(w, 0), new mxPoint(w - dx, h)], + this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('parallelogram', ParallelogramShape); + + // Trapezoid shape + function TrapezoidShape() + { + mxActor.call(this); + }; + mxUtils.extend(TrapezoidShape, mxActor); + TrapezoidShape.prototype.size = 0.2; + TrapezoidShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = w * Math.max(0, Math.min(0.5, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, h), new mxPoint(dx, 0), new mxPoint(w - dx, 0), new mxPoint(w, h)], + this.isRounded, arcSize, true); + }; + + mxCellRenderer.registerShape('trapezoid', TrapezoidShape); + + // Curly Bracket shape + function CurlyBracketShape() + { + mxActor.call(this); + }; + mxUtils.extend(CurlyBracketShape, mxActor); + CurlyBracketShape.prototype.size = 0.5; + CurlyBracketShape.prototype.redrawPath = function(c, x, y, w, h) + { + c.setFillColor(null); + var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(w, 0), new mxPoint(s, 0), new mxPoint(s, h / 2), + new mxPoint(0, h / 2), new mxPoint(s, h / 2), new mxPoint(s, h), + new mxPoint(w, h)], this.isRounded, arcSize, false); + c.end(); + }; + + mxCellRenderer.registerShape('curlyBracket', CurlyBracketShape); + + // Parallel marker shape + function ParallelMarkerShape() + { + mxActor.call(this); + }; + mxUtils.extend(ParallelMarkerShape, mxActor); + ParallelMarkerShape.prototype.redrawPath = function(c, x, y, w, h) + { + c.setStrokeWidth(1); + c.setFillColor(this.stroke); + var w2 = w / 5; + c.rect(0, 0, w2, h); + c.fillAndStroke(); + c.rect(2 * w2, 0, w2, h); + c.fillAndStroke(); + c.rect(4 * w2, 0, w2, h); + c.fillAndStroke(); + }; + + mxCellRenderer.registerShape('parallelMarker', ParallelMarkerShape); + + /** + * Adds handJiggle style (jiggle=n sets jiggle) + */ + function HandJiggle(canvas, defaultVariation) + { + this.canvas = canvas; + + // Avoids "spikes" in the output + this.canvas.setLineJoin('round'); + this.canvas.setLineCap('round'); + + this.defaultVariation = defaultVariation; + + this.originalLineTo = this.canvas.lineTo; + this.canvas.lineTo = mxUtils.bind(this, HandJiggle.prototype.lineTo); + + this.originalMoveTo = this.canvas.moveTo; + this.canvas.moveTo = mxUtils.bind(this, HandJiggle.prototype.moveTo); + + this.originalClose = this.canvas.close; + this.canvas.close = mxUtils.bind(this, HandJiggle.prototype.close); + + this.originalQuadTo = this.canvas.quadTo; + this.canvas.quadTo = mxUtils.bind(this, HandJiggle.prototype.quadTo); + + this.originalCurveTo = this.canvas.curveTo; + this.canvas.curveTo = mxUtils.bind(this, HandJiggle.prototype.curveTo); + + this.originalArcTo = this.canvas.arcTo; + this.canvas.arcTo = mxUtils.bind(this, HandJiggle.prototype.arcTo); + }; + + HandJiggle.prototype.moveTo = function(endX, endY) + { + this.originalMoveTo.apply(this.canvas, arguments); + this.lastX = endX; + this.lastY = endY; + this.firstX = endX; + this.firstY = endY; + }; + + HandJiggle.prototype.close = function() + { + if (this.firstX != null && this.firstY != null) + { + this.lineTo(this.firstX, this.firstY); + this.originalClose.apply(this.canvas, arguments); + } + + this.originalClose.apply(this.canvas, arguments); + }; + + HandJiggle.prototype.quadTo = function(x1, y1, x2, y2) + { + this.originalQuadTo.apply(this.canvas, arguments); + this.lastX = x2; + this.lastY = y2; + }; + + HandJiggle.prototype.curveTo = function(x1, y1, x2, y2, x3, y3) + { + this.originalCurveTo.apply(this.canvas, arguments); + this.lastX = x3; + this.lastY = y3; + }; + + HandJiggle.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y) + { + this.originalArcTo.apply(this.canvas, arguments); + this.lastX = x; + this.lastY = y; + }; + + HandJiggle.prototype.lineTo = function(endX, endY) + { + // LATER: Check why this.canvas.lastX cannot be used + if (this.lastX != null && this.lastY != null) + { + var dx = Math.abs(endX - this.lastX); + var dy = Math.abs(endY - this.lastY); + var dist = Math.sqrt(dx * dx + dy * dy); + + if (dist < 2) + { + this.originalLineTo.apply(this.canvas, arguments); + this.lastX = endX; + this.lastY = endY; + + return; + } + + var segs = Math.round(dist / 10); + var variation = this.defaultVariation; + + if (segs < 5) + { + segs = 5; + variation /= 3; + } + + function sign(x) + { + return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN; + } + + var stepX = sign(endX - this.lastX) * dx / segs; + var stepY = sign(endY - this.lastY) * dy / segs; + + var fx = dx / dist; + var fy = dy / dist; + + for (var s = 0; s < segs; s++) + { + var x = stepX * s + this.lastX; + var y = stepY * s + this.lastY; + + var offset = (Math.random() - 0.5) * variation; + this.originalLineTo.call(this.canvas, x - offset * fy, y - offset * fx); + } + + this.originalLineTo.call(this.canvas, endX, endY); + this.lastX = endX; + this.lastY = endY; + } + else + { + this.originalLineTo.apply(this.canvas, arguments); + this.lastX = endX; + this.lastY = endY; + } + }; + + HandJiggle.prototype.destroy = function() + { + this.canvas.lineTo = this.originalLineTo; + this.canvas.moveTo = this.originalMoveTo; + this.canvas.close = this.originalClose; + this.canvas.quadTo = this.originalQuadTo; + this.canvas.curveTo = this.originalCurveTo; + this.canvas.arcTo = this.originalArcTo; + }; + + // Installs hand jiggle in all shapes + var mxShapePaint0 = mxShape.prototype.paint; + mxShape.prototype.defaultJiggle = 1.5; + mxShape.prototype.paint = function(c) + { + // NOTE: getValue does not return a boolean value so !('0') would return true here and below + if (this.style != null && mxUtils.getValue(this.style, 'comic', '0') != '0' && c.handHiggle == null) + { + c.handJiggle = new HandJiggle(c, mxUtils.getValue(this.style, 'jiggle', this.defaultJiggle)); + } + + mxShapePaint0.apply(this, arguments); + + if (c.handJiggle != null) + { + c.handJiggle.destroy(); + delete c.handJiggle; + } + }; + + // Sets default jiggle for diamond + mxRhombus.prototype.defaultJiggle = 2; + + /** + * Overrides to avoid call to rect + */ + var mxRectangleShapeIsHtmlAllowed0 = mxRectangleShape.prototype.isHtmlAllowed; + mxRectangleShape.prototype.isHtmlAllowed = function() + { + return (this.style == null || mxUtils.getValue(this.style, 'comic', '0') == '0') && + mxRectangleShapeIsHtmlAllowed0.apply(this, arguments); + }; + + var mxRectangleShapePaintBackground0 = mxRectangleShape.prototype.paintBackground; + mxRectangleShape.prototype.paintBackground = function(c, x, y, w, h) + { + if (c.handJiggle == null) + { + mxRectangleShapePaintBackground0.apply(this, arguments); + } + else + { + var events = true; + + if (this.style != null) + { + events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1'; + } + + if (events || (this.fill != null && this.fill != mxConstants.NONE) || + (this.stroke != null && this.stroke != mxConstants.NONE)) + { + if (!events && (this.fill == null || this.fill == mxConstants.NONE)) + { + c.pointerEvents = false; + } + + c.begin(); + + if (this.isRounded) + { + var r = 0; + + if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') + { + r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style, + mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2)); + } + else + { + var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; + r = Math.min(w * f, h * f); + } + + c.moveTo(x + r, y); + c.lineTo(x + w - r, y); + c.quadTo(x + w, y, x + w, y + r); + c.lineTo(x + w, y + h - r); + c.quadTo(x + w, y + h, x + w - r, y + h); + c.lineTo(x + r, y + h); + c.quadTo(x, y + h, x, y + h - r); + c.lineTo(x, y + r); + c.quadTo(x, y, x + r, y); + } + else + { + + c.moveTo(x, y); + c.lineTo(x + w, y); + c.lineTo(x + w, y + h); + c.lineTo(x, y + h); + c.lineTo(x, y); + } + + // LATER: Check if close is needed here + c.close(); + c.end(); + + c.fillAndStroke(); + } + } + }; + + /** + * Disables glass effect with hand jiggle. + */ + var mxRectangleShapePaintForeground0 = mxRectangleShape.prototype.paintForeground; + mxRectangleShape.prototype.paintForeground = function(c, x, y, w, h) + { + if (c.handJiggle == null) + { + mxRectangleShapePaintForeground0.apply(this, arguments); + } + }; + + // End of hand jiggle integration + + // Process Shape + function ProcessShape() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(ProcessShape, mxRectangleShape); + ProcessShape.prototype.size = 0.1; + ProcessShape.prototype.isHtmlAllowed = function() + { + return false; + }; + ProcessShape.prototype.getLabelBounds = function(rect) + { + if (mxUtils.getValue(this.state.style, mxConstants.STYLE_HORIZONTAL, true) == + (this.direction == null || + this.direction == mxConstants.DIRECTION_EAST || + this.direction == mxConstants.DIRECTION_WEST)) + { + var w = rect.width; + var h = rect.height; + var r = new mxRectangle(rect.x, rect.y, w, h); + + var inset = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + + if (this.isRounded) + { + var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; + inset = Math.max(inset, Math.min(w * f, h * f)); + } + + r.x += Math.round(inset); + r.width -= Math.round(2 * inset); + + return r; + } + + return rect; + }; + ProcessShape.prototype.paintForeground = function(c, x, y, w, h) + { + var inset = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + + if (this.isRounded) + { + var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; + inset = Math.max(inset, Math.min(w * f, h * f)); + } + + // Crisp rendering of inner lines + inset = Math.round(inset); + + c.begin(); + c.moveTo(x + inset, y); + c.lineTo(x + inset, y + h); + c.moveTo(x + w - inset, y); + c.lineTo(x + w - inset, y + h); + c.end(); + c.stroke(); + mxRectangleShape.prototype.paintForeground.apply(this, arguments); + }; + + mxCellRenderer.registerShape('process', ProcessShape); + + // Transparent Shape + function TransparentShape() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(TransparentShape, mxRectangleShape); + TransparentShape.prototype.paintBackground = function(c, x, y, w, h) + { + c.setFillColor(mxConstants.NONE); + c.rect(x, y, w, h); + c.fill(); + }; + TransparentShape.prototype.paintForeground = function(c, x, y, w, h) { }; + + mxCellRenderer.registerShape('transparent', TransparentShape); + + // Callout shape + function CalloutShape() + { + mxActor.call(this); + }; + mxUtils.extend(CalloutShape, mxHexagon); + CalloutShape.prototype.size = 30; + CalloutShape.prototype.position = 0.5; + CalloutShape.prototype.position2 = 0.5; + CalloutShape.prototype.base = 20; + CalloutShape.prototype.getLabelMargins = function() + { + return new mxRectangle(0, 0, 0, parseFloat(mxUtils.getValue( + this.style, 'size', this.size)) * this.scale); + }; + CalloutShape.prototype.redrawPath = function(c, x, y, w, h) + { + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + var s = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var dx = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position', this.position)))); + var dx2 = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position2', this.position2)))); + var base = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'base', this.base)))); + + this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, h - s), + new mxPoint(Math.min(w, dx + base), h - s), new mxPoint(dx2, h), + new mxPoint(Math.max(0, dx), h - s), new mxPoint(0, h - s)], + this.isRounded, arcSize, true, [4]); + }; + + mxCellRenderer.registerShape('callout', CalloutShape); + + // Step shape + function StepShape() + { + mxActor.call(this); + }; + mxUtils.extend(StepShape, mxActor); + StepShape.prototype.size = 0.2; + StepShape.prototype.fixedSize = 20; + StepShape.prototype.redrawPath = function(c, x, y, w, h) + { + var fixed = mxUtils.getValue(this.style, 'fixedSize', '0') != '0'; + var s = (fixed) ? Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'size', this.fixedSize)))) : + w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w - s, 0), new mxPoint(w, h / 2), new mxPoint(w - s, h), + new mxPoint(0, h), new mxPoint(s, h / 2)], this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('step', StepShape); + + // Hexagon shape + function HexagonShape() + { + mxActor.call(this); + }; + mxUtils.extend(HexagonShape, mxHexagon); + HexagonShape.prototype.size = 0.25; + HexagonShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w - s, 0), new mxPoint(w, 0.5 * h), new mxPoint(w - s, h), + new mxPoint(s, h), new mxPoint(0, 0.5 * h)], this.isRounded, arcSize, true); + }; + + mxCellRenderer.registerShape('hexagon', HexagonShape); + + // Plus Shape + function PlusShape() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(PlusShape, mxRectangleShape); + PlusShape.prototype.isHtmlAllowed = function() + { + return false; + }; + PlusShape.prototype.paintForeground = function(c, x, y, w, h) + { + var border = Math.min(w / 5, h / 5) + 1; + + c.begin(); + c.moveTo(x + w / 2, y + border); + c.lineTo(x + w / 2, y + h - border); + c.moveTo(x + border, y + h / 2); + c.lineTo(x + w - border, y + h / 2); + c.end(); + c.stroke(); + mxRectangleShape.prototype.paintForeground.apply(this, arguments); + }; + + mxCellRenderer.registerShape('plus', PlusShape); + + // Overrides painting of rhombus shape to allow for double style + var mxRhombusPaintVertexShape = mxRhombus.prototype.paintVertexShape; + mxRhombus.prototype.getLabelBounds = function(rect) + { + if (this.style['double'] == 1) + { + var margin = (Math.max(2, this.strokewidth + 1) * 2 + parseFloat( + this.style[mxConstants.STYLE_MARGIN] || 0)) * this.scale; + + return new mxRectangle(rect.x + margin, rect.y + margin, + rect.width - 2 * margin, rect.height - 2 * margin); + } + + return rect; + }; + mxRhombus.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxRhombusPaintVertexShape.apply(this, arguments); + + if (!this.outline && this.style['double'] == 1) + { + var margin = Math.max(2, this.strokewidth + 1) * 2 + + parseFloat(this.style[mxConstants.STYLE_MARGIN] || 0); + x += margin; + y += margin; + w -= 2 * margin; + h -= 2 * margin; + + if (w > 0 && h > 0) + { + c.setShadow(false); + + // Workaround for closure compiler bug where the lines with x and y above + // are removed if arguments is used as second argument in call below. + mxRhombusPaintVertexShape.apply(this, [c, x, y, w, h]); + } + } + }; + + // CompositeShape + function ExtendedShape() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(ExtendedShape, mxRectangleShape); + ExtendedShape.prototype.isHtmlAllowed = function() + { + return false; + }; + ExtendedShape.prototype.getLabelBounds = function(rect) + { + if (this.style['double'] == 1) + { + var margin = (Math.max(2, this.strokewidth + 1) + parseFloat( + this.style[mxConstants.STYLE_MARGIN] || 0)) * this.scale; + + return new mxRectangle(rect.x + margin, rect.y + margin, + rect.width - 2 * margin, rect.height - 2 * margin); + } + + return rect; + }; + + ExtendedShape.prototype.paintForeground = function(c, x, y, w, h) + { + if (this.style != null) + { + if (!this.outline && this.style['double'] == 1) + { + var margin = Math.max(2, this.strokewidth + 1) + parseFloat(this.style[mxConstants.STYLE_MARGIN] || 0); + x += margin; + y += margin; + w -= 2 * margin; + h -= 2 * margin; + + if (w > 0 && h > 0) + { + mxRectangleShape.prototype.paintBackground.apply(this, arguments); + } + } + + c.setDashed(false); + + // Draws the symbols defined in the style. The symbols are + // numbered from 1...n. Possible postfixes are align, + // verticalAlign, spacing, arcSpacing, width, height + var counter = 0; + var shape = null; + + do + { + shape = mxCellRenderer.defaultShapes[this.style['symbol' + counter]]; + + if (shape != null) + { + var align = this.style['symbol' + counter + 'Align']; + var valign = this.style['symbol' + counter + 'VerticalAlign']; + var width = this.style['symbol' + counter + 'Width']; + var height = this.style['symbol' + counter + 'Height']; + var spacing = this.style['symbol' + counter + 'Spacing'] || 0; + var vspacing = this.style['symbol' + counter + 'VSpacing'] || spacing; + var arcspacing = this.style['symbol' + counter + 'ArcSpacing']; + + if (arcspacing != null) + { + var arcSize = this.getArcSize(w + this.strokewidth, h + this.strokewidth) * arcspacing; + spacing += arcSize; + vspacing += arcSize; + } + + var x2 = x; + var y2 = y; + + if (align == mxConstants.ALIGN_CENTER) + { + x2 += (w - width) / 2; + } + else if (align == mxConstants.ALIGN_RIGHT) + { + x2 += w - width - spacing; + } + else + { + x2 += spacing; + } + + if (valign == mxConstants.ALIGN_MIDDLE) + { + y2 += (h - height) / 2; + } + else if (valign == mxConstants.ALIGN_BOTTOM) + { + y2 += h - height - vspacing; + } + else + { + y2 += vspacing; + } + + c.save(); + + // Small hack to pass style along into subshape + var tmp = new shape(); + // TODO: Clone style and override settings (eg. strokewidth) + tmp.style = this.style; + shape.prototype.paintVertexShape.call(tmp, c, x2, y2, width, height); + c.restore(); + } + + counter++; + } + while (shape != null); + } + + // Paints glass effect + mxRectangleShape.prototype.paintForeground.apply(this, arguments); + }; + + mxCellRenderer.registerShape('ext', ExtendedShape); + + // Tape Shape, supports size style + function MessageShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(MessageShape, mxCylinder); + MessageShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + if (isForeground) + { + path.moveTo(0, 0); + path.lineTo(w / 2, h / 2); + path.lineTo(w, 0); + path.end(); + } + else + { + path.moveTo(0, 0); + path.lineTo(w, 0); + path.lineTo(w, h); + path.lineTo(0, h); + path.close(); + } + }; + + mxCellRenderer.registerShape('message', MessageShape); + + // UML Actor Shape + function UmlActorShape() + { + mxShape.call(this); + }; + mxUtils.extend(UmlActorShape, mxShape); + UmlActorShape.prototype.paintBackground = function(c, x, y, w, h) + { + c.translate(x, y); + + // Head + c.ellipse(w / 4, 0, w / 2, h / 4); + c.fillAndStroke(); + + c.begin(); + c.moveTo(w / 2, h / 4); + c.lineTo(w / 2, 2 * h / 3); + + // Arms + c.moveTo(w / 2, h / 3); + c.lineTo(0, h / 3); + c.moveTo(w / 2, h / 3); + c.lineTo(w, h / 3); + + // Legs + c.moveTo(w / 2, 2 * h / 3); + c.lineTo(0, h); + c.moveTo(w / 2, 2 * h / 3); + c.lineTo(w, h); + c.end(); + + c.stroke(); + }; + + // Replaces existing actor shape + mxCellRenderer.registerShape('umlActor', UmlActorShape); + + // UML Boundary Shape + function UmlBoundaryShape() + { + mxShape.call(this); + }; + mxUtils.extend(UmlBoundaryShape, mxShape); + UmlBoundaryShape.prototype.getLabelMargins = function(rect) + { + return new mxRectangle(rect.width / 6, 0, 0, 0); + }; + UmlBoundaryShape.prototype.paintBackground = function(c, x, y, w, h) + { + c.translate(x, y); + + // Base line + c.begin(); + c.moveTo(0, h / 4); + c.lineTo(0, h * 3 / 4); + c.end(); + c.stroke(); + + // Horizontal line + c.begin(); + c.moveTo(0, h / 2); + c.lineTo(w / 6, h / 2); + c.end(); + c.stroke(); + + // Circle + c.ellipse(w / 6, 0, w * 5 / 6, h); + c.fillAndStroke(); + }; + + // Replaces existing actor shape + mxCellRenderer.registerShape('umlBoundary', UmlBoundaryShape); + + // UML Entity Shape + function UmlEntityShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(UmlEntityShape, mxEllipse); + UmlEntityShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxEllipse.prototype.paintVertexShape.apply(this, arguments); + + c.begin(); + c.moveTo(x + w / 8, y + h); + c.lineTo(x + w * 7 / 8, y + h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('umlEntity', UmlEntityShape); + + // UML Destroy Shape + function UmlDestroyShape() + { + mxShape.call(this); + }; + mxUtils.extend(UmlDestroyShape, mxShape); + UmlDestroyShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + c.translate(x, y); + + c.begin(); + c.moveTo(w, 0); + c.lineTo(0, h); + c.moveTo(0, 0); + c.lineTo(w, h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('umlDestroy', UmlDestroyShape); + + // UML Control Shape + function UmlControlShape() + { + mxShape.call(this); + }; + mxUtils.extend(UmlControlShape, mxShape); + UmlControlShape.prototype.getLabelBounds = function(rect) + { + return new mxRectangle(rect.x, rect.y + rect.height / 8, rect.width, rect.height * 7 / 8); + }; + UmlControlShape.prototype.paintBackground = function(c, x, y, w, h) + { + c.translate(x, y); + + // Upper line + c.begin(); + c.moveTo(w * 3 / 8, h / 8 * 1.1); + c.lineTo(w * 5 / 8, 0); + c.end(); + c.stroke(); + + // Circle + c.ellipse(0, h / 8, w, h * 7 / 8); + c.fillAndStroke(); + }; + UmlControlShape.prototype.paintForeground = function(c, x, y, w, h) + { + // Lower line + c.begin(); + c.moveTo(w * 3 / 8, h / 8 * 1.1); + c.lineTo(w * 5 / 8, h / 4); + c.end(); + c.stroke(); + }; + + // Replaces existing actor shape + mxCellRenderer.registerShape('umlControl', UmlControlShape); + + // UML Lifeline Shape + function UmlLifeline() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(UmlLifeline, mxRectangleShape); + UmlLifeline.prototype.size = 40; + UmlLifeline.prototype.isHtmlAllowed = function() + { + return false; + }; + UmlLifeline.prototype.getLabelBounds = function(rect) + { + var size = Math.max(0, Math.min(rect.height, parseFloat( + mxUtils.getValue(this.style, 'size', this.size)) * this.scale)); + + return new mxRectangle(rect.x, rect.y, rect.width, size); + }; + UmlLifeline.prototype.paintBackground = function(c, x, y, w, h) + { + var size = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var participant = mxUtils.getValue(this.style, 'participant'); + + if (participant == null || this.state == null) + { + mxRectangleShape.prototype.paintBackground.call(this, c, x, y, w, size); + } + else + { + var ctor = this.state.view.graph.cellRenderer.getShape(participant); + + if (ctor != null && ctor != UmlLifeline) + { + var shape = new ctor(); + shape.apply(this.state); + c.save(); + shape.paintVertexShape(c, x, y, w, size); + c.restore(); + } + } + + if (size < h) + { + c.setDashed(true); + c.begin(); + c.moveTo(x + w / 2, y + size); + c.lineTo(x + w / 2, y + h); + c.end(); + c.stroke(); + } + }; + UmlLifeline.prototype.paintForeground = function(c, x, y, w, h) + { + var size = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + mxRectangleShape.prototype.paintForeground.call(this, c, x, y, w, Math.min(h, size)); + }; + + mxCellRenderer.registerShape('umlLifeline', UmlLifeline); + + // UML Frame Shape + function UmlFrame() + { + mxShape.call(this); + }; + mxUtils.extend(UmlFrame, mxShape); + UmlFrame.prototype.width = 60; + UmlFrame.prototype.height = 30; + UmlFrame.prototype.corner = 10; + UmlFrame.prototype.getLabelMargins = function(rect) + { + return new mxRectangle(0, 0, + rect.width - (parseFloat(mxUtils.getValue(this.style, 'width', this.width) * this.scale)), + rect.height - (parseFloat(mxUtils.getValue(this.style, 'height', this.height) * this.scale))); + }; + UmlFrame.prototype.paintBackground = function(c, x, y, w, h) + { + var co = this.corner; + var w0 = Math.min(w, Math.max(co, parseFloat(mxUtils.getValue(this.style, 'width', this.width)))); + var h0 = Math.min(h, Math.max(co * 1.5, parseFloat(mxUtils.getValue(this.style, 'height', this.height)))); + var bg = mxUtils.getValue(this.style, mxConstants.STYLE_SWIMLANE_FILLCOLOR, mxConstants.NONE); + + if (bg != mxConstants.NONE) + { + c.setFillColor(bg); + c.rect(x, y, w, h); + c.fill(); + } + + if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) + { + var b = this.getGradientBounds(c, x, y, w, h); + c.setGradient(this.fill, this.gradient, x, y, w, h, this.gradientDirection); + } + else + { + c.setFillColor(this.fill); + } + + c.begin(); + c.moveTo(x, y); + c.lineTo(x + w0, y); + c.lineTo(x + w0, y + Math.max(0, h0 - co * 1.5)); + c.lineTo(x + Math.max(0, w0 - co), y + h0); + c.lineTo(x, y + h0); + c.close(); + c.fillAndStroke(); + + c.begin(); + c.moveTo(x + w0, y); + c.lineTo(x + w, y); + c.lineTo(x + w, y + h); + c.lineTo(x, y + h); + c.lineTo(x, y + h0); + c.stroke(); + }; + + mxCellRenderer.registerShape('umlFrame', UmlFrame); + + mxPerimeter.LifelinePerimeter = function (bounds, vertex, next, orthogonal) + { + var size = UmlLifeline.prototype.size; + + if (vertex != null) + { + size = mxUtils.getValue(vertex.style, 'size', size) * vertex.view.scale; + } + + var sw = (parseFloat(vertex.style[mxConstants.STYLE_STROKEWIDTH] || 1) * vertex.view.scale / 2) - 1; + + if (next.x < bounds.getCenterX()) + { + sw += 1; + sw *= -1; + } + + return new mxPoint(bounds.getCenterX() + sw, Math.min(bounds.y + bounds.height, + Math.max(bounds.y + size, next.y))); + }; + + mxStyleRegistry.putValue('lifelinePerimeter', mxPerimeter.LifelinePerimeter); + + mxPerimeter.OrthogonalPerimeter = function (bounds, vertex, next, orthogonal) + { + orthogonal = true; + + return mxPerimeter.RectanglePerimeter.apply(this, arguments); + }; + + mxStyleRegistry.putValue('orthogonalPerimeter', mxPerimeter.OrthogonalPerimeter); + + mxPerimeter.BackbonePerimeter = function (bounds, vertex, next, orthogonal) + { + var sw = (parseFloat(vertex.style[mxConstants.STYLE_STROKEWIDTH] || 1) * vertex.view.scale / 2) - 1; + + if (vertex.style['backboneSize'] != null) + { + sw += (parseFloat(vertex.style['backboneSize']) * vertex.view.scale / 2) - 1; + } + + if (vertex.style[mxConstants.STYLE_DIRECTION] == 'south' || + vertex.style[mxConstants.STYLE_DIRECTION] == 'north') + { + if (next.x < bounds.getCenterX()) + { + sw += 1; + sw *= -1; + } + + return new mxPoint(bounds.getCenterX() + sw, Math.min(bounds.y + bounds.height, + Math.max(bounds.y, next.y))); + } + else + { + if (next.y < bounds.getCenterY()) + { + sw += 1; + sw *= -1; + } + + return new mxPoint(Math.min(bounds.x + bounds.width, Math.max(bounds.x, next.x)), + bounds.getCenterY() + sw); + } + }; + + mxStyleRegistry.putValue('backbonePerimeter', mxPerimeter.BackbonePerimeter); + + // Callout Perimeter + mxPerimeter.CalloutPerimeter = function (bounds, vertex, next, orthogonal) + { + return mxPerimeter.RectanglePerimeter(mxUtils.getDirectedBounds(bounds, new mxRectangle(0, 0, 0, + Math.max(0, Math.min(bounds.height, parseFloat(mxUtils.getValue(vertex.style, 'size', + CalloutShape.prototype.size)) * vertex.view.scale))), + vertex.style), vertex, next, orthogonal); + }; + + mxStyleRegistry.putValue('calloutPerimeter', mxPerimeter.CalloutPerimeter); + + // Parallelogram Perimeter + mxPerimeter.ParallelogramPerimeter = function (bounds, vertex, next, orthogonal) + { + var size = ParallelogramShape.prototype.size; + + if (vertex != null) + { + size = mxUtils.getValue(vertex.style, 'size', size); + } + + var x = bounds.x; + var y = bounds.y; + var w = bounds.width; + var h = bounds.height; + + var direction = (vertex != null) ? mxUtils.getValue( + vertex.style, mxConstants.STYLE_DIRECTION, + mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; + var vertical = direction == mxConstants.DIRECTION_NORTH || + direction == mxConstants.DIRECTION_SOUTH; + var points; + + if (vertical) + { + var dy = h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y), new mxPoint(x + w, y + dy), + new mxPoint(x + w, y + h), new mxPoint(x, y + h - dy), new mxPoint(x, y)]; + } + else + { + var dx = w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x + dx, y), new mxPoint(x + w, y), + new mxPoint(x + w - dx, y + h), new mxPoint(x, y + h), new mxPoint(x + dx, y)]; + } + + var cx = bounds.getCenterX(); + var cy = bounds.getCenterY(); + + var p1 = new mxPoint(cx, cy); + + if (orthogonal) + { + if (next.x < x || next.x > x + w) + { + p1.y = next.y; + } + else + { + p1.x = next.x; + } + } + + return mxUtils.getPerimeterPoint(points, p1, next); + }; + + mxStyleRegistry.putValue('parallelogramPerimeter', mxPerimeter.ParallelogramPerimeter); + + // Trapezoid Perimeter + mxPerimeter.TrapezoidPerimeter = function (bounds, vertex, next, orthogonal) + { + var size = TrapezoidShape.prototype.size; + + if (vertex != null) + { + size = mxUtils.getValue(vertex.style, 'size', size); + } + + var x = bounds.x; + var y = bounds.y; + var w = bounds.width; + var h = bounds.height; + + var direction = (vertex != null) ? mxUtils.getValue( + vertex.style, mxConstants.STYLE_DIRECTION, + mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; + var points; + + if (direction == mxConstants.DIRECTION_EAST) + { + var dx = w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x + dx, y), new mxPoint(x + w - dx, y), + new mxPoint(x + w, y + h), new mxPoint(x, y + h), new mxPoint(x + dx, y)]; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + var dx = w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y), new mxPoint(x + w, y), + new mxPoint(x + w - dx, y + h), new mxPoint(x + dx, y + h), new mxPoint(x, y)]; + } + else if (direction == mxConstants.DIRECTION_NORTH) + { + var dy = h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y + dy), new mxPoint(x + w, y), + new mxPoint(x + w, y + h), new mxPoint(x, y + h - dy), new mxPoint(x, y + dy)]; + } + else + { + var dy = h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y), new mxPoint(x + w, y + dy), + new mxPoint(x + w, y + h - dy), new mxPoint(x, y + h), new mxPoint(x, y)]; + } + + var cx = bounds.getCenterX(); + var cy = bounds.getCenterY(); + + var p1 = new mxPoint(cx, cy); + + if (orthogonal) + { + if (next.x < x || next.x > x + w) + { + p1.y = next.y; + } + else + { + p1.x = next.x; + } + } + + return mxUtils.getPerimeterPoint(points, p1, next); + }; + + mxStyleRegistry.putValue('trapezoidPerimeter', mxPerimeter.TrapezoidPerimeter); + + // Step Perimeter + mxPerimeter.StepPerimeter = function (bounds, vertex, next, orthogonal) + { + var fixed = mxUtils.getValue(vertex.style, 'fixedSize', '0') != '0'; + var size = (fixed) ? StepShape.prototype.fixedSize : StepShape.prototype.size; + + if (vertex != null) + { + size = mxUtils.getValue(vertex.style, 'size', size); + } + + var x = bounds.x; + var y = bounds.y; + var w = bounds.width; + var h = bounds.height; + + var cx = bounds.getCenterX(); + var cy = bounds.getCenterY(); + + var direction = (vertex != null) ? mxUtils.getValue( + vertex.style, mxConstants.STYLE_DIRECTION, + mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; + var points; + + if (direction == mxConstants.DIRECTION_EAST) + { + var dx = (fixed) ? Math.max(0, Math.min(w, size)) : w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y), new mxPoint(x + w - dx, y), new mxPoint(x + w, cy), + new mxPoint(x + w - dx, y + h), new mxPoint(x, y + h), + new mxPoint(x + dx, cy), new mxPoint(x, y)]; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + var dx = (fixed) ? Math.max(0, Math.min(w, size)) : w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x + dx, y), new mxPoint(x + w, y), new mxPoint(x + w - dx, cy), + new mxPoint(x + w, y + h), new mxPoint(x + dx, y + h), + new mxPoint(x, cy), new mxPoint(x + dx, y)]; + } + else if (direction == mxConstants.DIRECTION_NORTH) + { + var dy = (fixed) ? Math.max(0, Math.min(h, size)) : h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y + dy), new mxPoint(cx, y), new mxPoint(x + w, y + dy), + new mxPoint(x + w, y + h), new mxPoint(cx, y + h - dy), + new mxPoint(x, y + h), new mxPoint(x, y + dy)]; + } + else + { + var dy = (fixed) ? Math.max(0, Math.min(h, size)) : h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x, y), new mxPoint(cx, y + dy), new mxPoint(x + w, y), + new mxPoint(x + w, y + h - dy), new mxPoint(cx, y + h), + new mxPoint(x, y + h - dy), new mxPoint(x, y)]; + } + + var p1 = new mxPoint(cx, cy); + + if (orthogonal) + { + if (next.x < x || next.x > x + w) + { + p1.y = next.y; + } + else + { + p1.x = next.x; + } + } + + return mxUtils.getPerimeterPoint(points, p1, next); + }; + + mxStyleRegistry.putValue('stepPerimeter', mxPerimeter.StepPerimeter); + + // Hexagon Perimeter 2 (keep existing one) + mxPerimeter.HexagonPerimeter2 = function (bounds, vertex, next, orthogonal) + { + var size = HexagonShape.prototype.size; + + if (vertex != null) + { + size = mxUtils.getValue(vertex.style, 'size', size); + } + + var x = bounds.x; + var y = bounds.y; + var w = bounds.width; + var h = bounds.height; + + var cx = bounds.getCenterX(); + var cy = bounds.getCenterY(); + + var direction = (vertex != null) ? mxUtils.getValue( + vertex.style, mxConstants.STYLE_DIRECTION, + mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; + var vertical = direction == mxConstants.DIRECTION_NORTH || + direction == mxConstants.DIRECTION_SOUTH; + var points; + + if (vertical) + { + var dy = h * Math.max(0, Math.min(1, size)); + points = [new mxPoint(cx, y), new mxPoint(x + w, y + dy), new mxPoint(x + w, y + h - dy), + new mxPoint(cx, y + h), new mxPoint(x, y + h - dy), + new mxPoint(x, y + dy), new mxPoint(cx, y)]; + } + else + { + var dx = w * Math.max(0, Math.min(1, size)); + points = [new mxPoint(x + dx, y), new mxPoint(x + w - dx, y), new mxPoint(x + w, cy), + new mxPoint(x + w - dx, y + h), new mxPoint(x + dx, y + h), + new mxPoint(x, cy), new mxPoint(x + dx, y)]; + } + + var p1 = new mxPoint(cx, cy); + + if (orthogonal) + { + if (next.x < x || next.x > x + w) + { + p1.y = next.y; + } + else + { + p1.x = next.x; + } + } + + return mxUtils.getPerimeterPoint(points, p1, next); + }; + + mxStyleRegistry.putValue('hexagonPerimeter2', mxPerimeter.HexagonPerimeter2); + + // Lollipop Shape + function LollipopShape() + { + mxShape.call(this); + }; + mxUtils.extend(LollipopShape, mxShape); + LollipopShape.prototype.size = 10; + LollipopShape.prototype.paintBackground = function(c, x, y, w, h) + { + var sz = parseFloat(mxUtils.getValue(this.style, 'size', this.size)); + c.translate(x, y); + + c.ellipse((w - sz) / 2, 0, sz, sz); + c.fillAndStroke(); + + c.begin(); + c.moveTo(w / 2, sz); + c.lineTo(w / 2, h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('lollipop', LollipopShape); + + // Lollipop Shape + function RequiresShape() + { + mxShape.call(this); + }; + mxUtils.extend(RequiresShape, mxShape); + RequiresShape.prototype.size = 10; + RequiresShape.prototype.inset = 2; + RequiresShape.prototype.paintBackground = function(c, x, y, w, h) + { + var sz = parseFloat(mxUtils.getValue(this.style, 'size', this.size)); + var inset = parseFloat(mxUtils.getValue(this.style, 'inset', this.inset)) + this.strokewidth; + c.translate(x, y); + + c.begin(); + c.moveTo(w / 2, sz + inset); + c.lineTo(w / 2, h); + c.end(); + c.stroke(); + + c.begin(); + c.moveTo((w - sz) / 2 - inset, sz / 2); + c.quadTo((w - sz) / 2 - inset, sz + inset, w / 2, sz + inset); + c.quadTo((w + sz) / 2 + inset, sz + inset, (w + sz) / 2 + inset, sz / 2); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('requires', RequiresShape); + + // Component shape + function ComponentShape() + { + mxCylinder.call(this); + }; + mxUtils.extend(ComponentShape, mxCylinder); + ComponentShape.prototype.jettyWidth = 32; + ComponentShape.prototype.jettyHeight = 12; + ComponentShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) + { + var dx = parseFloat(mxUtils.getValue(this.style, 'jettyWidth', this.jettyWidth)); + var dy = parseFloat(mxUtils.getValue(this.style, 'jettyHeight', this.jettyHeight)); + var x0 = dx / 2; + var x1 = x0 + dx / 2; + var y0 = 0.3 * h - dy / 2; + var y1 = 0.7 * h - dy / 2; + + if (isForeground) + { + path.moveTo(x0, y0); + path.lineTo(x1, y0); + path.lineTo(x1, y0 + dy); + path.lineTo(x0, y0 + dy); + path.moveTo(x0, y1); + path.lineTo(x1, y1); + path.lineTo(x1, y1 + dy); + path.lineTo(x0, y1 + dy); + path.end(); + } + else + { + path.moveTo(x0, 0); + path.lineTo(w, 0); + path.lineTo(w, h); + path.lineTo(x0, h); + path.lineTo(x0, y1 + dy); + path.lineTo(0, y1 + dy); + path.lineTo(0, y1); + path.lineTo(x0, y1); + path.lineTo(x0, y0 + dy); + path.lineTo(0, y0 + dy); + path.lineTo(0, y0); + path.lineTo(x0, y0); + path.close(); + path.end(); + } + }; + + mxCellRenderer.registerShape('component', ComponentShape); + + // State Shapes derives from double ellipse + function StateShape() + { + mxDoubleEllipse.call(this); + }; + mxUtils.extend(StateShape, mxDoubleEllipse); + StateShape.prototype.outerStroke = true; + StateShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + var inset = Math.min(4, Math.min(w / 5, h / 5)); + + if (w > 0 && h > 0) + { + c.ellipse(x + inset, y + inset, w - 2 * inset, h - 2 * inset); + c.fillAndStroke(); + } + + c.setShadow(false); + + if (this.outerStroke) + { + c.ellipse(x, y, w, h); + c.stroke(); + } + }; + + mxCellRenderer.registerShape('endState', StateShape); + + function StartStateShape() + { + StateShape.call(this); + }; + mxUtils.extend(StartStateShape, StateShape); + StartStateShape.prototype.outerStroke = false; + + mxCellRenderer.registerShape('startState', StartStateShape); + + // Link shape + function LinkShape() + { + mxArrowConnector.call(this); + this.spacing = 0; + }; + mxUtils.extend(LinkShape, mxArrowConnector); + LinkShape.prototype.defaultWidth = 4; + + LinkShape.prototype.isOpenEnded = function() + { + return true; + }; + + LinkShape.prototype.getEdgeWidth = function() + { + return mxUtils.getNumber(this.style, 'width', this.defaultWidth) + Math.max(0, this.strokewidth - 1); + }; + + LinkShape.prototype.isArrowRounded = function() + { + return this.isRounded; + }; + + // Registers the link shape + mxCellRenderer.registerShape('link', LinkShape); + + // Generic arrow + function FlexArrowShape() + { + mxArrowConnector.call(this); + this.spacing = 0; + }; + mxUtils.extend(FlexArrowShape, mxArrowConnector); + FlexArrowShape.prototype.defaultWidth = 10; + FlexArrowShape.prototype.defaultArrowWidth = 20; + + FlexArrowShape.prototype.getStartArrowWidth = function() + { + return this.getEdgeWidth() + mxUtils.getNumber(this.style, 'startWidth', this.defaultArrowWidth); + }; + + FlexArrowShape.prototype.getEndArrowWidth = function() + { + return this.getEdgeWidth() + mxUtils.getNumber(this.style, 'endWidth', this.defaultArrowWidth);; + }; + + FlexArrowShape.prototype.getEdgeWidth = function() + { + return mxUtils.getNumber(this.style, 'width', this.defaultWidth) + Math.max(0, this.strokewidth - 1); + }; + + // Registers the link shape + mxCellRenderer.registerShape('flexArrow', FlexArrowShape); + + // Manual Input shape + function ManualInputShape() + { + mxActor.call(this); + }; + mxUtils.extend(ManualInputShape, mxActor); + ManualInputShape.prototype.size = 30; + ManualInputShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, h), new mxPoint(0, s), new mxPoint(w, 0), new mxPoint(w, h)], + this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('manualInput', ManualInputShape); + + // Internal storage + function InternalStorageShape() + { + mxRectangleShape.call(this); + }; + mxUtils.extend(InternalStorageShape, mxRectangleShape); + InternalStorageShape.prototype.dx = 20; + InternalStorageShape.prototype.dy = 20; + InternalStorageShape.prototype.isHtmlAllowed = function() + { + return false; + }; + InternalStorageShape.prototype.paintForeground = function(c, x, y, w, h) + { + mxRectangleShape.prototype.paintForeground.apply(this, arguments); + var inset = 0; + + if (this.isRounded) + { + var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, + mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; + inset = Math.max(inset, Math.min(w * f, h * f)); + } + + var dx = Math.max(inset, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); + var dy = Math.max(inset, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); + + c.begin(); + c.moveTo(x, y + dy); + c.lineTo(x + w, y + dy); + c.end(); + c.stroke(); + + c.begin(); + c.moveTo(x + dx, y); + c.lineTo(x + dx, y + h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('internalStorage', InternalStorageShape); + + // Internal storage + function CornerShape() + { + mxActor.call(this); + }; + mxUtils.extend(CornerShape, mxActor); + CornerShape.prototype.dx = 20; + CornerShape.prototype.dy = 20; + + // Corner + CornerShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); + var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); + + var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, dy), new mxPoint(dx, dy), + new mxPoint(dx, h), new mxPoint(0, h)], this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('corner', CornerShape); + + // Crossbar shape + function CrossbarShape() + { + mxActor.call(this); + }; + mxUtils.extend(CrossbarShape, mxActor); + + CrossbarShape.prototype.redrawPath = function(c, x, y, w, h) + { + c.moveTo(0, 0); + c.lineTo(0, h); + c.end(); + + c.moveTo(w, 0); + c.lineTo(w, h); + c.end(); + + c.moveTo(0, h / 2); + c.lineTo(w, h / 2); + c.end(); + }; + + mxCellRenderer.registerShape('crossbar', CrossbarShape); + + // Internal storage + function TeeShape() + { + mxActor.call(this); + }; + mxUtils.extend(TeeShape, mxActor); + TeeShape.prototype.dx = 20; + TeeShape.prototype.dy = 20; + + // Corner + TeeShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); + var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); + var w2 = Math.abs(w - dx) / 2; + + var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, dy), new mxPoint((w + dx) / 2, dy), + new mxPoint((w + dx) / 2, h), new mxPoint((w - dx) / 2, h), new mxPoint((w - dx) / 2, dy), + new mxPoint(0, dy)], this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('tee', TeeShape); + + // Arrow + function SingleArrowShape() + { + mxActor.call(this); + }; + mxUtils.extend(SingleArrowShape, mxActor); + SingleArrowShape.prototype.arrowWidth = 0.3; + SingleArrowShape.prototype.arrowSize = 0.2; + SingleArrowShape.prototype.redrawPath = function(c, x, y, w, h) + { + var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', this.arrowWidth)))); + var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', this.arrowSize)))); + var at = (h - aw) / 2; + var ab = at + aw; + + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, at), new mxPoint(w - as, at), new mxPoint(w - as, 0), new mxPoint(w, h / 2), + new mxPoint(w - as, h), new mxPoint(w - as, ab), new mxPoint(0, ab)], + this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('singleArrow', SingleArrowShape); + + // Arrow + function DoubleArrowShape() + { + mxActor.call(this); + }; + mxUtils.extend(DoubleArrowShape, mxActor); + DoubleArrowShape.prototype.redrawPath = function(c, x, y, w, h) + { + var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', SingleArrowShape.prototype.arrowWidth)))); + var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', SingleArrowShape.prototype.arrowSize)))); + var at = (h - aw) / 2; + var ab = at + aw; + + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, h / 2), new mxPoint(as, 0), new mxPoint(as, at), new mxPoint(w - as, at), + new mxPoint(w - as, 0), new mxPoint(w, h / 2), new mxPoint(w - as, h), + new mxPoint(w - as, ab), new mxPoint(as, ab), new mxPoint(as, h)], + this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('doubleArrow', DoubleArrowShape); + + // Data storage + function DataStorageShape() + { + mxActor.call(this); + }; + mxUtils.extend(DataStorageShape, mxActor); + DataStorageShape.prototype.size = 0.1; + DataStorageShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + + c.moveTo(s, 0); + c.lineTo(w, 0); + c.quadTo(w - s * 2, h / 2, w, h); + c.lineTo(s, h); + c.quadTo(s - s * 2, h / 2, s, 0); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('dataStorage', DataStorageShape); + + // Or + function OrShape() + { + mxActor.call(this); + }; + mxUtils.extend(OrShape, mxActor); + OrShape.prototype.redrawPath = function(c, x, y, w, h) + { + c.moveTo(0, 0); + c.quadTo(w, 0, w, h / 2); + c.quadTo(w, h, 0, h); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('or', OrShape); + + // Xor + function XorShape() + { + mxActor.call(this); + }; + mxUtils.extend(XorShape, mxActor); + XorShape.prototype.redrawPath = function(c, x, y, w, h) + { + c.moveTo(0, 0); + c.quadTo(w, 0, w, h / 2); + c.quadTo(w, h, 0, h); + c.quadTo(w / 2, h / 2, 0, 0); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('xor', XorShape); + + // Loop limit + function LoopLimitShape() + { + mxActor.call(this); + }; + mxUtils.extend(LoopLimitShape, mxActor); + LoopLimitShape.prototype.size = 20; + LoopLimitShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w - s, 0), new mxPoint(w, s * 0.8), new mxPoint(w, h), + new mxPoint(0, h), new mxPoint(0, s * 0.8)], this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('loopLimit', LoopLimitShape); + + // Off page connector + function OffPageConnectorShape() + { + mxActor.call(this); + }; + mxUtils.extend(OffPageConnectorShape, mxActor); + OffPageConnectorShape.prototype.size = 3 / 8; + OffPageConnectorShape.prototype.redrawPath = function(c, x, y, w, h) + { + var s = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, h - s), new mxPoint(w / 2, h), + new mxPoint(0, h - s)], this.isRounded, arcSize, true); + c.end(); + }; + + mxCellRenderer.registerShape('offPageConnector', OffPageConnectorShape); + + // Internal storage + function TapeDataShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(TapeDataShape, mxEllipse); + TapeDataShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxEllipse.prototype.paintVertexShape.apply(this, arguments); + + c.begin(); + c.moveTo(x + w / 2, y + h); + c.lineTo(x + w, y + h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('tapeData', TapeDataShape); + + // OrEllipseShape + function OrEllipseShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(OrEllipseShape, mxEllipse); + OrEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxEllipse.prototype.paintVertexShape.apply(this, arguments); + + c.setShadow(false); + c.begin(); + c.moveTo(x, y + h / 2); + c.lineTo(x + w, y + h / 2); + c.end(); + c.stroke(); + + c.begin(); + c.moveTo(x + w / 2, y); + c.lineTo(x + w / 2, y + h); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('orEllipse', OrEllipseShape); + + // SumEllipseShape + function SumEllipseShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(SumEllipseShape, mxEllipse); + SumEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxEllipse.prototype.paintVertexShape.apply(this, arguments); + var s2 = 0.145; + + c.setShadow(false); + c.begin(); + c.moveTo(x + w * s2, y + h * s2); + c.lineTo(x + w * (1 - s2), y + h * (1 - s2)); + c.end(); + c.stroke(); + + c.begin(); + c.moveTo(x + w * (1 - s2), y + h * s2); + c.lineTo(x + w * s2, y + h * (1 - s2)); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('sumEllipse', SumEllipseShape); + + // SortShape + function SortShape() + { + mxRhombus.call(this); + }; + mxUtils.extend(SortShape, mxRhombus); + SortShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxRhombus.prototype.paintVertexShape.apply(this, arguments); + + c.setShadow(false); + c.begin(); + c.moveTo(x, y + h / 2); + c.lineTo(x + w, y + h / 2); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('sortShape', SortShape); + + // CollateShape + function CollateShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(CollateShape, mxEllipse); + CollateShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + c.begin(); + c.moveTo(x, y); + c.lineTo(x + w, y); + c.lineTo(x + w / 2, y + h / 2); + c.close(); + c.fillAndStroke(); + + c.begin(); + c.moveTo(x, y + h); + c.lineTo(x + w, y + h); + c.lineTo(x + w / 2, y + h / 2); + c.close(); + c.fillAndStroke(); + }; + + mxCellRenderer.registerShape('collate', CollateShape); + + // DimensionShape + function DimensionShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(DimensionShape, mxEllipse); + DimensionShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + // Arrow size + var al = 10; + var cy = y + h - al / 2; + + c.begin(); + c.moveTo(x, y); + c.lineTo(x, y + h); + c.moveTo(x, cy); + c.lineTo(x + al, cy - al / 2); + c.moveTo(x, cy); + c.lineTo(x + al, cy + al / 2); + c.moveTo(x, cy); + c.lineTo(x + w, cy); + + // Opposite side + c.moveTo(x + w, y); + c.lineTo(x + w, y + h); + c.moveTo(x + w, cy); + c.lineTo(x + w - al, cy - al / 2); + c.moveTo(x + w, cy); + c.lineTo(x + w - al, cy + al / 2); + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('dimension', DimensionShape); + + // PartialRectangleShape + function PartialRectangleShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(PartialRectangleShape, mxEllipse); + PartialRectangleShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + if (!this.outline) + { + c.setStrokeColor(null); + } + + mxRectangleShape.prototype.paintBackground.apply(this, arguments); + + if (this.style != null) + { + c.setStrokeColor(this.stroke); + c.rect(x, y, w, h); + c.fill(); + + c.begin(); + c.moveTo(x, y); + + if (mxUtils.getValue(this.style, 'top', '1') == '1') + { + c.lineTo(x + w, y); + } + else + { + c.moveTo(x + w, y); + } + + if (mxUtils.getValue(this.style, 'right', '1') == '1') + { + c.lineTo(x + w, y + h); + } + else + { + c.moveTo(x + w, y + h); + } + + if (mxUtils.getValue(this.style, 'bottom', '1') == '1') + { + c.lineTo(x, y + h); + } + else + { + c.moveTo(x, y + h); + } + + if (mxUtils.getValue(this.style, 'left', '1') == '1') + { + c.lineTo(x, y - this.strokewidth / 2); + } + + c.end(); + c.stroke(); + } + }; + + mxCellRenderer.registerShape('partialRectangle', PartialRectangleShape); + + // LineEllipseShape + function LineEllipseShape() + { + mxEllipse.call(this); + }; + mxUtils.extend(LineEllipseShape, mxEllipse); + LineEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) + { + mxEllipse.prototype.paintVertexShape.apply(this, arguments); + + c.setShadow(false); + c.begin(); + + if (mxUtils.getValue(this.style, 'line') == 'vertical') + { + c.moveTo(x + w / 2, y); + c.lineTo(x + w / 2, y + h); + } + else + { + c.moveTo(x, y + h / 2); + c.lineTo(x + w, y + h / 2); + } + + c.end(); + c.stroke(); + }; + + mxCellRenderer.registerShape('lineEllipse', LineEllipseShape); + + // Delay + function DelayShape() + { + mxActor.call(this); + }; + mxUtils.extend(DelayShape, mxActor); + DelayShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = Math.min(w, h / 2); + c.moveTo(0, 0); + c.lineTo(w - dx, 0); + c.quadTo(w, 0, w, h / 2); + c.quadTo(w, h, w - dx, h); + c.lineTo(0, h); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('delay', DelayShape); + + // Cross Shape + function CrossShape() + { + mxActor.call(this); + }; + mxUtils.extend(CrossShape, mxActor); + CrossShape.prototype.size = 0.2; + CrossShape.prototype.redrawPath = function(c, x, y, w, h) + { + var m = Math.min(h, w); + var size = Math.max(0, Math.min(m, m * parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); + var t = (h - size) / 2; + var b = t + size; + var l = (w - size) / 2; + var r = l + size; + + c.moveTo(0, t); + c.lineTo(l, t); + c.lineTo(l, 0); + c.lineTo(r, 0); + c.lineTo(r, t); + c.lineTo(w, t); + c.lineTo(w, b); + c.lineTo(r, b); + c.lineTo(r, h); + c.lineTo(l, h); + c.lineTo(l, b); + c.lineTo(0, b); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('cross', CrossShape); + + // Display + function DisplayShape() + { + mxActor.call(this); + }; + mxUtils.extend(DisplayShape, mxActor); + DisplayShape.prototype.size = 0.25; + DisplayShape.prototype.redrawPath = function(c, x, y, w, h) + { + var dx = Math.min(w, h / 2); + var s = Math.min(w - dx, Math.max(0, parseFloat(mxUtils.getValue(this.style, 'size', this.size))) * w); + + c.moveTo(0, h / 2); + c.lineTo(s, 0); + c.lineTo(w - dx, 0); + c.quadTo(w, 0, w, h / 2); + c.quadTo(w, h, w - dx, h); + c.lineTo(s, h); + c.close(); + c.end(); + }; + + mxCellRenderer.registerShape('display', DisplayShape); + + // FilledEdge shape + function FilledEdge() + { + mxConnector.call(this); + }; + mxUtils.extend(FilledEdge, mxConnector); + + FilledEdge.prototype.origPaintEdgeShape = FilledEdge.prototype.paintEdgeShape; + FilledEdge.prototype.paintEdgeShape = function(c, pts, rounded) + { + // Markers modify incoming points array + var temp = []; + + for (var i = 0; i < pts.length; i++) + { + temp.push(mxUtils.clone(pts[i])); + } + + // paintEdgeShape resets dashed to false + var dashed = c.state.dashed; + var fixDash = c.state.fixDash; + FilledEdge.prototype.origPaintEdgeShape.apply(this, [c, temp, rounded]); + + if (c.state.strokeWidth >= 3) + { + var fillClr = mxUtils.getValue(this.style, 'fillColor', null); + + if (fillClr != null) + { + c.setStrokeColor(fillClr); + c.setStrokeWidth(c.state.strokeWidth - 2); + c.setDashed(dashed, fixDash); + + FilledEdge.prototype.origPaintEdgeShape.apply(this, [c, pts, rounded]); + } + } + }; + + // Registers the link shape + mxCellRenderer.registerShape('filledEdge', FilledEdge); + + // Implements custom colors for shapes + if (typeof StyleFormatPanel !== 'undefined') + { + (function() + { + var styleFormatPanelGetCustomColors = StyleFormatPanel.prototype.getCustomColors; + + StyleFormatPanel.prototype.getCustomColors = function() + { + var ss = this.format.getSelectionState(); + var result = styleFormatPanelGetCustomColors.apply(this, arguments); + + if (ss.style.shape == 'umlFrame') + { + result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); + } + + return result; + }; + })(); + } + + // Registers and defines the custom marker + mxMarker.addMarker('dash', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + var nx = unitX * (size + sw + 1); + var ny = unitY * (size + sw + 1); + + return function() + { + c.begin(); + c.moveTo(pe.x - nx / 2 - ny / 2, pe.y - ny / 2 + nx / 2); + c.lineTo(pe.x + ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 - nx / 2); + c.stroke(); + }; + }); + + // Registers and defines the custom marker + mxMarker.addMarker('cross', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + var nx = unitX * (size + sw + 1); + var ny = unitY * (size + sw + 1); + + return function() + { + c.begin(); + c.moveTo(pe.x - nx / 2 - ny / 2, pe.y - ny / 2 + nx / 2); + c.lineTo(pe.x + ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 - nx / 2); + c.moveTo(pe.x - nx / 2 + ny / 2, pe.y - ny / 2 - nx / 2); + c.lineTo(pe.x - ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 + nx / 2); + c.stroke(); + }; + }); + + function circleMarker(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + var a = size / 2; + var size = size + sw; + + var pt = pe.clone(); + + pe.x -= unitX * (2 * size + sw); + pe.y -= unitY * (2 * size + sw); + + unitX = unitX * (size + sw); + unitY = unitY * (size + sw); + + return function() + { + c.ellipse(pt.x - unitX - size, pt.y - unitY - size, 2 * size, 2 * size); + + if (filled) + { + c.fillAndStroke(); + } + else + { + c.stroke(); + } + }; + }; + + mxMarker.addMarker('circle', circleMarker); + mxMarker.addMarker('circlePlus', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + var pt = pe.clone(); + var fn = circleMarker.apply(this, arguments); + var nx = unitX * (size + 2 * sw); // (size + sw + 1); + var ny = unitY * (size + 2 * sw); //(size + sw + 1); + + return function() + { + fn.apply(this, arguments); + + c.begin(); + c.moveTo(pt.x - unitX * (sw), pt.y - unitY * (sw)); + c.lineTo(pt.x - 2 * nx + unitX * (sw), pt.y - 2 * ny + unitY * (sw)); + c.moveTo(pt.x - nx - ny + unitY * sw, pt.y - ny + nx - unitX * sw); + c.lineTo(pt.x + ny - nx - unitY * sw, pt.y - ny - nx + unitX * sw); + c.stroke(); + }; + }); + + mxMarker.addMarker('async', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + // The angle of the forward facing arrow sides against the x axis is + // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for + // only half the strokewidth is processed ). + var endOffsetX = unitX * sw * 1.118; + var endOffsetY = unitY * sw * 1.118; + + unitX = unitX * (size + sw); + unitY = unitY * (size + sw); + + var pt = pe.clone(); + pt.x -= endOffsetX; + pt.y -= endOffsetY; + + var f = 1; + pe.x += -unitX * f - endOffsetX; + pe.y += -unitY * f - endOffsetY; + + return function() + { + c.begin(); + c.moveTo(pt.x, pt.y); + + if (source) + { + c.lineTo(pt.x - unitX - unitY / 2, pt.y - unitY + unitX / 2); + } + else + { + c.lineTo(pt.x + unitY / 2 - unitX, pt.y - unitY - unitX / 2); + } + + c.lineTo(pt.x - unitX, pt.y - unitY); + c.close(); + + if (filled) + { + c.fillAndStroke(); + } + else + { + c.stroke(); + } + }; + }); + + function createOpenAsyncArrow(widthFactor) + { + widthFactor = (widthFactor != null) ? widthFactor : 2; + + return function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + unitX = unitX * (size + sw); + unitY = unitY * (size + sw); + + var pt = pe.clone(); + + return function() + { + c.begin(); + c.moveTo(pt.x, pt.y); + + if (source) + { + c.lineTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor); + } + else + { + c.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor); + } + + c.stroke(); + }; + } + }; + + mxMarker.addMarker('openAsync', createOpenAsyncArrow(2)); + + function arrow(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) + { + // The angle of the forward facing arrow sides against the x axis is + // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for + // only half the strokewidth is processed ). + var endOffsetX = unitX * sw * 1.118; + var endOffsetY = unitY * sw * 1.118; + + unitX = unitX * (size + sw); + unitY = unitY * (size + sw); + + var pt = pe.clone(); + pt.x -= endOffsetX; + pt.y -= endOffsetY; + + var f = (type != mxConstants.ARROW_CLASSIC && type != mxConstants.ARROW_CLASSIC_THIN) ? 1 : 3 / 4; + pe.x += -unitX * f - endOffsetX; + pe.y += -unitY * f - endOffsetY; + + return function() + { + canvas.begin(); + canvas.moveTo(pt.x, pt.y); + canvas.lineTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor); + + if (type == mxConstants.ARROW_CLASSIC || type == mxConstants.ARROW_CLASSIC_THIN) + { + canvas.lineTo(pt.x - unitX * 3 / 4, pt.y - unitY * 3 / 4); + } + + canvas.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor); + canvas.close(); + + if (filled) + { + canvas.fillAndStroke(); + } + else + { + canvas.stroke(); + } + }; + } + + // Handlers are only added if mxVertexHandler is defined (ie. not in embedded graph) + if (typeof mxVertexHandler !== 'undefined') + { + function createHandle(state, keys, getPositionFn, setPositionFn, ignoreGrid, redrawEdges) + { + var handle = new mxHandle(state, null, mxVertexHandler.prototype.secondaryHandleImage); + + handle.execute = function() + { + for (var i = 0; i < keys.length; i++) + { + this.copyStyle(keys[i]); + } + }; + + handle.getPosition = getPositionFn; + handle.setPosition = setPositionFn; + handle.ignoreGrid = (ignoreGrid != null) ? ignoreGrid : true; + + // Overridden to update connected edges + if (redrawEdges) + { + var positionChanged = handle.positionChanged; + + handle.positionChanged = function() + { + positionChanged.apply(this, arguments); + + // Redraws connected edges TODO: Include child edges + state.view.invalidate(this.state.cell); + state.view.validate(); + }; + } + + return handle; + }; + + function createArcHandle(state, yOffset) + { + return createHandle(state, [mxConstants.STYLE_ARCSIZE], function(bounds) + { + var tmp = (yOffset != null) ? yOffset : bounds.height / 8; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') + { + var arcSize = mxUtils.getValue(state.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; + + return new mxPoint(bounds.x + bounds.width - Math.min(bounds.width / 2, arcSize), bounds.y + tmp); + } + else + { + var arcSize = Math.max(0, parseFloat(mxUtils.getValue(state.style, + mxConstants.STYLE_ARCSIZE, mxConstants.RECTANGLE_ROUNDING_FACTOR * 100))) / 100; + + return new mxPoint(bounds.x + bounds.width - Math.min(Math.max(bounds.width / 2, bounds.height / 2), + Math.min(bounds.width, bounds.height) * arcSize), bounds.y + tmp); + } + }, function(bounds, pt, me) + { + if (mxUtils.getValue(state.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') + { + this.state.style[mxConstants.STYLE_ARCSIZE] = Math.round(Math.max(0, Math.min(bounds.width, + (bounds.x + bounds.width - pt.x) * 2))); + } + else + { + var f = Math.min(50, Math.max(0, (bounds.width - pt.x + bounds.x) * 100 / + Math.min(bounds.width, bounds.height))); + this.state.style[mxConstants.STYLE_ARCSIZE] = Math.round(f); + } + }); + } + + function createArcHandleFunction() + { + return function(state) + { + var handles = []; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }; + }; + + function createTrapezoidHandleFunction(max) + { + return function(state) + { + var handles = [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(max, parseFloat(mxUtils.getValue(this.state.style, 'size', TrapezoidShape.prototype.size)))); + + return new mxPoint(bounds.x + size * bounds.width * 0.75, bounds.y + bounds.height / 4); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(max, (pt.x - bounds.x) / (bounds.width * 0.75))); + }, null, true)]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }; + }; + + function createDisplayHandleFunction(defaultValue, allowArcHandle, max, redrawEdges, fixedDefaultValue) + { + max = (max != null) ? max : 1; + + return function(state) + { + var handles = [createHandle(state, ['size'], function(bounds) + { + var fixed = (fixedDefaultValue != null) ? mxUtils.getValue(this.state.style, 'fixedSize', '0') != '0' : null; + var size = parseFloat(mxUtils.getValue(this.state.style, 'size', (fixed) ? fixedDefaultValue : defaultValue)); + + return new mxPoint(bounds.x + Math.max(0, Math.min(bounds.width, size * ((fixed) ? 1 : bounds.width))), bounds.getCenterY()); + }, function(bounds, pt, me) + { + var fixed = (fixedDefaultValue != null) ? mxUtils.getValue(this.state.style, 'fixedSize', '0') != '0' : null; + var size = (fixed) ? (pt.x - bounds.x) : Math.max(0, Math.min(max, (pt.x - bounds.x) / bounds.width)); + + if (fixed && !mxEvent.isAltDown(me.getEvent())) + { + size = state.view.graph.snap(size); + } + + this.state.style['size'] = size; + }, null, redrawEdges)]; + + if (allowArcHandle && mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }; + }; + + function createCubeHandleFunction(factor, defaultValue, allowArcHandle) + { + return function(state) + { + var handles = [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.width, Math.min(bounds.height, parseFloat( + mxUtils.getValue(this.state.style, 'size', defaultValue))))) * factor; + + return new mxPoint(bounds.x + size, bounds.y + size); + }, function(bounds, pt) + { + this.state.style['size'] = Math.round(Math.max(0, Math.min(Math.min(bounds.width, pt.x - bounds.x), + Math.min(bounds.height, pt.y - bounds.y))) / factor); + })]; + + if (allowArcHandle && mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }; + }; + + function createArrowHandleFunction(maxSize) + { + return function(state) + { + return [createHandle(state, ['arrowWidth', 'arrowSize'], function(bounds) + { + var aw = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'arrowWidth', SingleArrowShape.prototype.arrowWidth))); + var as = Math.max(0, Math.min(maxSize, mxUtils.getValue(this.state.style, 'arrowSize', SingleArrowShape.prototype.arrowSize))); + + return new mxPoint(bounds.x + (1 - as) * bounds.width, bounds.y + (1 - aw) * bounds.height / 2); + }, function(bounds, pt) + { + this.state.style['arrowWidth'] = Math.max(0, Math.min(1, Math.abs(bounds.y + bounds.height / 2 - pt.y) / bounds.height * 2)); + this.state.style['arrowSize'] = Math.max(0, Math.min(maxSize, (bounds.x + bounds.width - pt.x) / (bounds.width))); + })]; + }; + }; + + function createEdgeHandle(state, keys, start, getPosition, setPosition) + { + return createHandle(state, keys, function(bounds) + { + var pts = state.absolutePoints; + var n = pts.length - 1; + + var tr = state.view.translate; + var s = state.view.scale; + + var p0 = (start) ? pts[0] : pts[n]; + var p1 = (start) ? pts[1] : pts[n - 1]; + var dx = (start) ? p1.x - p0.x : p1.x - p0.x; + var dy = (start) ? p1.y - p0.y : p1.y - p0.y; + + var dist = Math.sqrt(dx * dx + dy * dy); + + var pt = getPosition.call(this, dist, dx / dist, dy / dist, p0, p1); + + return new mxPoint(pt.x / s - tr.x, pt.y / s - tr.y); + }, function(bounds, pt, me) + { + var pts = state.absolutePoints; + var n = pts.length - 1; + + var tr = state.view.translate; + var s = state.view.scale; + + var p0 = (start) ? pts[0] : pts[n]; + var p1 = (start) ? pts[1] : pts[n - 1]; + var dx = (start) ? p1.x - p0.x : p1.x - p0.x; + var dy = (start) ? p1.y - p0.y : p1.y - p0.y; + + var dist = Math.sqrt(dx * dx + dy * dy); + pt.x = (pt.x + tr.x) * s; + pt.y = (pt.y + tr.y) * s; + + setPosition.call(this, dist, dx / dist, dy / dist, p0, p1, pt, me); + }); + }; + + function createEdgeWidthHandle(state, start, spacing) + { + return createEdgeHandle(state, ['width'], start, function(dist, nx, ny, p0, p1) + { + var w = state.shape.getEdgeWidth() * state.view.scale + spacing; + + return new mxPoint(p0.x + nx * dist / 4 + ny * w / 2, p0.y + ny * dist / 4 - nx * w / 2); + }, function(dist, nx, ny, p0, p1, pt) + { + var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); + state.style['width'] = Math.round(w * 2) / state.view.scale - spacing; + }); + }; + + function ptLineDistance(x1, y1, x2, y2, x0, y0) + { + return Math.abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)); + } + + var handleFactory = { + 'link': function(state) + { + var spacing = 10; + + return [createEdgeWidthHandle(state, true, spacing), createEdgeWidthHandle(state, false, spacing)]; + }, + 'flexArrow': function(state) + { + // Do not use state.shape.startSize/endSize since it is cached + var tol = state.view.graph.gridSize / state.view.scale; + var handles = []; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_STARTARROW, mxConstants.NONE) != mxConstants.NONE) + { + handles.push(createEdgeHandle(state, ['width', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], true, function(dist, nx, ny, p0, p1) + { + var w = (state.shape.getEdgeWidth() - state.shape.strokewidth) * state.view.scale; + var l = mxUtils.getNumber(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; + + return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) + ny * w / 2, + p0.y + ny * (l + state.shape.strokewidth * state.view.scale) - nx * w / 2); + }, function(dist, nx, ny, p0, p1, pt, me) + { + var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); + var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); + + state.style[mxConstants.STYLE_STARTSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; + state.style['width'] = Math.round(w * 2) / state.view.scale; + + // Applies to opposite side + if (mxEvent.isControlDown(me.getEvent())) + { + state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; + } + + // Snaps to end geometry + if (!mxEvent.isAltDown(me.getEvent())) + { + if (Math.abs(parseFloat(state.style[mxConstants.STYLE_STARTSIZE]) - parseFloat(state.style[mxConstants.STYLE_ENDSIZE])) < tol / 6) + { + state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; + } + } + })); + + handles.push(createEdgeHandle(state, ['startWidth', 'endWidth', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], true, function(dist, nx, ny, p0, p1) + { + var w = (state.shape.getStartArrowWidth() - state.shape.strokewidth) * state.view.scale; + var l = mxUtils.getNumber(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; + + return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) + ny * w / 2, + p0.y + ny * (l + state.shape.strokewidth * state.view.scale) - nx * w / 2); + }, function(dist, nx, ny, p0, p1, pt, me) + { + var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); + var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); + + state.style[mxConstants.STYLE_STARTSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; + state.style['startWidth'] = Math.max(0, Math.round(w * 2) - state.shape.getEdgeWidth()) / state.view.scale; + + // Applies to opposite side + if (mxEvent.isControlDown(me.getEvent())) + { + state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; + state.style['endWidth'] = state.style['startWidth']; + } + + // Snaps to endWidth + if (!mxEvent.isAltDown(me.getEvent())) + { + if (Math.abs(parseFloat(state.style[mxConstants.STYLE_STARTSIZE]) - parseFloat(state.style[mxConstants.STYLE_ENDSIZE])) < tol / 6) + { + state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; + } + + if (Math.abs(parseFloat(state.style['startWidth']) - parseFloat(state.style['endWidth'])) < tol) + { + state.style['startWidth'] = state.style['endWidth']; + } + } + })); + } + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ENDARROW, mxConstants.NONE) != mxConstants.NONE) + { + handles.push(createEdgeHandle(state, ['width', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], false, function(dist, nx, ny, p0, p1) + { + var w = (state.shape.getEdgeWidth() - state.shape.strokewidth) * state.view.scale; + var l = mxUtils.getNumber(state.style, mxConstants.STYLE_ENDSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; + + return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) - ny * w / 2, + p0.y + ny * (l + state.shape.strokewidth * state.view.scale) + nx * w / 2); + }, function(dist, nx, ny, p0, p1, pt, me) + { + var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); + var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); + + state.style[mxConstants.STYLE_ENDSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; + state.style['width'] = Math.round(w * 2) / state.view.scale; + + // Applies to opposite side + if (mxEvent.isControlDown(me.getEvent())) + { + state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; + } + + // Snaps to start geometry + if (!mxEvent.isAltDown(me.getEvent())) + { + if (Math.abs(parseFloat(state.style[mxConstants.STYLE_ENDSIZE]) - parseFloat(state.style[mxConstants.STYLE_STARTSIZE])) < tol / 6) + { + state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; + } + } + })); + + handles.push(createEdgeHandle(state, ['startWidth', 'endWidth', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], false, function(dist, nx, ny, p0, p1) + { + var w = (state.shape.getEndArrowWidth() - state.shape.strokewidth) * state.view.scale; + var l = mxUtils.getNumber(state.style, mxConstants.STYLE_ENDSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; + + return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) - ny * w / 2, + p0.y + ny * (l + state.shape.strokewidth * state.view.scale) + nx * w / 2); + }, function(dist, nx, ny, p0, p1, pt, me) + { + var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); + var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); + + state.style[mxConstants.STYLE_ENDSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; + state.style['endWidth'] = Math.max(0, Math.round(w * 2) - state.shape.getEdgeWidth()) / state.view.scale; + + // Applies to opposite side + if (mxEvent.isControlDown(me.getEvent())) + { + state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; + state.style['startWidth'] = state.style['endWidth']; + } + + // Snaps to start geometry + if (!mxEvent.isAltDown(me.getEvent())) + { + if (Math.abs(parseFloat(state.style[mxConstants.STYLE_ENDSIZE]) - parseFloat(state.style[mxConstants.STYLE_STARTSIZE])) < tol / 6) + { + state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; + } + + if (Math.abs(parseFloat(state.style['endWidth']) - parseFloat(state.style['startWidth'])) < tol) + { + state.style['endWidth'] = state.style['startWidth']; + } + } + })); + } + + return handles; + }, + 'swimlane': function(state) + { + var handles = [createHandle(state, [mxConstants.STYLE_STARTSIZE], function(bounds) + { + var size = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); + + if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, 1) == 1) + { + return new mxPoint(bounds.getCenterX(), bounds.y + Math.max(0, Math.min(bounds.height, size))); + } + else + { + return new mxPoint(bounds.x + Math.max(0, Math.min(bounds.width, size)), bounds.getCenterY()); + } + }, function(bounds, pt) + { + state.style[mxConstants.STYLE_STARTSIZE] = + (mxUtils.getValue(this.state.style, mxConstants.STYLE_HORIZONTAL, 1) == 1) ? + Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))) : + Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); + })]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED)) + { + var size = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); + handles.push(createArcHandle(state, size / 2)); + } + + return handles; + }, + 'label': createArcHandleFunction(), + 'ext': createArcHandleFunction(), + 'rectangle': createArcHandleFunction(), + 'triangle': createArcHandleFunction(), + 'rhombus': createArcHandleFunction(), + 'umlLifeline': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.height, parseFloat(mxUtils.getValue(this.state.style, 'size', UmlLifeline.prototype.size)))); + + return new mxPoint(bounds.getCenterX(), bounds.y + size); + }, function(bounds, pt) + { + this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); + }, false)]; + }, + 'umlFrame': function(state) + { + var handles = [createHandle(state, ['width', 'height'], function(bounds) + { + var w0 = Math.max(UmlFrame.prototype.corner, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'width', UmlFrame.prototype.width))); + var h0 = Math.max(UmlFrame.prototype.corner * 1.5, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'height', UmlFrame.prototype.height))); + + return new mxPoint(bounds.x + w0, bounds.y + h0); + }, function(bounds, pt) + { + this.state.style['width'] = Math.round(Math.max(UmlFrame.prototype.corner, Math.min(bounds.width, pt.x - bounds.x))); + this.state.style['height'] = Math.round(Math.max(UmlFrame.prototype.corner * 1.5, Math.min(bounds.height, pt.y - bounds.y))); + }, false)]; + + return handles; + }, + 'process': function(state) + { + var handles = [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(0.5, parseFloat(mxUtils.getValue(this.state.style, 'size', ProcessShape.prototype.size)))); + + return new mxPoint(bounds.x + bounds.width * size, bounds.y + bounds.height / 4); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(0.5, (pt.x - bounds.x) / bounds.width)); + })]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }, + 'cross': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var m = Math.min(bounds.width, bounds.height); + var size = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'size', CrossShape.prototype.size))) * m / 2; + + return new mxPoint(bounds.getCenterX() - size, bounds.getCenterY() - size); + }, function(bounds, pt) + { + var m = Math.min(bounds.width, bounds.height); + this.state.style['size'] = Math.max(0, Math.min(1, Math.min((Math.max(0, bounds.getCenterY() - pt.y) / m) * 2, + (Math.max(0, bounds.getCenterX() - pt.x) / m) * 2))); + })]; + }, + 'note': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.width, Math.min(bounds.height, parseFloat( + mxUtils.getValue(this.state.style, 'size', NoteShape.prototype.size))))); + + return new mxPoint(bounds.x + bounds.width - size, bounds.y + size); + }, function(bounds, pt) + { + this.state.style['size'] = Math.round(Math.max(0, Math.min(Math.min(bounds.width, bounds.x + bounds.width - pt.x), + Math.min(bounds.height, pt.y - bounds.y)))); + })]; + }, + 'manualInput': function(state) + { + var handles = [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', ManualInputShape.prototype.size))); + + return new mxPoint(bounds.x + bounds.width / 4, bounds.y + size * 3 / 4); + }, function(bounds, pt) + { + this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, (pt.y - bounds.y) * 4 / 3))); + })]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }, + 'dataStorage': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', DataStorageShape.prototype.size)))); + + return new mxPoint(bounds.x + (1 - size) * bounds.width, bounds.getCenterY()); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(1, (bounds.x + bounds.width - pt.x) / bounds.width)); + })]; + }, + 'callout': function(state) + { + var handles = [createHandle(state, ['size', 'position'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', CalloutShape.prototype.size))); + var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); + var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); + + return new mxPoint(bounds.x + position * bounds.width, bounds.y + bounds.height - size); + }, function(bounds, pt) + { + var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); + this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, bounds.y + bounds.height - pt.y))); + this.state.style['position'] = Math.round(Math.max(0, Math.min(1, (pt.x - bounds.x) / bounds.width)) * 100) / 100; + }), createHandle(state, ['position2'], function(bounds) + { + var position2 = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position2', CalloutShape.prototype.position2))); + + return new mxPoint(bounds.x + position2 * bounds.width, bounds.y + bounds.height); + }, function(bounds, pt) + { + this.state.style['position2'] = Math.round(Math.max(0, Math.min(1, (pt.x - bounds.x) / bounds.width)) * 100) / 100; + }), createHandle(state, ['base'], function(bounds) + { + var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', CalloutShape.prototype.size))); + var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); + var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); + + return new mxPoint(bounds.x + Math.min(bounds.width, position * bounds.width + base), bounds.y + bounds.height - size); + }, function(bounds, pt) + { + var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); + + this.state.style['base'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x - position * bounds.width))); + })]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }, + 'internalStorage': function(state) + { + var handles = [createHandle(state, ['dx', 'dy'], function(bounds) + { + var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', InternalStorageShape.prototype.dx))); + var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', InternalStorageShape.prototype.dy))); + + return new mxPoint(bounds.x + dx, bounds.y + dy); + }, function(bounds, pt) + { + this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); + this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); + })]; + + if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) + { + handles.push(createArcHandle(state)); + } + + return handles; + }, + 'corner': function(state) + { + return [createHandle(state, ['dx', 'dy'], function(bounds) + { + var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', CornerShape.prototype.dx))); + var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', CornerShape.prototype.dy))); + + return new mxPoint(bounds.x + dx, bounds.y + dy); + }, function(bounds, pt) + { + this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); + this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); + })]; + }, + 'tee': function(state) + { + return [createHandle(state, ['dx', 'dy'], function(bounds) + { + var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', TeeShape.prototype.dx))); + var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', TeeShape.prototype.dy))); + + return new mxPoint(bounds.x + (bounds.width + dx) / 2, bounds.y + dy); + }, function(bounds, pt) + { + this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width / 2, (pt.x - bounds.x - bounds.width / 2)) * 2)); + this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); + })]; + }, + 'singleArrow': createArrowHandleFunction(1), + 'doubleArrow': createArrowHandleFunction(0.5), + 'folder': function(state) + { + return [createHandle(state, ['tabWidth', 'tabHeight'], function(bounds) + { + var tw = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'tabWidth', FolderShape.prototype.tabWidth))); + var th = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'tabHeight', FolderShape.prototype.tabHeight))); + + if (mxUtils.getValue(this.state.style, 'tabPosition', FolderShape.prototype.tabPosition) == mxConstants.ALIGN_RIGHT) + { + tw = bounds.width - tw; + } + + return new mxPoint(bounds.x + tw, bounds.y + th); + }, function(bounds, pt) + { + var tw = Math.max(0, Math.min(bounds.width, pt.x - bounds.x)); + + if (mxUtils.getValue(this.state.style, 'tabPosition', FolderShape.prototype.tabPosition) == mxConstants.ALIGN_RIGHT) + { + tw = bounds.width - tw; + } + + this.state.style['tabWidth'] = Math.round(tw); + this.state.style['tabHeight'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); + })]; + }, + 'document': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', DocumentShape.prototype.size)))); + + return new mxPoint(bounds.x + 3 * bounds.width / 4, bounds.y + (1 - size) * bounds.height); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(1, (bounds.y + bounds.height - pt.y) / bounds.height)); + })]; + }, + 'tape': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', TapeShape.prototype.size)))); + + return new mxPoint(bounds.getCenterX(), bounds.y + size * bounds.height / 2); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(1, ((pt.y - bounds.y) / bounds.height) * 2)); + })]; + }, + 'offPageConnector': function(state) + { + return [createHandle(state, ['size'], function(bounds) + { + var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', OffPageConnectorShape.prototype.size)))); + + return new mxPoint(bounds.getCenterX(), bounds.y + (1 - size) * bounds.height); + }, function(bounds, pt) + { + this.state.style['size'] = Math.max(0, Math.min(1, (bounds.y + bounds.height - pt.y) / bounds.height)); + })]; + }, + 'step': createDisplayHandleFunction(StepShape.prototype.size, true, null, true, StepShape.prototype.fixedSize), + 'hexagon': createDisplayHandleFunction(HexagonShape.prototype.size, true, 0.5, true), + 'curlyBracket': createDisplayHandleFunction(CurlyBracketShape.prototype.size, false), + 'display': createDisplayHandleFunction(DisplayShape.prototype.size, false), + 'cube': createCubeHandleFunction(1, CubeShape.prototype.size, false), + 'card': createCubeHandleFunction(0.5, CardShape.prototype.size, true), + 'loopLimit': createCubeHandleFunction(0.5, LoopLimitShape.prototype.size, true), + 'trapezoid': createTrapezoidHandleFunction(0.5), + 'parallelogram': createTrapezoidHandleFunction(1) + }; + + // Exposes custom handles + Graph.createHandle = createHandle; + Graph.handleFactory = handleFactory; + + mxVertexHandler.prototype.createCustomHandles = function() + { + // Not rotatable means locked + if (this.state.view.graph.getSelectionCount() == 1) + { + if (this.graph.isCellRotatable(this.state.cell)) + // LATER: Make locked state independent of rotatable flag, fix toggle if default is false + //if (this.graph.isCellResizable(this.state.cell) || this.graph.isCellMovable(this.state.cell)) + { + var name = this.state.style['shape']; + + if (mxCellRenderer.defaultShapes[name] == null && + mxStencilRegistry.getStencil(name) == null) + { + name = mxConstants.SHAPE_RECTANGLE; + } + + var fn = handleFactory[name]; + + if (fn == null && this.state.shape != null && this.state.shape.isRoundable()) + { + fn = handleFactory[mxConstants.SHAPE_RECTANGLE]; + } + + if (fn != null) + { + return fn(this.state); + } + } + } + + return null; + }; + + mxEdgeHandler.prototype.createCustomHandles = function() + { + if (this.state.view.graph.getSelectionCount() == 1) + { + var name = this.state.style['shape']; + + if (mxCellRenderer.defaultShapes[name] == null && + mxStencilRegistry.getStencil(name) == null) + { + name = mxConstants.SHAPE_CONNECTOR; + } + + var fn = handleFactory[name]; + + if (fn != null) + { + return fn(this.state); + } + } + + return null; + } + } + else + { + // Dummy entries to avoid NPE in embed mode + Graph.createHandle = function() {}; + Graph.handleFactory = {}; + } + + var isoHVector = new mxPoint(1, 0); + var isoVVector = new mxPoint(1, 0); + + var alpha1 = mxUtils.toRadians(-30); + + var cos1 = Math.cos(alpha1); + var sin1 = Math.sin(alpha1); + + isoHVector = mxUtils.getRotatedPoint(isoHVector, cos1, sin1); + + var alpha2 = mxUtils.toRadians(-150); + + var cos2 = Math.cos(alpha2); + var sin2 = Math.sin(alpha2); + + isoVVector = mxUtils.getRotatedPoint(isoVVector, cos2, sin2); + + mxEdgeStyle.IsometricConnector = function (state, source, target, points, result) + { + var view = state.view; + var pt = (points != null && points.length > 0) ? points[0] : null; + var pts = state.absolutePoints; + var p0 = pts[0]; + var pe = pts[pts.length-1]; + + if (pt != null) + { + pt = view.transformControlPoint(state, pt); + } + + if (p0 == null) + { + if (source != null) + { + p0 = new mxPoint(source.getCenterX(), source.getCenterY()); + } + } + + if (pe == null) + { + if (target != null) + { + pe = new mxPoint(target.getCenterX(), target.getCenterY()); + } + } + + var a1 = isoHVector.x; + var a2 = isoHVector.y; + + var b1 = isoVVector.x; + var b2 = isoVVector.y; + + var elbow = mxUtils.getValue(state.style, 'elbow', 'horizontal') == 'horizontal'; + + if (pe != null && p0 != null) + { + var last = p0; + + function isoLineTo(x, y, ignoreFirst) + { + var c1 = x - last.x; + var c2 = y - last.y; + + // Solves for isometric base vectors + var h = (b2 * c1 - b1 * c2) / (a1 * b2 - a2 * b1); + var v = (a2 * c1 - a1 * c2) / (a2 * b1 - a1 * b2); + + if (elbow) + { + if (ignoreFirst) + { + last = new mxPoint(last.x + a1 * h, last.y + a2 * h); + result.push(last); + } + + last = new mxPoint(last.x + b1 * v, last.y + b2 * v); + result.push(last); + } + else + { + if (ignoreFirst) + { + last = new mxPoint(last.x + b1 * v, last.y + b2 * v); + result.push(last); + } + + last = new mxPoint(last.x + a1 * h, last.y + a2 * h); + result.push(last); + } + }; + + if (pt == null) + { + pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2); + } + + isoLineTo(pt.x, pt.y, true); + isoLineTo(pe.x, pe.y, false); + } + }; + + mxStyleRegistry.putValue('isometricEdgeStyle', mxEdgeStyle.IsometricConnector); + + var graphCreateEdgeHandler = Graph.prototype.createEdgeHandler; + Graph.prototype.createEdgeHandler = function(state, edgeStyle) + { + if (edgeStyle == mxEdgeStyle.IsometricConnector) + { + var handler = new mxElbowEdgeHandler(state); + handler.snapToTerminals = false; + + return handler; + } + + return graphCreateEdgeHandler.apply(this, arguments); + }; + + // Defines connection points for all shapes + IsoRectangleShape.prototype.constraints = []; + IsoCubeShape.prototype.constraints = []; + CalloutShape.prototype.constraints = []; + mxRectangleShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.75, 0), true), + new mxConnectionConstraint(new mxPoint(0, 0.25), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(1, 0.25), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.25, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; + mxEllipse.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), true), new mxConnectionConstraint(new mxPoint(1, 0), true), + new mxConnectionConstraint(new mxPoint(0, 1), true), new mxConnectionConstraint(new mxPoint(1, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.5))]; + mxLabel.prototype.constraints = mxRectangleShape.prototype.constraints; + mxImageShape.prototype.constraints = mxRectangleShape.prototype.constraints; + mxSwimlane.prototype.constraints = mxRectangleShape.prototype.constraints; + PlusShape.prototype.constraints = mxRectangleShape.prototype.constraints; + NoteShape.prototype.constraints = mxRectangleShape.prototype.constraints; + CardShape.prototype.constraints = mxRectangleShape.prototype.constraints; + CubeShape.prototype.constraints = mxRectangleShape.prototype.constraints; + FolderShape.prototype.constraints = mxRectangleShape.prototype.constraints; + InternalStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints; + DataStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints; + TapeDataShape.prototype.constraints = mxEllipse.prototype.constraints; + OrEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; + SumEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; + LineEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; + ManualInputShape.prototype.constraints = mxRectangleShape.prototype.constraints; + DelayShape.prototype.constraints = mxRectangleShape.prototype.constraints; + DisplayShape.prototype.constraints = mxRectangleShape.prototype.constraints; + LoopLimitShape.prototype.constraints = mxRectangleShape.prototype.constraints; + OffPageConnectorShape.prototype.constraints = mxRectangleShape.prototype.constraints; + mxCylinder.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.15, 0.05), false), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.85, 0.05), false), + new mxConnectionConstraint(new mxPoint(0, 0.3), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.7), true), + new mxConnectionConstraint(new mxPoint(1, 0.3), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.7), true), + new mxConnectionConstraint(new mxPoint(0.15, 0.95), false), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.85, 0.95), false)]; + UmlActorShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0.1), false), + new mxConnectionConstraint(new mxPoint(0.5, 0), false), + new mxConnectionConstraint(new mxPoint(0.75, 0.1), false), + new mxConnectionConstraint(new mxPoint(0, 1/3), false), + new mxConnectionConstraint(new mxPoint(0, 1), false), + new mxConnectionConstraint(new mxPoint(1, 1/3), false), + new mxConnectionConstraint(new mxPoint(1, 1), false), + new mxConnectionConstraint(new mxPoint(0.5, 0.5), false)]; + ComponentShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.75, 0), true), + new mxConnectionConstraint(new mxPoint(0, 0.3), true), + new mxConnectionConstraint(new mxPoint(0, 0.7), true), + new mxConnectionConstraint(new mxPoint(1, 0.25), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.25, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; + mxActor.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.25, 0.2), false), + new mxConnectionConstraint(new mxPoint(0.1, 0.5), false), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.75, 0.25), false), + new mxConnectionConstraint(new mxPoint(0.9, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.25, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; + SwitchShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), false), + new mxConnectionConstraint(new mxPoint(0.5, 0.25), false), + new mxConnectionConstraint(new mxPoint(1, 0), false), + new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), + new mxConnectionConstraint(new mxPoint(0, 1), false), + new mxConnectionConstraint(new mxPoint(0.5, 0.75), false), + new mxConnectionConstraint(new mxPoint(1, 1), false)]; + TapeShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.35), false), + new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(0, 0.65), false), + new mxConnectionConstraint(new mxPoint(1, 0.35), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.65), false), + new mxConnectionConstraint(new mxPoint(0.25, 1), false), + new mxConnectionConstraint(new mxPoint(0.75, 0), false)]; + StepShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.75, 0), true), + new mxConnectionConstraint(new mxPoint(0.25, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.75, 1), true), + new mxConnectionConstraint(new mxPoint(0, 0.25), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(1, 0.25), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.75), true)]; + mxLine.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; + LollipopShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), false), + new mxConnectionConstraint(new mxPoint(0.5, 1), false)]; + mxDoubleEllipse.prototype.constraints = mxEllipse.prototype.constraints; + mxRhombus.prototype.constraints = mxEllipse.prototype.constraints; + mxTriangle.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true)]; + mxHexagon.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.375, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.625, 0), true), + new mxConnectionConstraint(new mxPoint(0, 0.25), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(1, 0.25), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.75), true), + new mxConnectionConstraint(new mxPoint(0.375, 1), true), + new mxConnectionConstraint(new mxPoint(0.5, 1), true), + new mxConnectionConstraint(new mxPoint(0.625, 1), true)]; + mxCloud.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0.25), false), + new mxConnectionConstraint(new mxPoint(0.4, 0.1), false), + new mxConnectionConstraint(new mxPoint(0.16, 0.55), false), + new mxConnectionConstraint(new mxPoint(0.07, 0.4), false), + new mxConnectionConstraint(new mxPoint(0.31, 0.8), false), + new mxConnectionConstraint(new mxPoint(0.13, 0.77), false), + new mxConnectionConstraint(new mxPoint(0.8, 0.8), false), + new mxConnectionConstraint(new mxPoint(0.55, 0.95), false), + new mxConnectionConstraint(new mxPoint(0.875, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.96, 0.7), false), + new mxConnectionConstraint(new mxPoint(0.625, 0.2), false), + new mxConnectionConstraint(new mxPoint(0.88, 0.25), false)]; + ParallelogramShape.prototype.constraints = mxRectangleShape.prototype.constraints; + TrapezoidShape.prototype.constraints = mxRectangleShape.prototype.constraints; + DocumentShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), + new mxConnectionConstraint(new mxPoint(0.5, 0), true), + new mxConnectionConstraint(new mxPoint(0.75, 0), true), + new mxConnectionConstraint(new mxPoint(0, 0.25), true), + new mxConnectionConstraint(new mxPoint(0, 0.5), true), + new mxConnectionConstraint(new mxPoint(0, 0.75), true), + new mxConnectionConstraint(new mxPoint(1, 0.25), true), + new mxConnectionConstraint(new mxPoint(1, 0.5), true), + new mxConnectionConstraint(new mxPoint(1, 0.75), true)]; + mxArrow.prototype.constraints = null; + TeeShape.prototype.constraints = null; + CornerShape.prototype.constraints = null; + CrossbarShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), false), + new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(0, 1), false), + new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.5, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 1), false)]; + + SingleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; + DoubleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; + CrossShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.5, 0), false), + new mxConnectionConstraint(new mxPoint(0.5, 1), false)]; + UmlLifeline.prototype.constraints = null; + OrShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), false), + new mxConnectionConstraint(new mxPoint(0, 0.5), false), + new mxConnectionConstraint(new mxPoint(0, 0.75), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.7, 0.1), false), + new mxConnectionConstraint(new mxPoint(0.7, 0.9), false)]; + XorShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.175, 0.25), false), + new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.175, 0.75), false), + new mxConnectionConstraint(new mxPoint(1, 0.5), false), + new mxConnectionConstraint(new mxPoint(0.7, 0.1), false), + new mxConnectionConstraint(new mxPoint(0.7, 0.9), false)]; +})(); diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Sidebar.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Sidebar.js new file mode 100644 index 00000000..8f28f038 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Sidebar.js @@ -0,0 +1,3602 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Construcs a new sidebar for the given editor. + */ +function Sidebar(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; + this.palettes = new Object(); + this.taglist = new Object(); + this.showTooltips = true; + this.graph = editorUi.createTemporaryGraph(this.editorUi.editor.graph.getStylesheet()); + this.graph.cellRenderer.antiAlias = false; + this.graph.foldingEnabled = false; + this.graph.container.style.visibility = 'hidden'; + document.body.appendChild(this.graph.container); + + this.pointerUpHandler = mxUtils.bind(this, function() + { + this.showTooltips = true; + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', this.pointerUpHandler); + + this.pointerDownHandler = mxUtils.bind(this, function() + { + this.showTooltips = false; + this.hideTooltip(); + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', this.pointerDownHandler); + + this.pointerMoveHandler = mxUtils.bind(this, function(evt) + { + var src = mxEvent.getSource(evt); + + while (src != null) + { + if (src == this.currentElt) + { + return; + } + + src = src.parentNode; + } + + this.hideTooltip(); + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.pointerMoveHandler); + + // Handles mouse leaving the window + this.pointerOutHandler = mxUtils.bind(this, function(evt) + { + if (evt.toElement == null && evt.relatedTarget == null) + { + this.hideTooltip(); + } + }); + + mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerout' : 'mouseout', this.pointerOutHandler); + + // Enables tooltips after scroll + mxEvent.addListener(container, 'scroll', mxUtils.bind(this, function() + { + this.showTooltips = true; + this.hideTooltip(); + })); + + this.init(); + + // Pre-fetches tooltip image + if (!mxClient.IS_SVG) + { + new Image().src = IMAGE_PATH + '/tooltip.png'; + } +}; + +/** + * Adds all palettes to the sidebar. + */ +Sidebar.prototype.init = function() +{ + var dir = STENCIL_PATH; + + this.addSearchPalette(true); + this.addGeneralPalette(true); + this.addMiscPalette(false); + this.addAdvancedPalette(false); + this.addBasicPalette(dir); + this.addStencilPalette('arrows', mxResources.get('arrows'), dir + '/arrows.xml', + ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2'); + this.addUmlPalette(false); + this.addBpmnPalette(dir, false); + this.addStencilPalette('flowchart', 'Flowchart', dir + '/flowchart.xml', + ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2'); + this.addImagePalette('clipart', mxResources.get('clipart'), dir + '/clipart/', '_128x128.png', + ['Earth_globe', 'Empty_Folder', 'Full_Folder', 'Gear', 'Lock', 'Software', 'Virus', 'Email', + 'Database', 'Router_Icon', 'iPad', 'iMac', 'Laptop', 'MacBook', 'Monitor_Tower', 'Printer', + 'Server_Tower', 'Workstation', 'Firewall_02', 'Wireless_Router_N', 'Credit_Card', + 'Piggy_Bank', 'Graph', 'Safe', 'Shopping_Cart', 'Suit1', 'Suit2', 'Suit3', 'Pilot1', + 'Worker1', 'Soldier1', 'Doctor1', 'Tech1', 'Security1', 'Telesales1'], null, + {'Wireless_Router_N': 'wireless router switch wap wifi access point wlan', + 'Router_Icon': 'router switch'}); +}; + +/** + * Sets the default font size. + */ +Sidebar.prototype.collapsedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/collapsed.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAJmZmf///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNUQyRTJFNjZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNUQyRTJFNzZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFERjc3MEUxNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFERjc3MEUyNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhSMj6lrwAjcC1GyahV+dcZJgeIIFgA7'; + +/** + * Sets the default font size. + */ +Sidebar.prototype.expandedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/expanded.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAJmZmf///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxREY3NzBERjZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxREY3NzBFMDZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFERjc3MERENkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFERjc3MERFNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhGMj6nL3QAjVHIu6azbvPtWAAA7'; + +/** + * Sets the default font size. + */ +Sidebar.prototype.tooltipImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/tooltip.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAbCAMAAAB7jU7LAAAACVBMVEX///+ZmZn///9Y2COLAAAAA3RSTlP//wDXyg1BAAAAOElEQVR42mXQMQ4AMAgDsWv//+iutcJmIQSk+9dJpVKpVCqVSqVSqZTdncWzF8/NeP7FkxWenPEDOnUBiL3jWx0AAAAASUVORK5CYII='; + +/** + * + */ +Sidebar.prototype.searchImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/search.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAEaSURBVHjabNGxS5VxFIfxz71XaWuQUJCG/gCHhgTD9VpEETg4aMOlQRp0EoezObgcd220KQiXmpretTAHQRBdojlQEJyukPdt+b1ywfvAGc7wnHP4nlZd1yKijQW8xzNc4Su+ZOYfQ3T6/f4YNvEJYzjELXp4VVXVz263+7cR2niBxAFeZ2YPi3iHR/gYERPDwhpOsd6sz8x/mfkNG3iOlWFhFj8y89J9KvzGXER0GuEaD42mgwHqUtoljbcRsTBCeINpfM/MgZLKPpaxFxGbOCqDXmILN7hoJrTKH+axhxmcYRxP0MIDnOBDZv5q1XUNIuJxifJp+UNV7t7BFM6xeic0RMQ4Bpl5W/ol7GISx/eEUUTECrbx+f8A8xhiZht9zsgAAAAASUVORK5CYII='; + +/** + * + */ +Sidebar.prototype.dragPreviewBorder = '1px dashed black'; + +/** + * Specifies if tooltips should be visible. Default is true. + */ +Sidebar.prototype.enableTooltips = true; + +/** + * Specifies the delay for the tooltip. Default is 16 px. + */ +Sidebar.prototype.tooltipBorder = 16; + +/** + * Specifies the delay for the tooltip. Default is 300 ms. + */ +Sidebar.prototype.tooltipDelay = 300; + +/** + * Specifies the delay for the drop target icons. Default is 200 ms. + */ +Sidebar.prototype.dropTargetDelay = 200; + +/** + * Specifies the URL of the gear image. + */ +Sidebar.prototype.gearImage = STENCIL_PATH + '/clipart/Gear_128x128.png'; + +/** + * Specifies the width of the thumbnails. + */ +Sidebar.prototype.thumbWidth = 36; + +/** + * Specifies the height of the thumbnails. + */ +Sidebar.prototype.thumbHeight = 36; + +/** + * Specifies the padding for the thumbnails. Default is 3. + */ +Sidebar.prototype.thumbPadding = (document.documentMode >= 5) ? 0 : 1; + +/** + * Specifies the delay for the tooltip. Default is 2 px. + */ +Sidebar.prototype.thumbBorder = 2; + +/** + * Specifies the size of the sidebar titles. + */ +Sidebar.prototype.sidebarTitleSize = 9; + +/** + * Specifies if titles in the sidebar should be enabled. + */ +Sidebar.prototype.sidebarTitles = false; + +/** + * Specifies if titles in the tooltips should be enabled. + */ +Sidebar.prototype.tooltipTitles = true; + +/** + * Specifies if titles in the tooltips should be enabled. + */ +Sidebar.prototype.maxTooltipWidth = 400; + +/** + * Specifies if titles in the tooltips should be enabled. + */ +Sidebar.prototype.maxTooltipHeight = 400; + +/** + * Specifies if stencil files should be loaded and added to the search index + * when stencil palettes are added. If this is false then the stencil files + * are lazy-loaded when the palette is shown. + */ +Sidebar.prototype.addStencilsToIndex = true; + +/** + * Specifies the width for clipart images. Default is 80. + */ +Sidebar.prototype.defaultImageWidth = 80; + +/** + * Specifies the height for clipart images. Default is 80. + */ +Sidebar.prototype.defaultImageHeight = 80; + +/** + * Adds all palettes to the sidebar. + */ +Sidebar.prototype.getTooltipOffset = function() +{ + return new mxPoint(0, 0); +}; + +/** + * Adds all palettes to the sidebar. + */ +Sidebar.prototype.showTooltip = function(elt, cells, w, h, title, showLabel) +{ + if (this.enableTooltips && this.showTooltips) + { + if (this.currentElt != elt) + { + if (this.thread != null) + { + window.clearTimeout(this.thread); + this.thread = null; + } + + var show = mxUtils.bind(this, function() + { + // Lazy creation of the DOM nodes and graph instance + if (this.tooltip == null) + { + this.tooltip = document.createElement('div'); + this.tooltip.className = 'geSidebarTooltip'; + this.tooltip.style.zIndex = mxPopupMenu.prototype.zIndex - 1; + document.body.appendChild(this.tooltip); + + this.graph2 = new Graph(this.tooltip, null, null, this.editorUi.editor.graph.getStylesheet()); + this.graph2.resetViewOnRootChange = false; + this.graph2.foldingEnabled = false; + this.graph2.gridEnabled = false; + this.graph2.autoScroll = false; + this.graph2.setTooltips(false); + this.graph2.setConnectable(false); + this.graph2.setEnabled(false); + + if (!mxClient.IS_SVG) + { + this.graph2.view.canvas.style.position = 'relative'; + } + + this.tooltipImage = mxUtils.createImage(this.tooltipImage); + this.tooltipImage.className = 'geSidebarTooltipImage'; + this.tooltipImage.style.zIndex = mxPopupMenu.prototype.zIndex - 1; + this.tooltipImage.style.position = 'absolute'; + this.tooltipImage.style.width = '14px'; + this.tooltipImage.style.height = '27px'; + + document.body.appendChild(this.tooltipImage); + } + + this.graph2.model.clear(); + this.graph2.view.setTranslate(this.tooltipBorder, this.tooltipBorder); + + if (w > this.maxTooltipWidth || h > this.maxTooltipHeight) + { + this.graph2.view.scale = Math.round(Math.min(this.maxTooltipWidth / w, this.maxTooltipHeight / h) * 100) / 100; + } + else + { + this.graph2.view.scale = 1; + } + + this.tooltip.style.display = 'block'; + this.graph2.labelsVisible = (showLabel == null || showLabel); + var fo = mxClient.NO_FO; + mxClient.NO_FO = Editor.prototype.originalNoForeignObject; + this.graph2.addCells(cells); + mxClient.NO_FO = fo; + + var bounds = this.graph2.getGraphBounds(); + var width = bounds.width + 2 * this.tooltipBorder + 4; + var height = bounds.height + 2 * this.tooltipBorder; + + if (mxClient.IS_QUIRKS) + { + height += 4; + this.tooltip.style.overflow = 'hidden'; + } + else + { + this.tooltip.style.overflow = 'visible'; + } + + this.tooltipImage.style.visibility = 'visible'; + this.tooltip.style.width = width + 'px'; + + // Adds title for entry + if (this.tooltipTitles && title != null && title.length > 0) + { + if (this.tooltipTitle == null) + { + this.tooltipTitle = document.createElement('div'); + this.tooltipTitle.style.borderTop = '1px solid gray'; + this.tooltipTitle.style.textAlign = 'center'; + this.tooltipTitle.style.width = '100%'; + + // Oversize titles are cut-off currently. Should make tooltip wider later. + this.tooltipTitle.style.overflow = 'hidden'; + this.tooltipTitle.style.position = 'absolute'; + this.tooltipTitle.style.paddingTop = '6px'; + this.tooltipTitle.style.bottom = '6px'; + + this.tooltip.appendChild(this.tooltipTitle); + } + else + { + this.tooltipTitle.innerHTML = ''; + } + + this.tooltipTitle.style.display = ''; + mxUtils.write(this.tooltipTitle, title); + + var ddy = this.tooltipTitle.offsetHeight + 10; + height += ddy; + + if (mxClient.IS_SVG) + { + this.tooltipTitle.style.marginTop = (2 - ddy) + 'px'; + } + else + { + height -= 6; + this.tooltipTitle.style.top = (height - ddy) + 'px'; + } + } + else if (this.tooltipTitle != null && this.tooltipTitle.parentNode != null) + { + this.tooltipTitle.style.display = 'none'; + } + + this.tooltip.style.height = height + 'px'; + var x0 = -Math.round(bounds.x - this.tooltipBorder); + var y0 = -Math.round(bounds.y - this.tooltipBorder); + + var b = document.body; + var d = document.documentElement; + var off = this.getTooltipOffset(); + var bottom = Math.max(b.clientHeight || 0, d.clientHeight); + var left = this.container.clientWidth + this.editorUi.splitSize + 3 + this.editorUi.container.offsetLeft + off.x; + var top = Math.min(bottom - height - 20 /*status bar*/, Math.max(0, (this.editorUi.container.offsetTop + + this.container.offsetTop + elt.offsetTop - this.container.scrollTop - height / 2 + 16))) + off.y; + + if (mxClient.IS_SVG) + { + if (x0 != 0 || y0 != 0) + { + this.graph2.view.canvas.setAttribute('transform', 'translate(' + x0 + ',' + y0 + ')'); + } + else + { + this.graph2.view.canvas.removeAttribute('transform'); + } + } + else + { + this.graph2.view.drawPane.style.left = x0 + 'px'; + this.graph2.view.drawPane.style.top = y0 + 'px'; + } + + // Workaround for ignored position CSS style in IE9 + // (changes to relative without the following line) + this.tooltip.style.position = 'absolute'; + this.tooltip.style.left = left + 'px'; + this.tooltip.style.top = top + 'px'; + this.tooltipImage.style.left = (left - 13) + 'px'; + this.tooltipImage.style.top = (top + height / 2 - 13) + 'px'; + }); + + if (this.tooltip != null && this.tooltip.style.display != 'none') + { + show(); + } + else + { + this.thread = window.setTimeout(show, this.tooltipDelay); + } + + this.currentElt = elt; + } + } +}; + +/** + * Hides the current tooltip. + */ +Sidebar.prototype.hideTooltip = function() +{ + if (this.thread != null) + { + window.clearTimeout(this.thread); + this.thread = null; + } + + if (this.tooltip != null) + { + this.tooltip.style.display = 'none'; + this.tooltipImage.style.visibility = 'hidden'; + this.currentElt = null; + } +}; + +/** + * Hides the current tooltip. + */ +Sidebar.prototype.addDataEntry = function(tags, width, height, title, data) +{ + return this.addEntry(tags, mxUtils.bind(this, function() + { + return this.createVertexTemplateFromData(data, width, height, title); + })); +}; + +/** + * Hides the current tooltip. + */ +Sidebar.prototype.addEntry = function(tags, fn) +{ + if (this.taglist != null && tags != null && tags.length > 0) + { + // Replaces special characters + var tmp = tags.toLowerCase().replace(/[\/\,\(\)]/g, ' ').split(' '); + + var doAddEntry = mxUtils.bind(this, function(tag) + { + if (tag.length > 1) + { + var entry = this.taglist[tag]; + + if (typeof entry !== 'object') + { + entry = {entries: [], dict: new mxDictionary()}; + this.taglist[tag] = entry; + } + + // Ignores duplicates + if (entry.dict.get(fn) == null) + { + entry.dict.put(fn, fn); + entry.entries.push(fn); + } + } + }); + + for (var i = 0; i < tmp.length; i++) + { + doAddEntry(tmp[i]); + + // Adds additional entry with removed trailing numbers + var normalized = tmp[i].replace(/\.*\d*$/, ''); + + if (normalized != tmp[i]) + { + doAddEntry(normalized); + } + } + } + + return fn; +}; + +/** + * Adds shape search UI. + */ +Sidebar.prototype.searchEntries = function(searchTerms, count, page, success, error) +{ + if (this.taglist != null && searchTerms != null) + { + var tmp = searchTerms.toLowerCase().split(' '); + var dict = new mxDictionary(); + var max = (page + 1) * count; + var results = []; + var index = 0; + + for (var i = 0; i < tmp.length; i++) + { + if (tmp[i].length > 0) + { + var entry = this.taglist[tmp[i]]; + var tmpDict = new mxDictionary(); + + if (entry != null) + { + var arr = entry.entries; + results = []; + + for (var j = 0; j < arr.length; j++) + { + var entry = arr[j]; + + // NOTE Array does not contain duplicates + if ((index == 0) == (dict.get(entry) == null)) + { + tmpDict.put(entry, entry); + results.push(entry); + + if (i == tmp.length - 1 && results.length == max) + { + success(results.slice(page * count, max), max, true, tmp); + + return; + } + } + } + } + else + { + results = []; + } + + dict = tmpDict; + index++; + } + } + + var len = results.length; + success(results.slice(page * count, (page + 1) * count), len, false, tmp); + } + else + { + success([], null, null, tmp); + } +}; + +/** + * Adds shape search UI. + */ +Sidebar.prototype.filterTags = function(tags) +{ + if (tags != null) + { + var arr = tags.split(' '); + var result = []; + var hash = {}; + + // Ignores tags with leading numbers, strips trailing numbers + for (var i = 0; i < arr.length; i++) + { + // Removes duplicates + if (hash[arr[i]] == null) + { + hash[arr[i]] = '1'; + result.push(arr[i]); + } + } + + return result.join(' '); + } + + return null; +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.cloneCell = function(cell, value) +{ + var clone = cell.clone(); + + if (value != null) + { + clone.value = value; + } + + return clone; +}; + +/** + * Adds shape search UI. + */ +Sidebar.prototype.addSearchPalette = function(expand) +{ + var elt = document.createElement('div'); + elt.style.visibility = 'hidden'; + this.container.appendChild(elt); + + var div = document.createElement('div'); + div.className = 'geSidebar'; + div.style.boxSizing = 'border-box'; + div.style.overflow = 'hidden'; + div.style.width = '100%'; + div.style.padding = '8px'; + div.style.paddingTop = '14px'; + div.style.paddingBottom = '0px'; + + if (!expand) + { + div.style.display = 'none'; + } + + var inner = document.createElement('div'); + inner.style.whiteSpace = 'nowrap'; + inner.style.textOverflow = 'clip'; + inner.style.paddingBottom = '8px'; + inner.style.cursor = 'default'; + + var input = document.createElement('input'); + input.setAttribute('placeholder', mxResources.get('searchShapes')); + input.setAttribute('type', 'text'); + input.style.fontSize = '12px'; + input.style.overflow = 'hidden'; + input.style.boxSizing = 'border-box'; + input.style.border = 'solid 1px #d5d5d5'; + input.style.borderRadius = '4px'; + input.style.width = '100%'; + input.style.outline = 'none'; + input.style.padding = '6px'; + inner.appendChild(input); + + var cross = document.createElement('img'); + cross.setAttribute('src', Sidebar.prototype.searchImage); + cross.setAttribute('title', mxResources.get('search')); + cross.style.position = 'relative'; + cross.style.left = '-18px'; + + if (mxClient.IS_QUIRKS) + { + input.style.height = '28px'; + cross.style.top = '-4px'; + } + else + { + cross.style.top = '1px'; + } + + // Needed to block event transparency in IE + cross.style.background = 'url(\'' + this.editorUi.editor.transparentImage + '\')'; + + var find; + + inner.appendChild(cross); + div.appendChild(inner); + + var center = document.createElement('center'); + var button = mxUtils.button(mxResources.get('moreResults'), function() + { + find(); + }); + button.style.display = 'none'; + + // Workaround for inherited line-height in quirks mode + button.style.lineHeight = 'normal'; + button.style.marginTop = '4px'; + button.style.marginBottom = '8px'; + center.style.paddingTop = '4px'; + center.style.paddingBottom = '8px'; + + center.appendChild(button); + div.appendChild(center); + + var searchTerm = ''; + var active = false; + var complete = false; + var page = 0; + var hash = new Object(); + + // Count is dynamically updated below + var count = 12; + + var clearDiv = mxUtils.bind(this, function() + { + active = false; + this.currentSearch = null; + var child = div.firstChild; + + while (child != null) + { + var next = child.nextSibling; + + if (child != inner && child != center) + { + child.parentNode.removeChild(child); + } + + child = next; + } + }); + + mxEvent.addListener(cross, 'click', function() + { + if (cross.getAttribute('src') == Dialog.prototype.closeImage) + { + cross.setAttribute('src', Sidebar.prototype.searchImage); + cross.setAttribute('title', mxResources.get('search')); + button.style.display = 'none'; + input.value = ''; + searchTerm = ''; + clearDiv(); + } + + input.focus(); + }); + + find = mxUtils.bind(this, function() + { + // Shows 4 rows (minimum 4 results) + count = 4 * Math.max(1, Math.floor(this.container.clientWidth / (this.thumbWidth + 10))); + this.hideTooltip(); + + if (input.value != '') + { + if (center.parentNode != null) + { + if (searchTerm != input.value) + { + clearDiv(); + searchTerm = input.value; + hash = new Object(); + complete = false; + page = 0; + } + + if (!active && !complete) + { + button.setAttribute('disabled', 'true'); + button.style.display = ''; + button.style.cursor = 'wait'; + button.innerHTML = mxResources.get('loading') + '...'; + active = true; + + // Ignores old results + var current = new Object(); + this.currentSearch = current; + + this.searchEntries(searchTerm, count, page, mxUtils.bind(this, function(results, len, more, terms) + { + if (this.currentSearch == current) + { + results = (results != null) ? results : []; + active = false; + page++; + center.parentNode.removeChild(center); + this.insertSearchHint(div, searchTerm, count, page, results, len, more, terms); + + for (var i = 0; i < results.length; i++) + { + var elt = results[i](); + + // Avoids duplicates in results + if (hash[elt.innerHTML] == null) + { + hash[elt.innerHTML] = '1'; + div.appendChild(results[i]()); + } + } + + if (more) + { + button.removeAttribute('disabled'); + button.innerHTML = mxResources.get('moreResults'); + } + else + { + button.innerHTML = mxResources.get('reset'); + button.style.display = 'none'; + complete = true; + } + + button.style.cursor = ''; + div.appendChild(center); + } + }), mxUtils.bind(this, function() + { + // TODO: Error handling + button.style.cursor = ''; + })); + } + } + } + else + { + clearDiv(); + input.value = ''; + searchTerm = ''; + hash = new Object(); + button.style.display = 'none'; + complete = false; + input.focus(); + } + }); + + mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(evt) + { + if (evt.keyCode == 13 /* Enter */) + { + find(); + mxEvent.consume(evt); + } + })); + + mxEvent.addListener(input, 'focus', function() + { + input.style.paddingRight = ''; + }); + + mxEvent.addListener(input, 'blur', function() + { + input.style.paddingRight = '20px'; + }); + + input.style.paddingRight = '20px'; + + mxEvent.addListener(input, 'keyup', mxUtils.bind(this, function(evt) + { + if (input.value == '') + { + cross.setAttribute('src', Sidebar.prototype.searchImage); + cross.setAttribute('title', mxResources.get('search')); + } + else + { + cross.setAttribute('src', Dialog.prototype.closeImage); + cross.setAttribute('title', mxResources.get('reset')); + } + + if (input.value == '') + { + complete = true; + button.style.display = 'none'; + } + else if (input.value != searchTerm) + { + button.style.display = 'none'; + complete = false; + } + else if (!active) + { + if (complete) + { + button.style.display = 'none'; + } + else + { + button.style.display = ''; + } + } + })); + + // Workaround for blocked text selection in Editor + mxEvent.addListener(input, 'mousedown', function(evt) + { + if (evt.stopPropagation) + { + evt.stopPropagation(); + } + + evt.cancelBubble = true; + }); + + // Workaround for blocked text selection in Editor + mxEvent.addListener(input, 'selectstart', function(evt) + { + if (evt.stopPropagation) + { + evt.stopPropagation(); + } + + evt.cancelBubble = true; + }); + + var outer = document.createElement('div'); + outer.appendChild(div); + this.container.appendChild(outer); + + // Keeps references to the DOM nodes + this.palettes['search'] = [elt, outer]; +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.insertSearchHint = function(div, searchTerm, count, page, results, len, more, terms) +{ + if (results.length == 0 && page == 1) + { + var err = document.createElement('div'); + err.className = 'geTitle'; + err.style.cssText = 'background-color:transparent;border-color:transparent;' + + 'color:gray;padding:6px 0px 0px 0px !important;margin:4px 8px 4px 8px;' + + 'text-align:center;cursor:default !important'; + + mxUtils.write(err, mxResources.get('noResultsFor', [searchTerm])); + div.appendChild(err); + } +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.addGeneralPalette = function(expand) +{ + var lineTags = 'line lines connector connectors connection connections arrow arrows '; + + var fns = [ + this.createVertexTemplateEntry('rounded=0;whiteSpace=wrap;html=1;', 120, 60, '', 'Rectangle', null, null, 'rect rectangle box'), + this.createVertexTemplateEntry('rounded=1;whiteSpace=wrap;html=1;', 120, 60, '', 'Rounded Rectangle', null, null, 'rounded rect rectangle box'), + // Explicit strokecolor/fillcolor=none is a workaround to maintain transparent background regardless of current style + this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;', + 40, 20, 'Text', 'Text', null, null, 'text textbox textarea label'), + this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;', 190, 120, + '

Heading

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

', + 'Textbox', null, null, 'text textbox textarea'), + this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;', 120, 80, '', 'Ellipse', null, null, 'oval ellipse state'), + this.createVertexTemplateEntry('whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Square', null, null, 'square'), + this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Circle', null, null, 'circle'), + this.createVertexTemplateEntry('shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;', 120, 60, '', 'Process', null, null, 'process task'), + this.createVertexTemplateEntry('rhombus;whiteSpace=wrap;html=1;', 80, 80, '', 'Diamond', null, null, 'diamond rhombus if condition decision conditional question test'), + this.createVertexTemplateEntry('shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;', 120, 60, '', 'Parallelogram'), + this.createVertexTemplateEntry('shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;', 120, 80, '', 'Hexagon', null, null, 'hexagon preparation'), + this.createVertexTemplateEntry('triangle;whiteSpace=wrap;html=1;', 60, 80, '', 'Triangle', null, null, 'triangle logic inverter buffer'), + this.createVertexTemplateEntry('shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 60, 80, '', 'Cylinder', null, null, 'cylinder data database'), + this.createVertexTemplateEntry('ellipse;shape=cloud;whiteSpace=wrap;html=1;', 120, 80, '', 'Cloud', null, null, 'cloud network'), + this.createVertexTemplateEntry('shape=document;whiteSpace=wrap;html=1;boundedLbl=1;', 120, 80, '', 'Document'), + this.createVertexTemplateEntry('shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Internal Storage'), + this.createVertexTemplateEntry('shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 120, 80, '', 'Cube'), + this.createVertexTemplateEntry('shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;', 120, 80, '', 'Step'), + this.createVertexTemplateEntry('shape=trapezoid;perimeter=trapezoidPerimeter;whiteSpace=wrap;html=1;', 120, 60, '', 'Trapezoid'), + this.createVertexTemplateEntry('shape=tape;whiteSpace=wrap;html=1;', 120, 100, '', 'Tape'), + this.createVertexTemplateEntry('shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 100, '', 'Note'), + this.createVertexTemplateEntry('shape=card;whiteSpace=wrap;html=1;', 80, 100, '', 'Card'), + this.createVertexTemplateEntry('shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;', 120, 80, '', 'Callout', null, null, 'bubble chat thought speech message'), + this.createVertexTemplateEntry('shape=umlActor;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;html=1;outlineConnect=0;', 30, 60, 'Actor', 'Actor', false, null, 'user person human stickman'), + this.addEntry('curve', mxUtils.bind(this, function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 50, 50), 'curved=1;endArrow=classic;html=1;'); + cell.geometry.setTerminalPoint(new mxPoint(0, 50), true); + cell.geometry.setTerminalPoint(new mxPoint(50, 0), false); + cell.geometry.points = [new mxPoint(50, 50), new mxPoint(0, 0)]; + cell.geometry.relative = true; + cell.edge = true; + + return this.createEdgeTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Curve'); + })), + this.createEdgeTemplateEntry('shape=flexArrow;endArrow=classic;startArrow=classic;html=1;fillColor=#ffffff;', 50, 50, '', 'Bidirectional Arrow', null, lineTags + 'bidirectional'), + this.createEdgeTemplateEntry('shape=flexArrow;endArrow=classic;html=1;fillColor=#ffffff;', 50, 50, '', 'Arrow', null, lineTags + 'directional directed'), + this.createEdgeTemplateEntry('shape=link;html=1;', 50, 50, '', 'Link', null, lineTags + 'link'), + this.createEdgeTemplateEntry('endArrow=none;dashed=1;html=1;', 50, 50, '', 'Dashed Line', null, lineTags + 'dashed undirected no'), + this.createEdgeTemplateEntry('endArrow=none;html=1;', 50, 50, '', 'Line', null, lineTags + 'simple undirected plain blank no'), + this.createEdgeTemplateEntry('endArrow=classic;startArrow=classic;html=1;', 50, 50, '', 'Bidirectional Connector', null, lineTags + 'bidirectional'), + this.createEdgeTemplateEntry('endArrow=classic;html=1;', 50, 50, '', 'Directional Connector', null, lineTags + 'directional directed') + ]; + + this.addPaletteFunctions('general', mxResources.get('general'), (expand != null) ? expand : true, fns); +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.addBasicPalette = function(dir) +{ + this.addStencilPalette('basic', mxResources.get('basic'), dir + '/basic.xml', + ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2', + null, null, null, null, [ + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;top=0;bottom=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;right=0;top=0;bottom=0;fillColor=none;routingCenterX=-0.5;', 120, 60, '', 'Partial Rectangle'), + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;bottom=0;right=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;top=0;left=0;fillColor=none;', 120, 60, '', 'Partial Rectangle') + ]); +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.addMiscPalette = function(expand) +{ + var sb = this; + var lineTags = 'line lines connector connectors connection connections arrow arrows ' + + var fns = [ + this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;', 100, 40, 'Title', 'Title', null, null, 'text heading title'), + this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;whiteSpace=wrap;verticalAlign=middle;overflow=hidden;', 100, 80, + '
  • Value 1
  • Value 2
  • Value 3
', 'Unordered List'), + this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;whiteSpace=wrap;verticalAlign=middle;overflow=hidden;', 100, 80, + '
  1. Value 1
  2. Value 2
  3. Value 3
', 'Ordered List'), + this.createVertexTemplateEntry('text;html=1;strokeColor=#c0c0c0;fillColor=#ffffff;overflow=fill;rounded=0;', 280, 160, + '' + + '' + + '' + + '' + + '' + + '
Title 1Title 2Title 3
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
Value 10Value 11Value 12
', 'Table 1'), + this.createVertexTemplateEntry('text;html=1;strokeColor=#c0c0c0;fillColor=none;overflow=fill;', 180, 140, + '' + + '' + + '' + + '
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
', 'Table 2'), + this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;overflow=fill;', 180, 140, + '' + + '' + + '' + + '
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
', 'Table 3'), + this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;overflow=fill;', 160, 140, + '' + + '' + + '' + + '
Title
Section 1.1\nSection 1.2\nSection 1.3
Section 2.1\nSection 2.2\nSection 2.3
', 'Table 4'), + this.addEntry('link hyperlink', mxUtils.bind(this, function() + { + var cell = new mxCell('Link', new mxGeometry(0, 0, 60, 40), 'text;html=1;strokeColor=none;fillColor=none;whiteSpace=wrap;align=center;verticalAlign=middle;fontColor=#0000EE;fontStyle=4;'); + cell.vertex = true; + this.graph.setLinkForCell(cell, 'https://www.draw.io'); + + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Link'); + })), + this.addEntry('timestamp date time text label', mxUtils.bind(this, function() + { + var cell = new mxCell('%date{ddd mmm dd yyyy HH:MM:ss}%', new mxGeometry(0, 0, 160, 20), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'placeholders', '1'); + + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Timestamp'); + })), + this.addEntry('variable placeholder metadata hello world text label', mxUtils.bind(this, function() + { + var cell = new mxCell('%name% Text', new mxGeometry(0, 0, 80, 20), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'placeholders', '1'); + this.graph.setAttributeForCell(cell, 'name', 'Variable'); + + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Variable'); + })), + this.createVertexTemplateEntry('shape=ext;double=1;rounded=0;whiteSpace=wrap;html=1;', 120, 80, '', 'Double Rectangle', null, null, 'rect rectangle box double'), + this.createVertexTemplateEntry('shape=ext;double=1;rounded=1;whiteSpace=wrap;html=1;', 120, 80, '', 'Double Rounded Rectangle', null, null, 'rounded rect rectangle box double'), + this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;', 100, 60, '', 'Double Ellipse', null, null, 'oval ellipse start end state double'), + this.createVertexTemplateEntry('shape=ext;double=1;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Square', null, null, 'double square'), + this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Circle', null, null, 'double circle'), + this.createEdgeTemplateEntry('rounded=0;comic=1;strokeWidth=2;endArrow=blockThin;html=1;fontFamily=Comic Sans MS;fontStyle=1;', 50, 50, '', 'Comic Arrow'), + this.createVertexTemplateEntry('html=1;whiteSpace=wrap;comic=1;strokeWidth=2;fontFamily=Comic Sans MS;fontStyle=1;', 120, 60, 'RECTANGLE', 'Comic Rectangle', true, null, 'comic rectangle rect box text retro'), + this.createVertexTemplateEntry('rhombus;html=1;align=center;whiteSpace=wrap;comic=1;strokeWidth=2;fontFamily=Comic Sans MS;fontStyle=1;', 100, 100, 'DIAMOND', 'Comic Diamond', true, null, 'comic diamond rhombus if condition decision conditional question test retro'), + this.createVertexTemplateEntry('html=1;whiteSpace=wrap;aspect=fixed;shape=isoRectangle;', 150, 90, '', 'Isometric Square', true, null, 'rectangle rect box iso isometric'), + this.createVertexTemplateEntry('html=1;whiteSpace=wrap;aspect=fixed;shape=isoCube;backgroundOutline=1;', 90, 100, '', 'Isometric Cube', true, null, 'cube box iso isometric'), + this.createEdgeTemplateEntry('edgeStyle=isometricEdgeStyle;endArrow=none;html=1;', 50, 100, '', 'Isometric Edge 1'), + this.createEdgeTemplateEntry('edgeStyle=isometricEdgeStyle;endArrow=none;html=1;elbow=vertical;', 50, 100, '', 'Isometric Edge 2'), + this.createVertexTemplateEntry('shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;', 20, 120, '', 'Curly Bracket'), + this.createVertexTemplateEntry('line;strokeWidth=2;html=1;', 160, 10, '', 'Horizontal Line'), + this.createVertexTemplateEntry('line;strokeWidth=2;direction=south;html=1;', 10, 160, '', 'Vertical Line'), + this.createVertexTemplateEntry('line;strokeWidth=4;html=1;perimeter=backbonePerimeter;points=[];outlineConnect=0;', 160, 10, '', 'Horizontal Backbone', false, null, 'backbone bus network'), + this.createVertexTemplateEntry('line;strokeWidth=4;direction=south;html=1;perimeter=backbonePerimeter;points=[];outlineConnect=0;', 10, 160, '', 'Vertical Backbone', false, null, 'backbone bus network'), + this.createVertexTemplateEntry('shape=crossbar;whiteSpace=wrap;html=1;rounded=1;', 120, 20, '', 'Crossbar', false, null, 'crossbar distance measure dimension unit'), + this.createVertexTemplateEntry('shape=image;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;imageAspect=1;aspect=fixed;image=' + this.gearImage, 52, 61, '', 'Image (Fixed Aspect)', false, null, 'fixed image icon symbol'), + this.createVertexTemplateEntry('shape=image;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;imageAspect=0;image=' + this.gearImage, 50, 60, '', 'Image (Variable Aspect)', false, null, 'strechted image icon symbol'), + this.createVertexTemplateEntry('icon;html=1;image=' + this.gearImage, 60, 60, 'Icon', 'Icon', false, null, 'icon image symbol'), + this.createVertexTemplateEntry('label;whiteSpace=wrap;html=1;image=' + this.gearImage, 140, 60, 'Label', 'Label 1', null, null, 'label image icon symbol'), + this.createVertexTemplateEntry('label;whiteSpace=wrap;html=1;align=center;verticalAlign=bottom;spacingLeft=0;spacingBottom=4;imageAlign=center;imageVerticalAlign=top;image=' + this.gearImage, 120, 80, 'Label', 'Label 2', null, null, 'label image icon symbol'), + this.addEntry('shape group container', function() + { + var cell = new mxCell('Label', new mxGeometry(0, 0, 160, 70), + 'html=1;whiteSpace=wrap;container=1;recursiveResize=0;collapsible=0;'); + cell.vertex = true; + + var symbol = new mxCell('', new mxGeometry(20, 20, 20, 30), 'triangle;html=1;whiteSpace=wrap;'); + symbol.vertex = true; + cell.insert(symbol); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Shape Group'); + }), + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;left=0;right=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), + this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;bottom=1;right=1;top=0;bottom=1;fillColor=none;routingCenterX=-0.5;', 120, 60, '', 'Partial Rectangle'), + this.createEdgeTemplateEntry('edgeStyle=segmentEdgeStyle;endArrow=classic;html=1;', 50, 50, '', 'Manual Line', null, lineTags + 'manual'), + this.createEdgeTemplateEntry('shape=filledEdge;rounded=0;fixDash=1;endArrow=none;strokeWidth=10;fillColor=#ffffff;edgeStyle=orthogonalEdgeStyle;', 60, 40, '', 'Filled Edge'), + this.createEdgeTemplateEntry('edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;', 50, 50, '', 'Horizontal Elbow', null, lineTags + 'elbow horizontal'), + this.createEdgeTemplateEntry('edgeStyle=elbowEdgeStyle;elbow=vertical;endArrow=classic;html=1;', 50, 50, '', 'Vertical Elbow', null, lineTags + 'elbow vertical') + ]; + + this.addPaletteFunctions('misc', mxResources.get('misc'), (expand != null) ? expand : true, fns); +}; +/** + * Adds the container palette to the sidebar. + */ +Sidebar.prototype.addAdvancedPalette = function(expand) +{ + this.addPaletteFunctions('advanced', mxResources.get('advanced'), (expand != null) ? expand : false, this.createAdvancedShapes()); +}; + +/** + * Adds the container palette to the sidebar. + */ +Sidebar.prototype.createAdvancedShapes = function() +{ + // Avoids having to bind all functions to "this" + var sb = this; + + // Reusable cells + var field = new mxCell('List Item', new mxGeometry(0, 0, 60, 26), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'); + field.vertex = true; + + return [ + this.createVertexTemplateEntry('shape=xor;whiteSpace=wrap;html=1;', 60, 80, '', 'Or', null, null, 'logic or'), + this.createVertexTemplateEntry('shape=or;whiteSpace=wrap;html=1;', 60, 80, '', 'And', null, null, 'logic and'), + this.createVertexTemplateEntry('shape=dataStorage;whiteSpace=wrap;html=1;', 100, 80, '', 'Data Storage'), + this.createVertexTemplateEntry('shape=tapeData;whiteSpace=wrap;html=1;perimeter=ellipsePerimeter;', 80, 80, '', 'Tape Data'), + this.createVertexTemplateEntry('shape=manualInput;whiteSpace=wrap;html=1;', 80, 80, '', 'Manual Input'), + this.createVertexTemplateEntry('shape=loopLimit;whiteSpace=wrap;html=1;', 100, 80, '', 'Loop Limit'), + this.createVertexTemplateEntry('shape=offPageConnector;whiteSpace=wrap;html=1;', 80, 80, '', 'Off Page Connector'), + this.createVertexTemplateEntry('shape=delay;whiteSpace=wrap;html=1;', 80, 40, '', 'Delay'), + this.createVertexTemplateEntry('shape=display;whiteSpace=wrap;html=1;', 80, 40, '', 'Display'), + this.createVertexTemplateEntry('shape=singleArrow;direction=west;whiteSpace=wrap;html=1;', 100, 60, '', 'Arrow Left'), + this.createVertexTemplateEntry('shape=singleArrow;whiteSpace=wrap;html=1;', 100, 60, '', 'Arrow Right'), + this.createVertexTemplateEntry('shape=singleArrow;direction=north;whiteSpace=wrap;html=1;', 60, 100, '', 'Arrow Up'), + this.createVertexTemplateEntry('shape=singleArrow;direction=south;whiteSpace=wrap;html=1;', 60, 100, '', 'Arrow Down'), + this.createVertexTemplateEntry('shape=doubleArrow;whiteSpace=wrap;html=1;', 100, 60, '', 'Double Arrow'), + this.createVertexTemplateEntry('shape=doubleArrow;direction=south;whiteSpace=wrap;html=1;', 60, 100, '', 'Double Arrow Vertical', null, null, 'double arrow'), + this.createVertexTemplateEntry('shape=actor;whiteSpace=wrap;html=1;', 40, 60, '', 'User', null, null, 'user person human'), + this.createVertexTemplateEntry('shape=cross;whiteSpace=wrap;html=1;', 80, 80, '', 'Cross'), + this.createVertexTemplateEntry('shape=corner;whiteSpace=wrap;html=1;', 80, 80, '', 'Corner'), + this.createVertexTemplateEntry('shape=tee;whiteSpace=wrap;html=1;', 80, 80, '', 'Tee'), + this.createVertexTemplateEntry('shape=datastore;whiteSpace=wrap;html=1;', 60, 60, '', 'Data Store', null, null, 'data store cylinder database'), + this.createVertexTemplateEntry('shape=orEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Or', null, null, 'or circle oval ellipse'), + this.createVertexTemplateEntry('shape=sumEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Sum', null, null, 'sum circle oval ellipse'), + this.createVertexTemplateEntry('shape=lineEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Ellipse with horizontal divider', null, null, 'circle oval ellipse'), + this.createVertexTemplateEntry('shape=lineEllipse;line=vertical;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Ellipse with vertical divider', null, null, 'circle oval ellipse'), + this.createVertexTemplateEntry('shape=sortShape;perimeter=rhombusPerimeter;whiteSpace=wrap;html=1;', 80, 80, '', 'Sort', null, null, 'sort'), + this.createVertexTemplateEntry('shape=collate;whiteSpace=wrap;html=1;', 80, 80, '', 'Collate', null, null, 'collate'), + this.createVertexTemplateEntry('shape=switch;whiteSpace=wrap;html=1;', 60, 60, '', 'Switch', null, null, 'switch router'), + this.addEntry('process bar', function() + { + return sb.createVertexTemplateFromData('zZXRaoMwFIafJpcDjbNrb2233rRQ8AkyPdPQaCRJV+3T7yTG2rUVBoOtgpDzn/xJzncCIdGyateKNeVW5iBI9EqipZLS9KOqXYIQhAY8J9GKUBrgT+jbRDZ02aBhCmrzEwPtDZ9MHKBXdkpmoDWKCVN9VptO+Kw+8kqwGqMkK7nIN6yTB7uTNizbD1FSSsVPsjYMC1qFKHxwIZZSSIVxLZ1/nJNar5+oQPMT7IYCrqUta1ENzuqGaeOFTArBGs3f3Vmtoo2Se7ja1h00kSoHK4bBIKUNy3hdoPYU0mF91i9mT8EEL2ocZ3gKa00ayWujLZY4IfHKFonVDLsRGgXuQ90zBmWgneyTk3yT1iArMKrDKUeem9L3ajHrbSXwohxsQd/ggOleKM7ese048J2/fwuim1uQGmhQCW8vQMkacP3GCQgBFMftHEsr7cYYe95CnmKTPMFbYD8CQ++DGQy+/M5X4ku5wHYmdIktfvk9tecpavThqS3m/0YtnqIWPTy1cD77K2wYjo+Ay317I74A', 296, 100, 'Process Bar'); + }), + this.createVertexTemplateEntry('swimlane;', 200, 200, 'Container', 'Container', null, null, 'container swimlane lane pool group'), + this.addEntry('list group erd table', function() + { + var cell = new mxCell('List', new mxGeometry(0, 0, 140, 110), + 'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;' + + 'resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); + cell.vertex = true; + cell.insert(sb.cloneCell(field, 'Item 1')); + cell.insert(sb.cloneCell(field, 'Item 2')); + cell.insert(sb.cloneCell(field, 'Item 3')); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'List'); + }), + this.addEntry('list item entry value group erd table', function() + { + return sb.createVertexTemplateFromCells([sb.cloneCell(field, 'List Item')], field.geometry.width, field.geometry.height, 'List Item'); + }) + ]; +}; + +/** + * Adds the general palette to the sidebar. + */ +Sidebar.prototype.addUmlPalette = function(expand) +{ + // Avoids having to bind all functions to "this" + var sb = this; + + // Reusable cells + var field = new mxCell('+ field: type', new mxGeometry(0, 0, 100, 26), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'); + field.vertex = true; + + var divider = new mxCell('', new mxGeometry(0, 0, 40, 8), 'line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;'); + divider.vertex = true; + + // Default tags + var dt = 'uml static class '; + + var fns = [ + this.createVertexTemplateEntry('html=1;', 110, 50, 'Object', 'Object', null, null, dt + 'object instance'), + this.createVertexTemplateEntry('html=1;', 110, 50, '«interface»
Name', 'Interface', null, null, dt + 'interface object instance annotated annotation'), + this.addEntry(dt + 'object instance', function() + { + var cell = new mxCell('Classname', new mxGeometry(0, 0, 160, 90), + 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); + cell.vertex = true; + cell.insert(field.clone()); + cell.insert(divider.clone()); + cell.insert(sb.cloneCell(field, '+ method(type): type')); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Class'); + }), + this.addEntry(dt + 'section subsection', function() + { + var cell = new mxCell('Classname', new mxGeometry(0, 0, 140, 110), + 'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); + cell.vertex = true; + cell.insert(field.clone()); + cell.insert(field.clone()); + cell.insert(field.clone()); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Class 2'); + }), + this.addEntry(dt + 'item member method function variable field attribute label', function() + { + return sb.createVertexTemplateFromCells([sb.cloneCell(field, '+ item: attribute')], field.geometry.width, field.geometry.height, 'Item 1'); + }), + this.addEntry(dt + 'item member method function variable field attribute label', function() + { + var cell = new mxCell('item: attribute', new mxGeometry(0, 0, 120, field.geometry.height), 'label;fontStyle=0;strokeColor=none;fillColor=none;align=left;verticalAlign=top;overflow=hidden;' + + 'spacingLeft=28;spacingRight=4;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;imageWidth=16;imageHeight=16;image=' + sb.gearImage); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Item 2'); + }), + this.addEntry(dt + 'divider hline line separator', function() + { + return sb.createVertexTemplateFromCells([divider.clone()], divider.geometry.width, divider.geometry.height, 'Divider'); + }), + this.addEntry(dt + 'spacer space gap separator', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 20, 14), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=4;spacingRight=4;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;'); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Spacer'); + }), + this.createVertexTemplateEntry('text;align=center;fontStyle=1;verticalAlign=middle;spacingLeft=3;spacingRight=3;strokeColor=none;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;', + 80, 26, 'Title', 'Title', null, null, dt + 'title label'), + this.addEntry(dt + 'component', function() + { + var cell = new mxCell('«Annotation»
Component', new mxGeometry(0, 0, 180, 90), 'html=1;'); + cell.vertex = true; + + var symbol = new mxCell('', new mxGeometry(1, 0, 20, 20), 'shape=component;jettyWidth=8;jettyHeight=4;'); + symbol.vertex = true; + symbol.geometry.relative = true; + symbol.geometry.offset = new mxPoint(-27, 7); + cell.insert(symbol); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Component'); + }), + this.addEntry(dt + 'component', function() + { + var cell = new mxCell('

Component

' + + '

+ Attribute1: Type
+ Attribute2: Type

', new mxGeometry(0, 0, 180, 90), + 'align=left;overflow=fill;html=1;'); + cell.vertex = true; + + var symbol = new mxCell('', new mxGeometry(1, 0, 20, 20), 'shape=component;jettyWidth=8;jettyHeight=4;'); + symbol.vertex = true; + symbol.geometry.relative = true; + symbol.geometry.offset = new mxPoint(-24, 4); + cell.insert(symbol); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Component with Attributes'); + }), + this.createVertexTemplateEntry('verticalAlign=top;align=left;spacingTop=8;spacingLeft=2;spacingRight=12;shape=cube;size=10;direction=south;fontStyle=4;html=1;', + 180, 120, 'Block', 'Block', null, null, dt + 'block'), + this.createVertexTemplateEntry('shape=component;align=left;spacingLeft=36;', 120, 60, 'Module', 'Module', null, null, dt + 'module'), + this.createVertexTemplateEntry('shape=folder;fontStyle=1;spacingTop=10;tabWidth=40;tabHeight=14;tabPosition=left;html=1;', 70, 50, + 'package', 'Package', null, null, dt + 'package'), + this.createVertexTemplateEntry('verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;', + 160, 90, '

Object:Type


' + + '

field1 = value1
field2 = value2
field3 = value3

', 'Object', + null, null, dt + 'object instance'), + this.createVertexTemplateEntry('verticalAlign=top;align=left;overflow=fill;html=1;',180, 90, + '
Tablename
' + + '' + + '
PKuniqueId
FK1' + + 'foreignKey
fieldname
', 'Entity', null, null, 'er entity table'), + this.addEntry(dt + 'object instance', function() + { + var cell = new mxCell('

' + + 'Class

' + + '
', new mxGeometry(0, 0, 140, 60), + 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 3'); + }), + this.addEntry(dt + 'object instance', function() + { + var cell = new mxCell('

' + + 'Class

' + + '

', new mxGeometry(0, 0, 140, 60), + 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 4'); + }), + this.addEntry(dt + 'object instance', function() + { + var cell = new mxCell('

' + + 'Class

' + + '

+ field: Type


' + + '

+ method(): Type

', new mxGeometry(0, 0, 160, 90), + 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 5'); + }), + this.addEntry(dt + 'object instance', function() + { + var cell = new mxCell('

' + + '<<Interface>>
Interface

' + + '

+ field1: Type
' + + '+ field2: Type

' + + '

' + + '+ method1(Type): Type
' + + '+ method2(Type, Type): Type

', new mxGeometry(0, 0, 190, 140), + 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); + cell.vertex = true; + + return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Interface 2'); + }), + this.createVertexTemplateEntry('shape=lollipop;direction=south;html=1;', 30, 10, '', 'Provided Interface', null, null, dt + 'provided interface'), + this.createVertexTemplateEntry('shape=requires;direction=north;html=1;', 30, 20, '', 'Required Interface', null, null, dt + 'required interface'), + this.createVertexTemplateEntry('shape=umlBoundary;whiteSpace=wrap;html=1;', 100, 80, 'Boundary Object', 'Boundary Object', null, null, 'uml boundary object'), + this.createVertexTemplateEntry('ellipse;shape=umlEntity;whiteSpace=wrap;html=1;', 80, 80, 'Entity Object', 'Entity Object', null, null, 'uml entity object'), + this.createVertexTemplateEntry('ellipse;shape=umlControl;whiteSpace=wrap;html=1;', 70, 80, 'Control Object', 'Control Object', null, null, 'uml control object'), + this.createVertexTemplateEntry('shape=umlActor;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;html=1;', 30, 60, 'Actor', 'Actor', false, null, 'uml actor'), + this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;', 140, 70, 'Use Case', 'Use Case', null, null, 'uml use case usecase'), + this.addEntry('uml activity state start', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 30, 30), + 'ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;'); + cell.vertex = true; + + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge.geometry.setTerminalPoint(new mxPoint(15, 90), false); + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, true); + + return sb.createVertexTemplateFromCells([cell, edge], 30, 90, 'Start'); + }), + this.addEntry('uml activity state', function() + { + var cell = new mxCell('Activity', new mxGeometry(0, 0, 120, 40), + 'rounded=1;whiteSpace=wrap;html=1;arcSize=40;fillColor=#ffffc0;strokeColor=#ff0000;'); + cell.vertex = true; + + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge.geometry.setTerminalPoint(new mxPoint(60, 100), false); + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, true); + + return sb.createVertexTemplateFromCells([cell, edge], 120, 100, 'Activity'); + }), + this.addEntry('uml activity composite state', function() + { + var cell = new mxCell('Composite State', new mxGeometry(0, 0, 160, 60), + 'swimlane;html=1;fontStyle=1;align=center;verticalAlign=middle;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=0;resizeLast=1;container=0;collapsible=0;rounded=1;arcSize=30;strokeColor=#ff0000;fillColor=#ffffc0;swimlaneFillColor=#ffffc0;'); + cell.vertex = true; + + var cell1 = new mxCell('Subtitle', new mxGeometry(0, 0, 200, 26), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;'); + cell1.vertex = true; + cell.insert(cell1); + + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge.geometry.setTerminalPoint(new mxPoint(80, 120), false); + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, true); + + return sb.createVertexTemplateFromCells([cell, edge], 160, 120, 'Composite State'); + }), + this.addEntry('uml activity condition', function() + { + var cell = new mxCell('Condition', new mxGeometry(0, 0, 80, 40), 'rhombus;whiteSpace=wrap;html=1;fillColor=#ffffc0;strokeColor=#ff0000;'); + cell.vertex = true; + + var edge1 = new mxCell('no', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge1.geometry.setTerminalPoint(new mxPoint(180, 20), false); + edge1.geometry.relative = true; + edge1.geometry.x = -1; + edge1.edge = true; + + cell.insertEdge(edge1, true); + + var edge2 = new mxCell('yes', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;verticalAlign=top;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge2.geometry.setTerminalPoint(new mxPoint(40, 100), false); + edge2.geometry.relative = true; + edge2.geometry.x = -1; + edge2.edge = true; + + cell.insertEdge(edge2, true); + + return sb.createVertexTemplateFromCells([cell, edge1, edge2], 180, 100, 'Condition'); + }), + this.addEntry('uml activity fork join', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 200, 10), 'shape=line;html=1;strokeWidth=6;strokeColor=#ff0000;'); + cell.vertex = true; + + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); + edge.geometry.setTerminalPoint(new mxPoint(100, 80), false); + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, true); + + return sb.createVertexTemplateFromCells([cell, edge], 200, 80, 'Fork/Join'); + }), + this.createVertexTemplateEntry('ellipse;html=1;shape=endState;fillColor=#000000;strokeColor=#ff0000;', 30, 30, '', 'End', null, null, 'uml activity state end'), + this.createVertexTemplateEntry('shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;', 100, 300, ':Object', 'Lifeline', null, null, 'uml sequence participant lifeline'), + this.createVertexTemplateEntry('shape=umlLifeline;participant=umlActor;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', + 20, 300, '', 'Actor Lifeline', null, null, 'uml sequence participant lifeline actor'), + this.createVertexTemplateEntry('shape=umlLifeline;participant=umlBoundary;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', + 50, 300, '', 'Boundary Lifeline', null, null, 'uml sequence participant lifeline boundary'), + this.createVertexTemplateEntry('shape=umlLifeline;participant=umlEntity;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', + 40, 300, '', 'Entity Lifeline', null, null, 'uml sequence participant lifeline entity'), + this.createVertexTemplateEntry('shape=umlLifeline;participant=umlControl;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', + 40, 300, '', 'Control Lifeline', null, null, 'uml sequence participant lifeline control'), + this.createVertexTemplateEntry('shape=umlFrame;whiteSpace=wrap;html=1;', 300, 200, 'frame', 'Frame', null, null, 'uml sequence frame'), + this.createVertexTemplateEntry('shape=umlDestroy;whiteSpace=wrap;html=1;strokeWidth=3;', 30, 30, '', 'Destruction', null, null, 'uml sequence destruction destroy'), + this.createVertexTemplateEntry('shape=note;whiteSpace=wrap;html=1;size=14;verticalAlign=top;align=left;spacingTop=-6;', 100, 70, 'Note', 'Note', null, null, 'uml note'), + this.addEntry('uml sequence invoke invocation call activation', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 10, 80), 'html=1;points=[];perimeter=orthogonalPerimeter;'); + cell.vertex = true; + + var edge = new mxCell('dispatch', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;startArrow=oval;endArrow=block;startSize=8;'); + edge.geometry.setTerminalPoint(new mxPoint(-60, 0), true); + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, false); + + return sb.createVertexTemplateFromCells([cell, edge], 10, 80, 'Found Message'); + }), + this.addEntry('uml sequence invoke call delegation synchronous invocation activation', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 10, 80), 'html=1;points=[];perimeter=orthogonalPerimeter;'); + cell.vertex = true; + + var edge1 = new mxCell('dispatch', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=block;entryX=0;entryY=0;'); + edge1.geometry.setTerminalPoint(new mxPoint(-70, 0), true); + edge1.geometry.relative = true; + edge1.edge = true; + + cell.insertEdge(edge1, false); + + var edge2 = new mxCell('return', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;exitX=0;exitY=0.95;'); + edge2.geometry.setTerminalPoint(new mxPoint(-70, 76), false); + edge2.geometry.relative = true; + edge2.edge = true; + + cell.insertEdge(edge2, true); + + return sb.createVertexTemplateFromCells([cell, edge1, edge2], 10, 80, 'Synchronous Invocation'); + }), + this.addEntry('uml sequence self call recursion delegation activation', function() + { + var cell = new mxCell('', new mxGeometry(0, 20, 10, 40), 'html=1;points=[];perimeter=orthogonalPerimeter;'); + cell.vertex = true; + + var edge = new mxCell('self call', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;spacingLeft=2;endArrow=block;rounded=0;entryX=1;entryY=0;'); + edge.geometry.setTerminalPoint(new mxPoint(5, 0), true); + edge.geometry.points = [new mxPoint(30, 0)]; + edge.geometry.relative = true; + edge.edge = true; + + cell.insertEdge(edge, false); + + return sb.createVertexTemplateFromCells([cell, edge], 10, 60, 'Self Call'); + }), + this.addEntry('uml sequence invoke call delegation callback activation', function() + { + // TODO: Check if more entries should be converted to compressed XML + return sb.createVertexTemplateFromData('xZRNT8MwDIZ/Ta6oaymD47rBTkiTuMAxW6wmIm0q19s6fj1OE3V0Y2iCA4dK8euP2I+riGxedUuUjX52CqzIHkU2R+conKpuDtaKNDFKZAuRpgl/In264J303qSRCDVdk5CGhJ20WwhKEFo62ChoqritxURkReNMTa2X80LkC68AmgoIkEWHpF3pamlXR7WIFwASdBeb7KXY4RIc5+KBQ/ZGkY4RYY5Egyl1zLqLmmyDXQ6Zx4n5EIf+HkB2BmAjrV3LzftPIPw4hgNn1pQ1a2tH5Cp2QK1miG7vNeu4iJe4pdeY2BtvbCQDGlAljMCQxBJotJ8rWCFYSWY3LvUdmZi68rvkkLiU6QnL1m1xAzHoBOdw61WEb88II9AW67/ydQ2wq1Cy1aAGvOrFfPh6997qDA3g+dxzv3nIL6MPU/8T+kMw8+m4QPgdfrEJNo8PSQj/+s58Ag==', + 10, 60, 'Callback'); + }), + this.createVertexTemplateEntry('html=1;points=[];perimeter=orthogonalPerimeter;', 10, 80, '', 'Activation', null, null, 'uml sequence activation'), + this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;startArrow=oval;startFill=1;endArrow=block;startSize=8;', 60, 0, 'dispatch', 'Found Message 1', null, 'uml sequence message call invoke dispatch'), + this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;startArrow=circle;startFill=1;endArrow=open;startSize=6;endSize=8;', 80, 0, 'dispatch', 'Found Message 2', null, 'uml sequence message call invoke dispatch'), + this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;endArrow=block;', 80, 0, 'dispatch', 'Message', null, 'uml sequence message call invoke dispatch'), + this.addEntry('uml sequence return message', function() + { + var edge = new mxCell('return', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;'); + edge.geometry.setTerminalPoint(new mxPoint(80, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), false); + edge.geometry.relative = true; + edge.edge = true; + + return sb.createEdgeTemplateFromCells([edge], 80, 0, 'Return'); + }), + this.addEntry('uml relation', function() + { + var edge = new mxCell('name', new mxGeometry(0, 0, 0, 0), 'endArrow=block;endFill=1;html=1;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=top;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); + edge.geometry.relative = true; + edge.geometry.x = -1; + edge.edge = true; + + var cell = new mxCell('1', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); + cell.geometry.relative = true; + cell.setConnectable(false); + cell.vertex = true; + edge.insert(cell); + + return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Relation 1'); + }), + this.addEntry('uml association', function() + { + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'endArrow=none;html=1;edgeStyle=orthogonalEdgeStyle;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); + edge.geometry.relative = true; + edge.edge = true; + + var cell1 = new mxCell('parent', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); + cell1.geometry.relative = true; + cell1.setConnectable(false); + cell1.vertex = true; + edge.insert(cell1); + + var cell2 = new mxCell('child', new mxGeometry(1, 0, 0, 0), 'resizable=0;html=1;align=right;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); + cell2.geometry.relative = true; + cell2.setConnectable(false); + cell2.vertex = true; + edge.insert(cell2); + + return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Association 1'); + }), + this.addEntry('uml aggregation', function() + { + var edge = new mxCell('1', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=bottom;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); + edge.geometry.relative = true; + edge.geometry.x = -1; + edge.geometry.y = 3; + edge.edge = true; + + return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Aggregation 1'); + }), + this.addEntry('uml composition', function() + { + var edge = new mxCell('1', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=1;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=bottom;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); + edge.geometry.relative = true; + edge.geometry.x = -1; + edge.geometry.y = 3; + edge.edge = true; + + return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Composition 1'); + }), + this.addEntry('uml relation', function() + { + var edge = new mxCell('Relation', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); + edge.geometry.relative = true; + edge.edge = true; + + var cell1 = new mxCell('0..n', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;'); + cell1.geometry.relative = true; + cell1.setConnectable(false); + cell1.vertex = true; + edge.insert(cell1); + + var cell2 = new mxCell('1', new mxGeometry(1, 0, 0, 0), 'resizable=0;html=1;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;'); + cell2.geometry.relative = true; + cell2.setConnectable(false); + cell2.vertex = true; + edge.insert(cell2); + + return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Relation 2'); + }), + this.createEdgeTemplateEntry('endArrow=open;endSize=12;dashed=1;html=1;', 160, 0, 'Use', 'Dependency', null, 'uml dependency use'), + this.createEdgeTemplateEntry('endArrow=block;endSize=16;endFill=0;html=1;', 160, 0, 'Extends', 'Generalization', null, 'uml generalization extend'), + this.createEdgeTemplateEntry('endArrow=block;startArrow=block;endFill=1;startFill=1;html=1;', 160, 0, '', 'Association 2', null, 'uml association'), + this.createEdgeTemplateEntry('endArrow=open;startArrow=circlePlus;endFill=0;startFill=0;endSize=8;html=1;', 160, 0, '', 'Inner Class', null, 'inner class'), + this.createEdgeTemplateEntry('endArrow=open;startArrow=cross;endFill=0;startFill=0;endSize=8;startSize=10;html=1;', 160, 0, '', 'Terminate', null, 'terminate'), + this.createEdgeTemplateEntry('endArrow=block;dashed=1;endFill=0;endSize=12;html=1;', 160, 0, '', 'Implementation', null, 'realization implementation'), + this.createEdgeTemplateEntry('endArrow=diamondThin;endFill=0;endSize=24;html=1;', 160, 0, '', 'Aggregation 2', null, 'aggregation'), + this.createEdgeTemplateEntry('endArrow=diamondThin;endFill=1;endSize=24;html=1;', 160, 0, '', 'Composition 2', null, 'composition'), + this.createEdgeTemplateEntry('endArrow=open;endFill=1;endSize=12;html=1;', 160, 0, '', 'Association 3', null, 'association') + ]; + + this.addPaletteFunctions('uml', mxResources.get('uml'), expand || false, fns); +}; + +/** + * Adds the BPMN library to the sidebar. + */ +Sidebar.prototype.addBpmnPalette = function(dir, expand) +{ + // Avoids having to bind all functions to "this" + var sb = this; + + var fns = + [ + this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;', 120, 80, 'Task', 'Process', null, null, 'bpmn task process'), + this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;double=1;', 120, 80, 'Transaction', 'Transaction', null, null, 'bpmn transaction'), + this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;', 120, 80, 'Event\nSub-Process', 'Event Sub-Process', null, null, 'bpmn event subprocess sub process sub-process'), + this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;strokeWidth=3;', 120, 80, 'Call Activity', 'Call Activity', null, null, 'bpmn call activity'), + this.addEntry('bpmn subprocess sub process sub-process', function() + { + var cell = new mxCell('Sub-Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(-7, -14); + cell.insert(cell1); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Sub-Process'); + }), + this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'loop', 'subprocess sub process sub-process looped').join(' '), function() + { + var cell = new mxCell('Looped\nSub-Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=mxgraph.bpmn.loop;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(-15, -14); + cell.insert(cell1); + + var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;'); + cell2.vertex = true; + cell2.geometry.relative = true; + cell2.geometry.offset = new mxPoint(1, -14); + cell.insert(cell2); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Looped Sub-Process'); + }), + this.addEntry('bpmn receive task', function() + { + var cell = new mxCell('Receive', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0, 0, 20, 14), 'html=1;shape=message;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(7, 7); + cell.insert(cell1); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Receive Task'); + }), + this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' '), function() + { + var cell = new mxCell('User', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0, 0, 14, 14), 'html=1;shape=mxgraph.bpmn.user_task;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(7, 7); + cell.insert(cell1); + + var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;outlineConnect=0;'); + cell2.vertex = true; + cell2.geometry.relative = true; + cell2.geometry.offset = new mxPoint(-7, -14); + cell.insert(cell2); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'User Task'); + }), + this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'timer_start', 'attached').join(' '), function() + { + var cell = new mxCell('Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(1, 1, 30, 30), 'shape=mxgraph.bpmn.timer_start;perimeter=ellipsePerimeter;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(-40, -15); + cell.insert(cell1); + + return sb.createVertexTemplateFromCells([cell], 120, 95, 'Attached Timer Event 1'); + }), + this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'timer_start', 'attached').join(' '), function() + { + var cell = new mxCell('Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(1, 0, 30, 30), 'shape=mxgraph.bpmn.timer_start;perimeter=ellipsePerimeter;html=1;labelPosition=right;labelBackgroundColor=#ffffff;align=left;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(-15, 10); + cell.insert(cell1); + + return sb.createVertexTemplateFromCells([cell], 135, 80, 'Attached Timer Event 2'); + }), + this.createVertexTemplateEntry('swimlane;html=1;horizontal=0;startSize=20;', 320, 240, 'Pool', 'Pool', null, null, 'bpmn pool'), + this.createVertexTemplateEntry('swimlane;html=1;horizontal=0;swimlaneFillColor=white;swimlaneLine=0;', 300, 120, 'Lane', 'Lane', null, null, 'bpmn lane'), + this.createVertexTemplateEntry('shape=hexagon;html=1;whiteSpace=wrap;perimeter=hexagonPerimeter;rounded=0;', 60, 50, '', 'Conversation', null, null, 'bpmn conversation'), + this.createVertexTemplateEntry('shape=hexagon;html=1;whiteSpace=wrap;perimeter=hexagonPerimeter;strokeWidth=4;rounded=0;', 60, 50, '', 'Call Conversation', null, null, 'bpmn call conversation'), + this.addEntry('bpmn subconversation sub conversation sub-conversation', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 60, 50), 'shape=hexagon;whiteSpace=wrap;html=1;perimeter=hexagonPerimeter;rounded=0;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(-7, -14); + cell.insert(cell1); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Sub-Conversation'); + }), + this.addEntry('bpmn data object', function() + { + var cell = new mxCell('', new mxGeometry(0, 0, 40, 60), 'shape=note;whiteSpace=wrap;size=16;html=1;'); + cell.vertex = true; + + var cell1 = new mxCell('', new mxGeometry(0, 0, 14, 14), 'html=1;shape=singleArrow;arrowWidth=0.4;arrowSize=0.4;outlineConnect=0;'); + cell1.vertex = true; + cell1.geometry.relative = true; + cell1.geometry.offset = new mxPoint(2, 2); + cell.insert(cell1); + + var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;whiteSpace=wrap;shape=parallelMarker;outlineConnect=0;'); + cell2.vertex = true; + cell2.geometry.relative = true; + cell2.geometry.offset = new mxPoint(-7, -14); + cell.insert(cell2); + + return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Data Object'); + }), + this.createVertexTemplateEntry('shape=datastore;whiteSpace=wrap;html=1;', 60, 60, '', 'Data Store', null, null, 'bpmn data store'), + this.createVertexTemplateEntry('shape=plus;html=1;outlineConnect=0;', 14, 14, '', 'Sub-Process Marker', null, null, 'bpmn subprocess sub process sub-process marker'), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.loop;html=1;outlineConnect=0;', 14, 14, '', 'Loop Marker', null, null, 'bpmn loop marker'), + this.createVertexTemplateEntry('shape=parallelMarker;html=1;outlineConnect=0;', 14, 14, '', 'Parallel MI Marker', null, null, 'bpmn parallel mi marker'), + this.createVertexTemplateEntry('shape=parallelMarker;direction=south;html=1;outlineConnect=0;', 14, 14, '', 'Sequential MI Marker', null, null, 'bpmn sequential mi marker'), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.ad_hoc;fillColor=#000000;html=1;outlineConnect=0;', 14, 14, '', 'Ad Hoc Marker', null, null, 'bpmn ad hoc marker'), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.compensation;html=1;outlineConnect=0;', 14, 14, '', 'Compensation Marker', null, null, 'bpmn compensation marker'), + this.createVertexTemplateEntry('shape=message;whiteSpace=wrap;html=1;outlineConnect=0;fillColor=#000000;strokeColor=#ffffff;strokeWidth=2;', 40, 30, '', 'Send Task', null, null, 'bpmn send task'), + this.createVertexTemplateEntry('shape=message;whiteSpace=wrap;html=1;outlineConnect=0;', 40, 30, '', 'Receive Task', null, null, 'bpmn receive task'), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.user_task;html=1;outlineConnect=0;', 14, 14, '', 'User Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' ')), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.manual_task;html=1;outlineConnect=0;', 14, 14, '', 'Manual Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' ')), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.business_rule_task;html=1;outlineConnect=0;', 14, 14, '', 'Business Rule Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'business_rule_task').join(' ')), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.service_task;html=1;outlineConnect=0;', 14, 14, '', 'Service Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'service_task').join(' ')), + this.createVertexTemplateEntry('shape=mxgraph.bpmn.script_task;html=1;outlineConnect=0;', 14, 14, '', 'Script Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'script_task').join(' ')), + this.createVertexTemplateEntry('html=1;shape=mxgraph.flowchart.annotation_2;align=left;', 50, 100, '', 'Annotation', null, null, this.getTagsForStencil('bpmn', 'annotation_1', 'bpmn business process model ').join(' ')), + this.createVertexTemplateEntry('rounded=1;arcSize=10;dashed=1;strokeColor=#000000;fillColor=none;gradientColor=none;dashPattern=8 3 1 3;strokeWidth=2;', + 200, 200, '', 'Group', null, null, this.getTagsForStencil('bpmn', 'group', 'bpmn business process model ').join(' ')), + this.createEdgeTemplateEntry('endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Sequence Flow', null, 'bpmn sequence flow'), + this.createEdgeTemplateEntry('startArrow=dash;startSize=8;endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Default Flow', null, 'bpmn default flow'), + this.createEdgeTemplateEntry('startArrow=diamondThin;startFill=0;startSize=14;endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Conditional Flow', null, 'bpmn conditional flow'), + this.createEdgeTemplateEntry('startArrow=oval;startFill=0;startSize=7;endArrow=block;endFill=0;endSize=10;dashed=1;html=1;', 100, 0, '', 'Message Flow 1', null, 'bpmn message flow'), + this.addEntry('bpmn message flow', function() + { + var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'startArrow=oval;startFill=0;startSize=7;endArrow=block;endFill=0;endSize=10;dashed=1;html=1;'); + edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); + edge.geometry.setTerminalPoint(new mxPoint(100, 0), false); + edge.geometry.relative = true; + edge.edge = true; + + var cell = new mxCell('', new mxGeometry(0, 0, 20, 14), 'shape=message;html=1;outlineConnect=0;'); + cell.geometry.relative = true; + cell.vertex = true; + cell.geometry.offset = new mxPoint(-10, -7); + edge.insert(cell); + + return sb.createEdgeTemplateFromCells([edge], 100, 0, 'Message Flow 2'); + }), + this.createEdgeTemplateEntry('shape=link;html=1;', 100, 0, '', 'Link', null, 'bpmn link') + ]; + + this.addPaletteFunctions('bpmn', 'BPMN ' + mxResources.get('general'), false, fns); +}; + +/** + * Creates and returns the given title element. + */ +Sidebar.prototype.createTitle = function(label) +{ + var elt = document.createElement('a'); + elt.setAttribute('href', 'javascript:void(0);'); + elt.setAttribute('title', mxResources.get('sidebarTooltip')); + elt.className = 'geTitle'; + mxUtils.write(elt, label); + + return elt; +}; + +/** + * Creates a thumbnail for the given cells. + */ +Sidebar.prototype.createThumb = function(cells, width, height, parent, title, showLabel, showTitle, realWidth, realHeight) +{ + this.graph.labelsVisible = (showLabel == null || showLabel); + var fo = mxClient.NO_FO; + mxClient.NO_FO = Editor.prototype.originalNoForeignObject; + this.graph.view.scaleAndTranslate(1, 0, 0); + this.graph.addCells(cells); + var bounds = this.graph.getGraphBounds(); + var s = Math.floor(Math.min((width - 2 * this.thumbBorder) / bounds.width, + (height - 2 * this.thumbBorder) / bounds.height) * 100) / 100; + this.graph.view.scaleAndTranslate(s, Math.floor((width - bounds.width * s) / 2 / s - bounds.x), + Math.floor((height - bounds.height * s) / 2 / s - bounds.y)); + var node = null; + + // For supporting HTML labels in IE9 standards mode the container is cloned instead + if (this.graph.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO) + { + node = this.graph.view.getCanvas().ownerSVGElement.cloneNode(true); + } + // LATER: Check if deep clone can be used for quirks if container in DOM + else + { + node = this.graph.container.cloneNode(false); + node.innerHTML = this.graph.container.innerHTML; + + // Workaround for clipping in older IE versions + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + node.firstChild.style.overflow = 'visible'; + } + } + + this.graph.getModel().clear(); + mxClient.NO_FO = fo; + + // Catch-all event handling + if (mxClient.IS_IE6) + { + parent.style.backgroundImage = 'url(' + this.editorUi.editor.transparentImage + ')'; + } + + node.style.position = 'relative'; + node.style.overflow = 'hidden'; + node.style.cursor = 'move'; + node.style.left = this.thumbBorder + 'px'; + node.style.top = this.thumbBorder + 'px'; + node.style.width = width + 'px'; + node.style.height = height + 'px'; + node.style.visibility = ''; + node.style.minWidth = ''; + node.style.minHeight = ''; + + parent.appendChild(node); + + // Adds title for sidebar entries + if (this.sidebarTitles && title != null && showTitle != false) + { + var border = (mxClient.IS_QUIRKS) ? 2 * this.thumbPadding + 2: 0; + parent.style.height = (this.thumbHeight + border + this.sidebarTitleSize + 8) + 'px'; + + var div = document.createElement('div'); + div.style.fontSize = this.sidebarTitleSize + 'px'; + div.style.color = '#303030'; + div.style.textAlign = 'center'; + div.style.whiteSpace = 'nowrap'; + + if (mxClient.IS_IE) + { + div.style.height = (this.sidebarTitleSize + 12) + 'px'; + } + + div.style.paddingTop = '4px'; + mxUtils.write(div, title); + parent.appendChild(div); + } + + return bounds; +}; + +/** + * Creates and returns a new palette item for the given image. + */ +Sidebar.prototype.createItem = function(cells, title, showLabel, showTitle, width, height, allowCellsInserted) +{ + var elt = document.createElement('a'); + elt.setAttribute('href', 'javascript:void(0);'); + elt.className = 'geItem'; + elt.style.overflow = 'hidden'; + var border = (mxClient.IS_QUIRKS) ? 8 + 2 * this.thumbPadding : 2 * this.thumbBorder; + elt.style.width = (this.thumbWidth + border) + 'px'; + elt.style.height = (this.thumbHeight + border) + 'px'; + elt.style.padding = this.thumbPadding + 'px'; + + if (mxClient.IS_IE6) + { + elt.style.border = 'none'; + } + + // Blocks default click action + mxEvent.addListener(elt, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + this.createThumb(cells, this.thumbWidth, this.thumbHeight, elt, title, showLabel, showTitle, width, height); + var bounds = new mxRectangle(0, 0, width, height); + + if (cells.length > 1 || cells[0].vertex) + { + var ds = this.createDragSource(elt, this.createDropHandler(cells, true, allowCellsInserted, + bounds), this.createDragPreview(width, height), cells, bounds); + this.addClickHandler(elt, ds, cells); + + // Uses guides for vertices only if enabled in graph + ds.isGuidesEnabled = mxUtils.bind(this, function() + { + return this.editorUi.editor.graph.graphHandler.guidesEnabled; + }); + } + else if (cells[0] != null && cells[0].edge) + { + var ds = this.createDragSource(elt, this.createDropHandler(cells, false, allowCellsInserted, + bounds), this.createDragPreview(width, height), cells, bounds); + this.addClickHandler(elt, ds, cells); + } + + // Shows a tooltip with the rendered cell + if (!mxClient.IS_IOS) + { + mxEvent.addGestureListeners(elt, null, mxUtils.bind(this, function(evt) + { + if (mxEvent.isMouseEvent(evt)) + { + this.showTooltip(elt, cells, bounds.width, bounds.height, title, showLabel); + } + })); + } + + return elt; +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.updateShapes = function(source, targets) +{ + var graph = this.editorUi.editor.graph; + var sourceCellStyle = graph.getCellStyle(source); + var result = []; + + graph.model.beginUpdate(); + try + { + var cellStyle = graph.getModel().getStyle(source); + + // Lists the styles to carry over from the existing shape + var styles = ['shadow', 'dashed', 'dashPattern', 'fontFamily', 'fontSize', 'fontColor', 'align', 'startFill', + 'startSize', 'endFill', 'endSize', 'strokeColor', 'strokeWidth', 'fillColor', 'gradientColor', + 'html', 'part', 'noEdgeStyle', 'edgeStyle', 'elbow', 'childLayout', 'recursiveResize', + 'container', 'collapsible', 'connectable']; + + for (var i = 0; i < targets.length; i++) + { + var targetCell = targets[i]; + + if ((graph.getModel().isVertex(targetCell) == graph.getModel().isVertex(source)) || + (graph.getModel().isEdge(targetCell) == graph.getModel().isEdge(source))) + { + var state = graph.view.getState(targetCell); + var style = (state != null) ? state.style : graph.getCellStyle(targets[i]); + graph.getModel().setStyle(targetCell, cellStyle); + + // Removes all children of composite cells + if (state != null && mxUtils.getValue(state.style, 'composite', '0') == '1') + { + var childCount = graph.model.getChildCount(targetCell); + + for (var j = childCount; j >= 0; j--) + { + graph.model.remove(graph.model.getChildAt(targetCell, j)); + } + } + + if (style != null) + { + // Replaces the participant style in the lifeline shape with the target shape + if (style[mxConstants.STYLE_SHAPE] == 'umlLifeline' && + sourceCellStyle[mxConstants.STYLE_SHAPE] != 'umlLifeline') + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, 'umlLifeline', [targetCell]); + graph.setCellStyles('participant', sourceCellStyle[mxConstants.STYLE_SHAPE], [targetCell]); + } + + for (var j = 0; j < styles.length; j++) + { + var value = style[styles[j]]; + + if (value != null) + { + graph.setCellStyles(styles[j], value, [targetCell]); + } + } + } + + result.push(targetCell); + } + } + } + finally + { + graph.model.endUpdate(); + } + + return result; +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createDropHandler = function(cells, allowSplit, allowCellsInserted, bounds) +{ + allowCellsInserted = (allowCellsInserted != null) ? allowCellsInserted : true; + + return mxUtils.bind(this, function(graph, evt, target, x, y, force) + { + var elt = (force) ? null : ((mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt)) ? + document.elementFromPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)) : + mxEvent.getSource(evt)); + + while (elt != null && elt != this.container) + { + elt = elt.parentNode; + } + + if (elt == null && graph.isEnabled()) + { + cells = graph.getImportableCells(cells); + + if (cells.length > 0) + { + graph.stopEditing(); + + // Holding alt while mouse is released ignores drop target + var validDropTarget = (target != null && !mxEvent.isAltDown(evt)) ? + graph.isValidDropTarget(target, cells, evt) : false; + var select = null; + + if (target != null && !validDropTarget) + { + target = null; + } + + if (!graph.isCellLocked(target || graph.getDefaultParent())) + { + graph.model.beginUpdate(); + try + { + x = Math.round(x); + y = Math.round(y); + + // Splits the target edge or inserts into target group + if (allowSplit && graph.isSplitTarget(target, cells, evt)) + { + var clones = graph.cloneCells(cells); + graph.splitEdge(target, clones, null, + x - bounds.width / 2, y - bounds.height / 2); + select = clones; + } + else if (cells.length > 0) + { + select = graph.importCells(cells, x, y, target); + } + + // Executes parent layout hooks for position/order + if (graph.layoutManager != null) + { + var layout = graph.layoutManager.getLayout(target); + + if (layout != null) + { + var s = graph.view.scale; + var tr = graph.view.translate; + var tx = (x + tr.x) * s; + var ty = (y + tr.y) * s; + + for (var i = 0; i < select.length; i++) + { + layout.moveCell(select[i], tx, ty); + } + } + } + + if (allowCellsInserted) + { + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); + } + } + finally + { + graph.model.endUpdate(); + } + + if (select != null && select.length > 0) + { + graph.scrollCellToVisible(select[0]); + graph.setSelectionCells(select); + } + + if (graph.editAfterInsert && evt != null && mxEvent.isMouseEvent(evt) && + select != null && select.length == 1) + { + window.setTimeout(function() + { + graph.startEditing(select[0]); + }, 0); + } + } + } + + mxEvent.consume(evt); + } + }); +}; + +/** + * Creates and returns a preview element for the given width and height. + */ +Sidebar.prototype.createDragPreview = function(width, height) +{ + var elt = document.createElement('div'); + elt.style.border = this.dragPreviewBorder; + elt.style.width = width + 'px'; + elt.style.height = height + 'px'; + + return elt; +}; + +/** + * Creates a drag source for the given element. + */ +Sidebar.prototype.dropAndConnect = function(source, targets, direction, dropCellIndex, evt) +{ + var geo = this.getDropAndConnectGeometry(source, targets[dropCellIndex], direction, targets); + + // Targets without the new edge for selection + var tmp = []; + + if (geo != null) + { + var graph = this.editorUi.editor.graph; + var editingCell = null; + + graph.model.beginUpdate(); + try + { + var sourceGeo = graph.getCellGeometry(source); + var geo2 = graph.getCellGeometry(targets[dropCellIndex]); + + // Handles special case where target should be ignored for stack layouts + var targetParent = graph.model.getParent(source); + var validLayout = true; + + // Ignores parent if it has a stack layout + if (graph.layoutManager != null) + { + var layout = graph.layoutManager.getLayout(targetParent); + + // LATER: Use parent of parent if valid layout + if (layout != null && layout.constructor == mxStackLayout) + { + validLayout = false; + + var tmp = graph.view.getState(targetParent); + + // Offsets by parent position + if (tmp != null) + { + var offset = new mxPoint((tmp.x / graph.view.scale - graph.view.translate.x), + (tmp.y / graph.view.scale - graph.view.translate.y)); + geo.x += offset.x; + geo.y += offset.y; + var pt = geo.getTerminalPoint(false); + + if (pt != null) + { + pt.x += offset.x; + pt.y += offset.y; + } + } + } + } + + var dx = geo2.x; + var dy = geo2.y; + + // Ignores geometry of edges + if (graph.model.isEdge(targets[dropCellIndex])) + { + dx = 0; + dy = 0; + } + + var useParent = graph.model.isEdge(source) || (sourceGeo != null && !sourceGeo.relative && validLayout); + targets = graph.importCells(targets, (geo.x - (useParent ? dx : 0)), + (geo.y - (useParent ? dy : 0)), (useParent) ? targetParent : null); + tmp = targets; + + if (graph.model.isEdge(source)) + { + // Adds new terminal to edge + // LATER: Push new terminal out radially from edge start point + graph.model.setTerminal(source, targets[dropCellIndex], direction == mxConstants.DIRECTION_NORTH); + } + else if (graph.model.isEdge(targets[dropCellIndex])) + { + // Adds new outgoing connection to vertex and clears points + graph.model.setTerminal(targets[dropCellIndex], source, true); + var geo3 = graph.getCellGeometry(targets[dropCellIndex]); + geo3.points = null; + + if (geo3.getTerminalPoint(false) != null) + { + geo3.setTerminalPoint(geo.getTerminalPoint(false), false); + } + else if (useParent && graph.model.isVertex(targetParent)) + { + // Adds parent offset to other nodes + var tmpState = graph.view.getState(targetParent); + var offset = (tmpState.cell != graph.view.currentRoot) ? + new mxPoint((tmpState.x / graph.view.scale - graph.view.translate.x), + (tmpState.y / graph.view.scale - graph.view.translate.y)) : new mxPoint(0, 0); + + graph.cellsMoved(targets, offset.x, offset.y, null, null, true); + } + } + else + { + geo2 = graph.getCellGeometry(targets[dropCellIndex]); + dx = geo.x - Math.round(geo2.x); + dy = geo.y - Math.round(geo2.y); + geo.x = Math.round(geo2.x); + geo.y = Math.round(geo2.y); + graph.model.setGeometry(targets[dropCellIndex], geo); + graph.cellsMoved(targets, dx, dy, null, null, true); + tmp = targets.slice(); + editingCell = (tmp.length == 1) ? tmp[0] : null; + targets.push(graph.insertEdge(null, null, '', source, targets[dropCellIndex], + graph.createCurrentEdgeStyle())); + } + + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', targets)); + } + finally + { + graph.model.endUpdate(); + } + + if (graph.editAfterInsert && evt != null && mxEvent.isMouseEvent(evt) && + editingCell != null) + { + window.setTimeout(function() + { + graph.startEditing(editingCell); + }, 0); + } + } + + return tmp; +}; + +/** + * Creates a drag source for the given element. + */ +Sidebar.prototype.getDropAndConnectGeometry = function(source, target, direction, targets) +{ + var graph = this.editorUi.editor.graph; + var view = graph.view; + var keepSize = targets.length > 1; + var geo = graph.getCellGeometry(source); + var geo2 = graph.getCellGeometry(target); + + if (geo != null && geo2 != null) + { + geo2 = geo2.clone(); + + if (graph.model.isEdge(source)) + { + var state = graph.view.getState(source); + var pts = state.absolutePoints; + var p0 = pts[0]; + var pe = pts[pts.length - 1]; + + if (direction == mxConstants.DIRECTION_NORTH) + { + geo2.x = p0.x / view.scale - view.translate.x - geo2.width / 2; + geo2.y = p0.y / view.scale - view.translate.y - geo2.height / 2; + } + else + { + geo2.x = pe.x / view.scale - view.translate.x - geo2.width / 2; + geo2.y = pe.y / view.scale - view.translate.y - geo2.height / 2; + } + } + else + { + if (geo.relative) + { + var state = graph.view.getState(source); + geo = geo.clone(); + geo.x = (state.x - view.translate.x) / view.scale; + geo.y = (state.y - view.translate.y) / view.scale; + } + + var length = graph.defaultEdgeLength; + + // Maintains edge length + if (graph.model.isEdge(target) && geo2.getTerminalPoint(true) != null && geo2.getTerminalPoint(false) != null) + { + var p0 = geo2.getTerminalPoint(true); + var pe = geo2.getTerminalPoint(false); + var dx = pe.x - p0.x; + var dy = pe.y - p0.y; + + length = Math.sqrt(dx * dx + dy * dy); + + geo2.x = geo.getCenterX(); + geo2.y = geo.getCenterY(); + geo2.width = 1; + geo2.height = 1; + + if (direction == mxConstants.DIRECTION_NORTH) + { + geo2.height = length + geo2.y = geo.y - length; + geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y), false); + } + else if (direction == mxConstants.DIRECTION_EAST) + { + geo2.width = length + geo2.x = geo.x + geo.width; + geo2.setTerminalPoint(new mxPoint(geo2.x + geo2.width, geo2.y), false); + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + geo2.height = length + geo2.y = geo.y + geo.height; + geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y + geo2.height), false); + } + else if (direction == mxConstants.DIRECTION_WEST) + { + geo2.width = length + geo2.x = geo.x - length; + geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y), false); + } + } + else + { + // Try match size or ignore if width or height < 45 which + // is considered special enough to be ignored here + if (!keepSize && geo2.width > 45 && geo2.height > 45 && + geo.width > 45 && geo.height > 45) + { + geo2.width = geo2.width * (geo.height / geo2.height); + geo2.height = geo.height; + } + + geo2.x = geo.x + geo.width / 2 - geo2.width / 2; + geo2.y = geo.y + geo.height / 2 - geo2.height / 2; + + if (direction == mxConstants.DIRECTION_NORTH) + { + geo2.y = geo2.y - geo.height / 2 - geo2.height / 2 - length; + } + else if (direction == mxConstants.DIRECTION_EAST) + { + geo2.x = geo2.x + geo.width / 2 + geo2.width / 2 + length; + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + geo2.y = geo2.y + geo.height / 2 + geo2.height / 2 + length; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + geo2.x = geo2.x - geo.width / 2 - geo2.width / 2 - length; + } + + // Adds offset to match cells without connecting edge + if (graph.model.isEdge(target) && geo2.getTerminalPoint(true) != null && target.getTerminal(false) != null) + { + var targetGeo = graph.getCellGeometry(target.getTerminal(false)); + + if (targetGeo != null) + { + if (direction == mxConstants.DIRECTION_NORTH) + { + geo2.x -= targetGeo.getCenterX(); + geo2.y -= targetGeo.getCenterY() + targetGeo.height / 2; + } + else if (direction == mxConstants.DIRECTION_EAST) + { + geo2.x -= targetGeo.getCenterX() - targetGeo.width / 2; + geo2.y -= targetGeo.getCenterY(); + } + else if (direction == mxConstants.DIRECTION_SOUTH) + { + geo2.x -= targetGeo.getCenterX(); + geo2.y -= targetGeo.getCenterY() - targetGeo.height / 2; + } + else if (direction == mxConstants.DIRECTION_WEST) + { + geo2.x -= targetGeo.getCenterX() + targetGeo.width / 2; + geo2.y -= targetGeo.getCenterY(); + } + } + } + } + } + } + + return geo2; +}; + +/** + * Creates a drag source for the given element. + */ +Sidebar.prototype.createDragSource = function(elt, dropHandler, preview, cells, bounds) +{ + // Checks if the cells contain any vertices + var ui = this.editorUi; + var graph = ui.editor.graph; + var freeSourceEdge = null; + var firstVertex = null; + var sidebar = this; + + for (var i = 0; i < cells.length; i++) + { + if (firstVertex == null && this.editorUi.editor.graph.model.isVertex(cells[i])) + { + firstVertex = i; + } + else if (freeSourceEdge == null && this.editorUi.editor.graph.model.isEdge(cells[i]) && + this.editorUi.editor.graph.model.getTerminal(cells[i], true) == null) + { + freeSourceEdge = i; + } + + if (firstVertex != null && freeSourceEdge != null) + { + break; + } + } + + var dragSource = mxUtils.makeDraggable(elt, this.editorUi.editor.graph, mxUtils.bind(this, function(graph, evt, target, x, y) + { + if (this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + if (cells != null && currentStyleTarget != null && activeArrow == styleTarget) + { + var tmp = graph.isCellSelected(currentStyleTarget.cell) ? graph.getSelectionCells() : [currentStyleTarget.cell]; + var updatedCells = this.updateShapes((graph.model.isEdge(currentStyleTarget.cell)) ? cells[0] : cells[firstVertex], tmp); + graph.setSelectionCells(updatedCells); + } + else if (cells != null && activeArrow != null && currentTargetState != null && activeArrow != styleTarget) + { + var index = (graph.model.isEdge(currentTargetState.cell) || freeSourceEdge == null) ? firstVertex : freeSourceEdge; + graph.setSelectionCells(this.dropAndConnect(currentTargetState.cell, cells, direction, index, evt)); + } + else + { + dropHandler.apply(this, arguments); + } + + if (this.editorUi.hoverIcons != null) + { + this.editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); + } + }), preview, 0, 0, graph.autoscroll, true, true); + + // Stops dragging if cancel is pressed + graph.addListener(mxEvent.ESCAPE, function(sender, evt) + { + if (dragSource.isActive()) + { + dragSource.reset(); + } + }); + + // Overrides mouseDown to ignore popup triggers + var mouseDown = dragSource.mouseDown; + + dragSource.mouseDown = function(evt) + { + if (!mxEvent.isPopupTrigger(evt) && !mxEvent.isMultiTouchEvent(evt)) + { + graph.stopEditing(); + mouseDown.apply(this, arguments); + } + }; + + // Workaround for event redirection via image tag in quirks and IE8 + function createArrow(img, tooltip) + { + var arrow = null; + + if (mxClient.IS_IE && !mxClient.IS_SVG) + { + // Workaround for PNG images in IE6 + if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') + { + arrow = document.createElement(mxClient.VML_PREFIX + ':image'); + arrow.setAttribute('src', img.src); + arrow.style.borderStyle = 'none'; + } + else + { + arrow = document.createElement('div'); + arrow.style.backgroundImage = 'url(' + img.src + ')'; + arrow.style.backgroundPosition = 'center'; + arrow.style.backgroundRepeat = 'no-repeat'; + } + + arrow.style.width = (img.width + 4) + 'px'; + arrow.style.height = (img.height + 4) + 'px'; + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + } + else + { + arrow = mxUtils.createImage(img.src); + arrow.style.width = img.width + 'px'; + arrow.style.height = img.height + 'px'; + } + + if (tooltip != null) + { + arrow.setAttribute('title', tooltip); + } + + mxUtils.setOpacity(arrow, (img == this.refreshTarget) ? 30 : 20); + arrow.style.position = 'absolute'; + arrow.style.cursor = 'crosshair'; + + return arrow; + }; + + var currentTargetState = null; + var currentStateHandle = null; + var currentStyleTarget = null; + var activeTarget = false; + + var arrowUp = createArrow(this.triangleUp, mxResources.get('connect')); + var arrowRight = createArrow(this.triangleRight, mxResources.get('connect')); + var arrowDown = createArrow(this.triangleDown, mxResources.get('connect')); + var arrowLeft = createArrow(this.triangleLeft, mxResources.get('connect')); + var styleTarget = createArrow(this.refreshTarget, mxResources.get('replace')); + // Workaround for actual parentNode not being updated in old IE + var styleTargetParent = null; + var roundSource = createArrow(this.roundDrop); + var roundTarget = createArrow(this.roundDrop); + var direction = mxConstants.DIRECTION_NORTH; + var activeArrow = null; + + function checkArrow(x, y, bounds, arrow) + { + if (arrow.parentNode != null) + { + if (mxUtils.contains(bounds, x, y)) + { + mxUtils.setOpacity(arrow, 100); + activeArrow = arrow; + } + else + { + mxUtils.setOpacity(arrow, (arrow == styleTarget) ? 30 : 20); + } + } + + return bounds; + }; + + // Hides guides and preview if target is active + var dsCreatePreviewElement = dragSource.createPreviewElement; + + // Stores initial size of preview element + dragSource.createPreviewElement = function(graph) + { + var elt = dsCreatePreviewElement.apply(this, arguments); + + // Pass-through events required to tooltip on replace shape + if (mxClient.IS_SVG) + { + elt.style.pointerEvents = 'none'; + } + + this.previewElementWidth = elt.style.width; + this.previewElementHeight = elt.style.height; + + return elt; + }; + + // Shows/hides hover icons + var dragEnter = dragSource.dragEnter; + dragSource.dragEnter = function(graph, evt) + { + if (ui.hoverIcons != null) + { + ui.hoverIcons.setDisplay('none'); + } + + dragEnter.apply(this, arguments); + }; + + var dragExit = dragSource.dragExit; + dragSource.dragExit = function(graph, evt) + { + if (ui.hoverIcons != null) + { + ui.hoverIcons.setDisplay(''); + } + + dragExit.apply(this, arguments); + }; + + dragSource.dragOver = function(graph, evt) + { + mxDragSource.prototype.dragOver.apply(this, arguments); + + if (this.currentGuide != null && activeArrow != null) + { + this.currentGuide.hide(); + } + + if (this.previewElement != null) + { + var view = graph.view; + + if (currentStyleTarget != null && activeArrow == styleTarget) + { + this.previewElement.style.display = (graph.model.isEdge(currentStyleTarget.cell)) ? 'none' : ''; + + this.previewElement.style.left = currentStyleTarget.x + 'px'; + this.previewElement.style.top = currentStyleTarget.y + 'px'; + this.previewElement.style.width = currentStyleTarget.width + 'px'; + this.previewElement.style.height = currentStyleTarget.height + 'px'; + } + else if (currentTargetState != null && activeArrow != null) + { + var index = (graph.model.isEdge(currentTargetState.cell) || freeSourceEdge == null) ? firstVertex : freeSourceEdge; + var geo = sidebar.getDropAndConnectGeometry(currentTargetState.cell, cells[index], direction, cells); + var geo2 = (!graph.model.isEdge(currentTargetState.cell)) ? graph.getCellGeometry(currentTargetState.cell) : null; + var geo3 = graph.getCellGeometry(cells[index]); + var parent = graph.model.getParent(currentTargetState.cell); + var dx = view.translate.x * view.scale; + var dy = view.translate.y * view.scale; + + if (geo2 != null && !geo2.relative && graph.model.isVertex(parent) && parent != view.currentRoot) + { + var pState = view.getState(parent); + + dx = pState.x; + dy = pState.y; + } + + var dx2 = geo3.x; + var dy2 = geo3.y; + + // Ignores geometry of edges + if (graph.model.isEdge(cells[index])) + { + dx2 = 0; + dy2 = 0; + } + + // Shows preview at drop location + this.previewElement.style.left = ((geo.x - dx2) * view.scale + dx) + 'px'; + this.previewElement.style.top = ((geo.y - dy2) * view.scale + dy) + 'px'; + + if (cells.length == 1) + { + this.previewElement.style.width = (geo.width * view.scale) + 'px'; + this.previewElement.style.height = (geo.height * view.scale) + 'px'; + } + + this.previewElement.style.display = ''; + } + else if (dragSource.currentHighlight.state != null && + graph.model.isEdge(dragSource.currentHighlight.state.cell)) + { + // Centers drop cells when splitting edges + this.previewElement.style.left = Math.round(parseInt(this.previewElement.style.left) - + bounds.width * view.scale / 2) + 'px'; + this.previewElement.style.top = Math.round(parseInt(this.previewElement.style.top) - + bounds.height * view.scale / 2) + 'px'; + } + else + { + this.previewElement.style.width = this.previewElementWidth; + this.previewElement.style.height = this.previewElementHeight; + this.previewElement.style.display = ''; + } + } + }; + + var startTime = new Date().getTime(); + var timeOnTarget = 0; + var prev = null; + + // Gets source cell style to compare shape below + var sourceCellStyle = this.editorUi.editor.graph.getCellStyle(cells[0]); + + // Allows drop into cell only if target is a valid root + dragSource.getDropTarget = mxUtils.bind(this, function(graph, x, y, evt) + { + // Alt means no targets at all + // LATER: Show preview where result will go + var cell = (!mxEvent.isAltDown(evt) && cells != null) ? graph.getCellAt(x, y) : null; + + // Uses connectable parent vertex if one exists + if (cell != null && !this.graph.isCellConnectable(cell)) + { + var parent = this.graph.getModel().getParent(cell); + + if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent)) + { + cell = parent; + } + } + + // Ignores locked cells + if (graph.isCellLocked(cell)) + { + cell = null; + } + + var state = graph.view.getState(cell); + activeArrow = null; + var bbox = null; + + // Time on target + if (prev != state) + { + prev = state; + startTime = new Date().getTime(); + timeOnTarget = 0; + + if (this.updateThread != null) + { + window.clearTimeout(this.updateThread); + } + + if (state != null) + { + this.updateThread = window.setTimeout(function() + { + if (activeArrow == null) + { + prev = state; + dragSource.getDropTarget(graph, x, y, evt); + } + }, this.dropTargetDelay + 10); + } + } + else + { + timeOnTarget = new Date().getTime() - startTime; + } + + // Shift means disabled, delayed on cells with children, shows after this.dropTargetDelay, hides after 2500ms + if (timeOnTarget < 2500 && state != null && !mxEvent.isShiftDown(evt) && + // If shape is equal or target has no stroke, fill and gradient then use longer delay except for images + (((mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE) != mxUtils.getValue(sourceCellStyle, mxConstants.STYLE_SHAPE) && + (mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE) != mxConstants.NONE || + mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE) != mxConstants.NONE || + mxUtils.getValue(state.style, mxConstants.STYLE_GRADIENTCOLOR, mxConstants.NONE) != mxConstants.NONE)) || + mxUtils.getValue(sourceCellStyle, mxConstants.STYLE_SHAPE) == 'image') || + timeOnTarget > 1500 || graph.model.isEdge(state.cell)) && (timeOnTarget > this.dropTargetDelay) && + ((graph.model.isVertex(state.cell) && firstVertex != null) || + (graph.model.isEdge(state.cell) && graph.model.isEdge(cells[0])))) + { + currentStyleTarget = state; + var tmp = (graph.model.isEdge(state.cell)) ? graph.view.getPoint(state) : + new mxPoint(state.getCenterX(), state.getCenterY()); + tmp = new mxRectangle(tmp.x - this.refreshTarget.width / 2, tmp.y - this.refreshTarget.height / 2, + this.refreshTarget.width, this.refreshTarget.height); + + styleTarget.style.left = Math.floor(tmp.x) + 'px'; + styleTarget.style.top = Math.floor(tmp.y) + 'px'; + + if (styleTargetParent == null) + { + graph.container.appendChild(styleTarget); + styleTargetParent = styleTarget.parentNode; + } + + checkArrow(x, y, tmp, styleTarget); + } + // Does not reset on ignored edges + else if (currentStyleTarget == null || !mxUtils.contains(currentStyleTarget, x, y) || + (timeOnTarget > 1500 && !mxEvent.isShiftDown(evt))) + { + currentStyleTarget = null; + + if (styleTargetParent != null) + { + styleTarget.parentNode.removeChild(styleTarget); + styleTargetParent = null; + } + } + else if (currentStyleTarget != null && styleTargetParent != null) + { + // Sets active Arrow as side effect + var tmp = (graph.model.isEdge(currentStyleTarget.cell)) ? graph.view.getPoint(currentStyleTarget) : new mxPoint(currentStyleTarget.getCenterX(), currentStyleTarget.getCenterY()); + tmp = new mxRectangle(tmp.x - this.refreshTarget.width / 2, tmp.y - this.refreshTarget.height / 2, + this.refreshTarget.width, this.refreshTarget.height); + checkArrow(x, y, tmp, styleTarget); + } + + // Checks if inside bounds + if (activeTarget && currentTargetState != null && !mxEvent.isAltDown(evt) && activeArrow == null) + { + // LATER: Use hit-detection for edges + bbox = mxRectangle.fromRectangle(currentTargetState); + + if (graph.model.isEdge(currentTargetState.cell)) + { + var pts = currentTargetState.absolutePoints; + + if (roundSource.parentNode != null) + { + var p0 = pts[0]; + bbox.add(checkArrow(x, y, new mxRectangle(p0.x - this.roundDrop.width / 2, + p0.y - this.roundDrop.height / 2, this.roundDrop.width, this.roundDrop.height), roundSource)); + } + + if (roundTarget.parentNode != null) + { + var pe = pts[pts.length - 1]; + bbox.add(checkArrow(x, y, new mxRectangle(pe.x - this.roundDrop.width / 2, + pe.y - this.roundDrop.height / 2, + this.roundDrop.width, this.roundDrop.height), roundTarget)); + } + } + else + { + var bds = mxRectangle.fromRectangle(currentTargetState); + + // Uses outer bounding box to take rotation into account + if (currentTargetState.shape != null && currentTargetState.shape.boundingBox != null) + { + bds = mxRectangle.fromRectangle(currentTargetState.shape.boundingBox); + } + + bds.grow(this.graph.tolerance); + bds.grow(HoverIcons.prototype.arrowSpacing); + + var handler = this.graph.selectionCellsHandler.getHandler(currentTargetState.cell); + + if (handler != null) + { + bds.x -= handler.horizontalOffset / 2; + bds.y -= handler.verticalOffset / 2; + bds.width += handler.horizontalOffset; + bds.height += handler.verticalOffset; + + // Adds bounding box of rotation handle to avoid overlap + if (handler.rotationShape != null && handler.rotationShape.node != null && + handler.rotationShape.node.style.visibility != 'hidden' && + handler.rotationShape.node.style.display != 'none' && + handler.rotationShape.boundingBox != null) + { + bds.add(handler.rotationShape.boundingBox); + } + } + + bbox.add(checkArrow(x, y, new mxRectangle(currentTargetState.getCenterX() - this.triangleUp.width / 2, + bds.y - this.triangleUp.height, this.triangleUp.width, this.triangleUp.height), arrowUp)); + bbox.add(checkArrow(x, y, new mxRectangle(bds.x + bds.width, + currentTargetState.getCenterY() - this.triangleRight.height / 2, + this.triangleRight.width, this.triangleRight.height), arrowRight)); + bbox.add(checkArrow(x, y, new mxRectangle(currentTargetState.getCenterX() - this.triangleDown.width / 2, + bds.y + bds.height, this.triangleDown.width, this.triangleDown.height), arrowDown)); + bbox.add(checkArrow(x, y, new mxRectangle(bds.x - this.triangleLeft.width, + currentTargetState.getCenterY() - this.triangleLeft.height / 2, + this.triangleLeft.width, this.triangleLeft.height), arrowLeft)); + } + + // Adds tolerance + if (bbox != null) + { + bbox.grow(10); + } + } + + direction = mxConstants.DIRECTION_NORTH; + + if (activeArrow == arrowRight) + { + direction = mxConstants.DIRECTION_EAST; + } + else if (activeArrow == arrowDown || activeArrow == roundTarget) + { + direction = mxConstants.DIRECTION_SOUTH; + } + else if (activeArrow == arrowLeft) + { + direction = mxConstants.DIRECTION_WEST; + } + + if (currentStyleTarget != null && activeArrow == styleTarget) + { + state = currentStyleTarget; + } + + var validTarget = (firstVertex == null || graph.isCellConnectable(cells[firstVertex])) && + ((graph.model.isEdge(cell) && firstVertex != null) || + (graph.model.isVertex(cell) && graph.isCellConnectable(cell))); + + // Drop arrows shown after this.dropTargetDelay, hidden after 5 secs, switches arrows after 500ms + if ((currentTargetState != null && timeOnTarget >= 5000) || + (currentTargetState != state && + (bbox == null || !mxUtils.contains(bbox, x, y) || + (timeOnTarget > 500 && activeArrow == null && validTarget)))) + { + activeTarget = false; + currentTargetState = ((timeOnTarget < 5000 && timeOnTarget > this.dropTargetDelay) || graph.model.isEdge(cell)) ? state : null; + + if (currentTargetState != null && validTarget) + { + var elts = [roundSource, roundTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].parentNode != null) + { + elts[i].parentNode.removeChild(elts[i]); + } + } + + if (graph.model.isEdge(cell)) + { + var pts = state.absolutePoints; + + if (pts != null) + { + var p0 = pts[0]; + var pe = pts[pts.length - 1]; + var tol = graph.tolerance; + var box = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol); + + roundSource.style.left = Math.floor(p0.x - this.roundDrop.width / 2) + 'px'; + roundSource.style.top = Math.floor(p0.y - this.roundDrop.height / 2) + 'px'; + + roundTarget.style.left = Math.floor(pe.x - this.roundDrop.width / 2) + 'px'; + roundTarget.style.top = Math.floor(pe.y - this.roundDrop.height / 2) + 'px'; + + if (graph.model.getTerminal(cell, true) == null) + { + graph.container.appendChild(roundSource); + } + + if (graph.model.getTerminal(cell, false) == null) + { + graph.container.appendChild(roundTarget); + } + } + } + else + { + var bds = mxRectangle.fromRectangle(state); + + // Uses outer bounding box to take rotation into account + if (state.shape != null && state.shape.boundingBox != null) + { + bds = mxRectangle.fromRectangle(state.shape.boundingBox); + } + + bds.grow(this.graph.tolerance); + bds.grow(HoverIcons.prototype.arrowSpacing); + + var handler = this.graph.selectionCellsHandler.getHandler(state.cell); + + if (handler != null) + { + bds.x -= handler.horizontalOffset / 2; + bds.y -= handler.verticalOffset / 2; + bds.width += handler.horizontalOffset; + bds.height += handler.verticalOffset; + + // Adds bounding box of rotation handle to avoid overlap + if (handler.rotationShape != null && handler.rotationShape.node != null && + handler.rotationShape.node.style.visibility != 'hidden' && + handler.rotationShape.node.style.display != 'none' && + handler.rotationShape.boundingBox != null) + { + bds.add(handler.rotationShape.boundingBox); + } + } + + arrowUp.style.left = Math.floor(state.getCenterX() - this.triangleUp.width / 2) + 'px'; + arrowUp.style.top = Math.floor(bds.y - this.triangleUp.height) + 'px'; + + arrowRight.style.left = Math.floor(bds.x + bds.width) + 'px'; + arrowRight.style.top = Math.floor(state.getCenterY() - this.triangleRight.height / 2) + 'px'; + + arrowDown.style.left = arrowUp.style.left + arrowDown.style.top = Math.floor(bds.y + bds.height) + 'px'; + + arrowLeft.style.left = Math.floor(bds.x - this.triangleLeft.width) + 'px'; + arrowLeft.style.top = arrowRight.style.top; + + if (state.style['portConstraint'] != 'eastwest') + { + graph.container.appendChild(arrowUp); + graph.container.appendChild(arrowDown); + } + + graph.container.appendChild(arrowRight); + graph.container.appendChild(arrowLeft); + } + + // Hides handle for cell under mouse + if (state != null) + { + currentStateHandle = graph.selectionCellsHandler.getHandler(state.cell); + + if (currentStateHandle != null && currentStateHandle.setHandlesVisible != null) + { + currentStateHandle.setHandlesVisible(false); + } + } + + activeTarget = true; + } + else + { + var elts = [roundSource, roundTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].parentNode != null) + { + elts[i].parentNode.removeChild(elts[i]); + } + } + } + } + + if (!activeTarget && currentStateHandle != null) + { + currentStateHandle.setHandlesVisible(true); + } + + // Handles drop target + var target = ((!mxEvent.isAltDown(evt) || mxEvent.isShiftDown(evt)) && + !(currentStyleTarget != null && activeArrow == styleTarget)) ? + mxDragSource.prototype.getDropTarget.apply(this, arguments) : null; + var model = graph.getModel(); + + if (target != null) + { + if (activeArrow != null || !graph.isSplitTarget(target, cells, evt)) + { + // Selects parent group as drop target + while (target != null && !graph.isValidDropTarget(target, cells, evt) && model.isVertex(model.getParent(target))) + { + target = model.getParent(target); + } + + if (graph.view.currentRoot == target || (!graph.isValidRoot(target) && + graph.getModel().getChildCount(target) == 0) || + graph.isCellLocked(target) || model.isEdge(target)) + { + target = null; + } + } + } + + return target; + }); + + dragSource.stopDrag = function() + { + mxDragSource.prototype.stopDrag.apply(this, arguments); + + var elts = [roundSource, roundTarget, styleTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].parentNode != null) + { + elts[i].parentNode.removeChild(elts[i]); + } + } + + if (currentTargetState != null && currentStateHandle != null) + { + currentStateHandle.reset(); + } + + currentStateHandle = null; + currentTargetState = null; + currentStyleTarget = null; + styleTargetParent = null; + activeArrow = null; + }; + + return dragSource; +}; + +/** + * Adds a handler for inserting the cell with a single click. + */ +Sidebar.prototype.itemClicked = function(cells, ds, evt, elt) +{ + var graph = this.editorUi.editor.graph; + graph.container.focus(); + + // Alt+Click inserts and connects + if (mxEvent.isAltDown(evt)) + { + if (graph.getSelectionCount() == 1 && graph.model.isVertex(graph.getSelectionCell())) + { + var firstVertex = null; + + for (var i = 0; i < cells.length && firstVertex == null; i++) + { + if (graph.model.isVertex(cells[i])) + { + firstVertex = i; + } + } + + if (firstVertex != null) + { + graph.setSelectionCells(this.dropAndConnect(graph.getSelectionCell(), cells, (mxEvent.isMetaDown(evt) || mxEvent.isControlDown(evt)) ? + (mxEvent.isShiftDown(evt) ? mxConstants.DIRECTION_WEST : mxConstants.DIRECTION_NORTH) : + (mxEvent.isShiftDown(evt) ? mxConstants.DIRECTION_EAST : mxConstants.DIRECTION_SOUTH), + firstVertex, evt)); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + } + } + // Shift+Click updates shape + else if (mxEvent.isShiftDown(evt) && !graph.isSelectionEmpty()) + { + this.updateShapes(cells[0], graph.getSelectionCells()); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + else + { + var pt = graph.getFreeInsertPoint(); + ds.drop(graph, evt, null, pt.x, pt.y, true); + + if (this.editorUi.hoverIcons != null && (mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt))) + { + this.editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); + } + } +}; + +/** + * Adds a handler for inserting the cell with a single click. + */ +Sidebar.prototype.addClickHandler = function(elt, ds, cells) +{ + var graph = this.editorUi.editor.graph; + var oldMouseDown = ds.mouseDown; + var oldMouseMove = ds.mouseMove; + var oldMouseUp = ds.mouseUp; + var tol = graph.tolerance; + var first = null; + var sb = this; + + ds.mouseDown =function(evt) + { + oldMouseDown.apply(this, arguments); + first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); + + if (this.dragElement != null) + { + this.dragElement.style.display = 'none'; + mxUtils.setOpacity(elt, 50); + } + }; + + ds.mouseMove = function(evt) + { + if (this.dragElement != null && this.dragElement.style.display == 'none' && + first != null && (Math.abs(first.x - mxEvent.getClientX(evt)) > tol || + Math.abs(first.y - mxEvent.getClientY(evt)) > tol)) + { + this.dragElement.style.display = ''; + mxUtils.setOpacity(elt, 100); + } + + oldMouseMove.apply(this, arguments); + }; + + ds.mouseUp = function(evt) + { + if (!mxEvent.isPopupTrigger(evt) && this.currentGraph == null && + this.dragElement != null && this.dragElement.style.display == 'none') + { + sb.itemClicked(cells, ds, evt, elt); + } + + oldMouseUp.apply(ds, arguments); + mxUtils.setOpacity(elt, 100); + first = null; + + // Blocks tooltips on this element after single click + sb.currentElt = elt; + }; +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createVertexTemplateEntry = function(style, width, height, value, title, showLabel, showTitle, tags) +{ + tags = (tags != null && tags.length > 0) ? tags : title.toLowerCase(); + + return this.addEntry(tags, mxUtils.bind(this, function() + { + return this.createVertexTemplate(style, width, height, value, title, showLabel, showTitle); + })); +} + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createVertexTemplate = function(style, width, height, value, title, showLabel, showTitle, allowCellsInserted) +{ + var cells = [new mxCell((value != null) ? value : '', new mxGeometry(0, 0, width, height), style)]; + cells[0].vertex = true; + + return this.createVertexTemplateFromCells(cells, width, height, title, showLabel, showTitle, allowCellsInserted); +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createVertexTemplateFromData = function(data, width, height, title, showLabel, showTitle, allowCellsInserted) +{ + var doc = mxUtils.parseXml(this.graph.decompress(data)); + var codec = new mxCodec(doc); + + var model = new mxGraphModel(); + codec.decode(doc.documentElement, model); + + var cells = this.graph.cloneCells(model.root.getChildAt(0).children); + + return this.createVertexTemplateFromCells(cells, width, height, title, showLabel, showTitle, allowCellsInserted); +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createVertexTemplateFromCells = function(cells, width, height, title, showLabel, showTitle, allowCellsInserted) +{ + // Use this line to convert calls to this function with lots of boilerplate code for creating cells + //console.trace('xml', this.graph.compress(mxUtils.getXml(this.graph.encodeCells(cells))), cells); + return this.createItem(cells, title, showLabel, showTitle, width, height, allowCellsInserted); +}; + +/** + * + */ +Sidebar.prototype.createEdgeTemplateEntry = function(style, width, height, value, title, showLabel, tags, allowCellsInserted) +{ + tags = (tags != null && tags.length > 0) ? tags : title.toLowerCase(); + + return this.addEntry(tags, mxUtils.bind(this, function() + { + return this.createEdgeTemplate(style, width, height, value, title, showLabel, allowCellsInserted); + })); +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createEdgeTemplate = function(style, width, height, value, title, showLabel, allowCellsInserted) +{ + var cell = new mxCell((value != null) ? value : '', new mxGeometry(0, 0, width, height), style); + cell.geometry.setTerminalPoint(new mxPoint(0, height), true); + cell.geometry.setTerminalPoint(new mxPoint(width, 0), false); + cell.geometry.relative = true; + cell.edge = true; + + return this.createEdgeTemplateFromCells([cell], width, height, title, showLabel, allowCellsInserted); +}; + +/** + * Creates a drop handler for inserting the given cells. + */ +Sidebar.prototype.createEdgeTemplateFromCells = function(cells, width, height, title, showLabel, allowCellsInserted) +{ + return this.createItem(cells, title, showLabel, true, width, height, allowCellsInserted); +}; + +/** + * Adds the given palette. + */ +Sidebar.prototype.addPaletteFunctions = function(id, title, expanded, fns) +{ + this.addPalette(id, title, expanded, mxUtils.bind(this, function(content) + { + for (var i = 0; i < fns.length; i++) + { + content.appendChild(fns[i](content)); + } + })); +}; + +/** + * Adds the given palette. + */ +Sidebar.prototype.addPalette = function(id, title, expanded, onInit) +{ + var elt = this.createTitle(title); + this.container.appendChild(elt); + + var div = document.createElement('div'); + div.className = 'geSidebar'; + + // Disables built-in pan and zoom in IE10 and later + if (mxClient.IS_POINTER) + { + div.style.touchAction = 'none'; + } + + if (expanded) + { + onInit(div); + onInit = null; + } + else + { + div.style.display = 'none'; + } + + this.addFoldingHandler(elt, div, onInit); + + var outer = document.createElement('div'); + outer.appendChild(div); + this.container.appendChild(outer); + + // Keeps references to the DOM nodes + if (id != null) + { + this.palettes[id] = [elt, outer]; + } + + return div; +}; + +/** + * Create the given title element. + */ +Sidebar.prototype.addFoldingHandler = function(title, content, funct) +{ + var initialized = false; + + // Avoids mixed content warning in IE6-8 + if (!mxClient.IS_IE || document.documentMode >= 8) + { + title.style.backgroundImage = (content.style.display == 'none') ? + 'url(\'' + this.collapsedImage + '\')' : 'url(\'' + this.expandedImage + '\')'; + } + + title.style.backgroundRepeat = 'no-repeat'; + title.style.backgroundPosition = '0% 50%'; + + mxEvent.addListener(title, 'click', mxUtils.bind(this, function(evt) + { + if (content.style.display == 'none') + { + if (!initialized) + { + initialized = true; + + if (funct != null) + { + // Wait cursor does not show up on Mac + title.style.cursor = 'wait'; + var prev = title.innerHTML; + title.innerHTML = mxResources.get('loading') + '...'; + + window.setTimeout(function() + { + var fo = mxClient.NO_FO; + mxClient.NO_FO = Editor.prototype.originalNoForeignObject; + funct(content); + mxClient.NO_FO = fo; + content.style.display = 'block'; + title.style.cursor = ''; + title.innerHTML = prev; + }, 0); + } + else + { + content.style.display = 'block'; + } + } + else + { + content.style.display = 'block'; + } + + title.style.backgroundImage = 'url(\'' + this.expandedImage + '\')'; + } + else + { + title.style.backgroundImage = 'url(\'' + this.collapsedImage + '\')'; + content.style.display = 'none'; + } + + mxEvent.consume(evt); + })); +}; + +/** + * Removes the palette for the given ID. + */ +Sidebar.prototype.removePalette = function(id) +{ + var elts = this.palettes[id]; + + if (elts != null) + { + this.palettes[id] = null; + + for (var i = 0; i < elts.length; i++) + { + this.container.removeChild(elts[i]); + } + + return true; + } + + return false; +}; + +/** + * Adds the given image palette. + */ +Sidebar.prototype.addImagePalette = function(id, title, prefix, postfix, items, titles, tags) +{ + var showTitles = titles != null; + var fns = []; + + for (var i = 0; i < items.length; i++) + { + (mxUtils.bind(this, function(item, title, tmpTags) + { + if (tmpTags == null) + { + var slash = item.lastIndexOf('/'); + var dot = item.lastIndexOf('.'); + tmpTags = item.substring((slash >= 0) ? slash + 1 : 0, (dot >= 0) ? dot : item.length).replace(/[-_]/g, ' '); + } + + fns.push(this.createVertexTemplateEntry('image;html=1;labelBackgroundColor=#ffffff;image=' + prefix + item + postfix, + this.defaultImageWidth, this.defaultImageHeight, '', title, title != null, null, this.filterTags(tmpTags))); + }))(items[i], (titles != null) ? titles[i] : null, (tags != null) ? tags[items[i]] : null); + } + + this.addPaletteFunctions(id, title, false, fns); +}; + +/** + * Creates the array of tags for the given stencil. Duplicates are allowed and will be filtered out later. + */ +Sidebar.prototype.getTagsForStencil = function(packageName, stencilName, moreTags) +{ + var tags = packageName.split('.'); + + for (var i = 1; i < tags.length; i++) + { + tags[i] = tags[i].replace(/_/g, ' ') + } + + tags.push(stencilName.replace(/_/g, ' ')); + + if (moreTags != null) + { + tags.push(moreTags); + } + + return tags.slice(1, tags.length); +}; + +/** + * Adds the given stencil palette. + */ +Sidebar.prototype.addStencilPalette = function(id, title, stencilFile, style, ignore, onInit, scale, tags, customFns) +{ + scale = (scale != null) ? scale : 1; + + if (this.addStencilsToIndex) + { + // LATER: Handle asynchronous loading dependency + var fns = []; + + if (customFns != null) + { + for (var i = 0; i < customFns.length; i++) + { + fns.push(customFns[i]); + } + } + + mxStencilRegistry.loadStencilSet(stencilFile, mxUtils.bind(this, function(packageName, stencilName, displayName, w, h) + { + if (ignore == null || mxUtils.indexOf(ignore, stencilName) < 0) + { + var tmp = this.getTagsForStencil(packageName, stencilName); + var tmpTags = (tags != null) ? tags[stencilName] : null; + + if (tmpTags != null) + { + tmp.push(tmpTags); + } + + fns.push(this.createVertexTemplateEntry('shape=' + packageName + stencilName.toLowerCase() + style, + Math.round(w * scale), Math.round(h * scale), '', stencilName.replace(/_/g, ' '), null, null, + this.filterTags(tmp.join(' ')))); + } + }), true, true); + + this.addPaletteFunctions(id, title, false, fns); + } + else + { + this.addPalette(id, title, false, mxUtils.bind(this, function(content) + { + if (style == null) + { + style = ''; + } + + if (onInit != null) + { + onInit.call(this, content); + } + + if (customFns != null) + { + for (var i = 0; i < customFns.length; i++) + { + customFns[i](content); + } + } + + mxStencilRegistry.loadStencilSet(stencilFile, mxUtils.bind(this, function(packageName, stencilName, displayName, w, h) + { + if (ignore == null || mxUtils.indexOf(ignore, stencilName) < 0) + { + content.appendChild(this.createVertexTemplate('shape=' + packageName + stencilName.toLowerCase() + style, + Math.round(w * scale), Math.round(h * scale), '', stencilName.replace(/_/g, ' '), true)); + } + }), true); + })); + } +}; + +/** + * Adds the given stencil palette. + */ +Sidebar.prototype.destroy = function() +{ + if (this.graph != null) + { + if (this.graph.container != null && this.graph.container.parentNode != null) + { + this.graph.container.parentNode.removeChild(this.graph.container); + } + + this.graph.destroy(); + this.graph = null; + } + + if (this.pointerUpHandler != null) + { + mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', this.pointerUpHandler); + this.pointerUpHandler = null; + } + + if (this.pointerDownHandler != null) + { + mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', this.pointerDownHandler); + this.pointerDownHandler = null; + } + + if (this.pointerMoveHandler != null) + { + mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.pointerMoveHandler); + this.pointerMoveHandler = null; + } + + if (this.pointerOutHandler != null) + { + mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerout' : 'mouseout', this.pointerOutHandler); + this.pointerOutHandler = null; + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Toolbar.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Toolbar.js new file mode 100644 index 00000000..273b2a82 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/js/Toolbar.js @@ -0,0 +1,954 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Construcs a new toolbar for the given editor. + */ +function Toolbar(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; + this.staticElements = []; + this.init(); + + // Global handler to hide the current menu + this.gestureHandler = mxUtils.bind(this, function(evt) + { + if (this.editorUi.currentMenu != null && mxEvent.getSource(evt) != this.editorUi.currentMenu.div) + { + this.hideMenu(); + } + }); + + mxEvent.addGestureListeners(document, this.gestureHandler); +}; + +/** + * Image for the dropdown arrow. + */ +Toolbar.prototype.dropdownImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/dropdown.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAHt7e////yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREM1NkJFMjE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREM1NkJFMzE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkQzOUMzMjZCMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkQzOUMzMjZDMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhGMj6nL3QAjVHIu6azbvPtWAAA7'; + +/** + * Image element for the dropdown arrow. + */ +Toolbar.prototype.dropdownImageHtml = ''; + +/** + * Defines the background for selected buttons. + */ +Toolbar.prototype.selectedBackground = '#d0d0d0'; + +/** + * Defines the background for selected buttons. + */ +Toolbar.prototype.unselectedBackground = 'none'; + +/** + * Array that contains the DOM nodes that should never be removed. + */ +Toolbar.prototype.staticElements = null; + +/** + * Adds the toolbar elements. + */ +Toolbar.prototype.init = function() +{ + var sw = screen.width; + + // Takes into account initial compact mode + sw -= (screen.height > 740) ? 56 : 0; + + if (sw >= 700) + { + var formatMenu = this.addMenu('', mxResources.get('view') + ' (' + mxResources.get('panTooltip') + ')', true, 'viewPanels', null, true); + this.addDropDownArrow(formatMenu, 'geSprite-formatpanel', 38, 50, -4, -3, 36, -8); + this.addSeparator(); + } + + var viewMenu = this.addMenu('', mxResources.get('zoom') + ' (Alt+Mousewheel)', true, 'viewZoom', null, true); + viewMenu.showDisabled = true; + viewMenu.style.whiteSpace = 'nowrap'; + viewMenu.style.position = 'relative'; + viewMenu.style.overflow = 'hidden'; + + if (EditorUi.compactUi) + { + viewMenu.style.width = (mxClient.IS_QUIRKS) ? '58px' : '50px'; + } + else + { + viewMenu.style.width = (mxClient.IS_QUIRKS) ? '62px' : '36px'; + } + + if (sw >= 420) + { + this.addSeparator(); + var elts = this.addItems(['zoomIn', 'zoomOut']); + elts[0].setAttribute('title', mxResources.get('zoomIn') + ' (' + this.editorUi.actions.get('zoomIn').shortcut + ')'); + elts[1].setAttribute('title', mxResources.get('zoomOut') + ' (' + this.editorUi.actions.get('zoomOut').shortcut + ')'); + } + + // Updates the label if the scale changes + this.updateZoom = mxUtils.bind(this, function() + { + viewMenu.innerHTML = Math.round(this.editorUi.editor.graph.view.scale * 100) + '%' + + this.dropdownImageHtml; + + if (EditorUi.compactUi) + { + viewMenu.getElementsByTagName('img')[0].style.right = '1px'; + viewMenu.getElementsByTagName('img')[0].style.top = '5px'; + } + }); + + this.editorUi.editor.graph.view.addListener(mxEvent.EVENT_SCALE, this.updateZoom); + this.editorUi.editor.addListener('resetGraphView', this.updateZoom); + + var elts = this.addItems(['-', 'undo', 'redo']); + elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')'); + elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')'); + + if (sw >= 320) + { + var elts = this.addItems(['-', 'delete']); + elts[1].setAttribute('title', mxResources.get('delete') + ' (' + this.editorUi.actions.get('delete').shortcut + ')'); + } + + if (sw >= 550) + { + this.addItems(['-', 'toFront', 'toBack']); + } + + if (sw >= 740) + { + this.addItems(['-', 'fillColor']); + + if (sw >= 780) + { + this.addItems(['strokeColor']); + + if (sw >= 820) + { + this.addItems(['shadow']); + } + } + } + + if (sw >= 400) + { + this.addSeparator(); + + if (sw >= 440) + { + this.edgeShapeMenu = this.addMenuFunction('', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], [null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['link', null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['flexArrow', null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['arrow', null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); + })); + + this.addDropDownArrow(this.edgeShapeMenu, 'geSprite-connection', 44, 50, 0, 0, 22, -4); + } + + this.edgeStyleMenu = this.addMenuFunction('geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); + })); + + this.addDropDownArrow(this.edgeStyleMenu, 'geSprite-orthogonal', 44, 50, 0, 0, 22, -4); + } + + this.addSeparator(); + + var insertMenu = this.addMenu('', mxResources.get('insert') + ' (' + mxResources.get('doubleClickTooltip') + ')', true, 'insert', null, true); + this.addDropDownArrow(insertMenu, 'geSprite-plus', 38, 48, -4, -3, 36, -8); +}; + +/** + * Adds the toolbar elements. + */ +Toolbar.prototype.addDropDownArrow = function(menu, sprite, width, atlasWidth, left, top, atlasDelta, atlasLeft) +{ + atlasDelta = (atlasDelta != null) ? atlasDelta : 32; + left = (EditorUi.compactUi) ? left : atlasLeft; + + menu.style.whiteSpace = 'nowrap'; + menu.style.overflow = 'hidden'; + menu.style.position = 'relative'; + menu.innerHTML = '
' + + this.dropdownImageHtml; + menu.style.width = (mxClient.IS_QUIRKS) ? atlasWidth + 'px' : (atlasWidth - atlasDelta) + 'px'; + + if (mxClient.IS_QUIRKS) + { + menu.style.height = (EditorUi.compactUi) ? '24px' : '26px'; + } + + // Fix for item size in kennedy theme + if (EditorUi.compactUi) + { + menu.getElementsByTagName('img')[0].style.left = '24px'; + menu.getElementsByTagName('img')[0].style.top = '5px'; + menu.style.width = (mxClient.IS_QUIRKS) ? width + 'px' : (width - 10) + 'px'; + } +}; + +/** + * Sets the current font name. + */ +Toolbar.prototype.setFontName = function(value) +{ + if (this.fontMenu != null) + { + this.fontMenu.innerHTML = '
' + + mxUtils.htmlEntities(value) + '
' + this.dropdownImageHtml; + } +}; + +/** + * Sets the current font name. + */ +Toolbar.prototype.setFontSize = function(value) +{ + if (this.sizeMenu != null) + { + this.sizeMenu.innerHTML = '
' + + value + '
' + this.dropdownImageHtml; + } +}; + +/** + * Hides the current menu. + */ +Toolbar.prototype.createTextToolbar = function() +{ + var graph = this.editorUi.editor.graph; + + var styleElt = this.addMenu('', mxResources.get('style'), true, 'formatBlock'); + styleElt.style.position = 'relative'; + styleElt.style.whiteSpace = 'nowrap'; + styleElt.style.overflow = 'hidden'; + styleElt.innerHTML = mxResources.get('style') + this.dropdownImageHtml; + + if (EditorUi.compactUi) + { + styleElt.style.paddingRight = '18px'; + styleElt.getElementsByTagName('img')[0].style.right = '1px'; + styleElt.getElementsByTagName('img')[0].style.top = '5px'; + } + + this.addSeparator(); + + this.fontMenu = this.addMenu('', mxResources.get('fontFamily'), true, 'fontFamily'); + this.fontMenu.style.position = 'relative'; + this.fontMenu.style.whiteSpace = 'nowrap'; + this.fontMenu.style.overflow = 'hidden'; + this.fontMenu.style.width = (mxClient.IS_QUIRKS) ? '80px' : '60px'; + + this.setFontName(Menus.prototype.defaultFont); + + if (EditorUi.compactUi) + { + this.fontMenu.style.paddingRight = '18px'; + this.fontMenu.getElementsByTagName('img')[0].style.right = '1px'; + this.fontMenu.getElementsByTagName('img')[0].style.top = '5px'; + } + + this.addSeparator(); + + this.sizeMenu = this.addMenu(Menus.prototype.defaultFontSize, mxResources.get('fontSize'), true, 'fontSize'); + this.sizeMenu.style.position = 'relative'; + this.sizeMenu.style.whiteSpace = 'nowrap'; + this.sizeMenu.style.overflow = 'hidden'; + this.sizeMenu.style.width = (mxClient.IS_QUIRKS) ? '44px' : '24px'; + + this.setFontSize(Menus.prototype.defaultFontSize); + + if (EditorUi.compactUi) + { + this.sizeMenu.style.paddingRight = '18px'; + this.sizeMenu.getElementsByTagName('img')[0].style.right = '1px'; + this.sizeMenu.getElementsByTagName('img')[0].style.top = '5px'; + } + + var elts = this.addItems(['-', 'undo', 'redo','-', 'bold', 'italic', 'underline']); + elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')'); + elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')'); + elts[4].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); + elts[5].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); + elts[6].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); + + // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems + // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). + var alignMenu = this.addMenuFunction('', mxResources.get('align'), false, mxUtils.bind(this, function(menu) + { + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('justifyleft', false, null); + }), null, 'geIcon geSprite geSprite-left'); + elt.setAttribute('title', mxResources.get('left')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('justifycenter', false, null); + }), null, 'geIcon geSprite geSprite-center'); + elt.setAttribute('title', mxResources.get('center')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('justifyright', false, null); + }), null, 'geIcon geSprite geSprite-right'); + elt.setAttribute('title', mxResources.get('right')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('justifyfull', false, null); + }), null, 'geIcon geSprite geSprite-justifyfull'); + elt.setAttribute('title', mxResources.get('justifyfull')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('insertorderedlist', false, null); + }), null, 'geIcon geSprite geSprite-orderedlist'); + elt.setAttribute('title', mxResources.get('numberedList')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('insertunorderedlist', false, null); + }), null, 'geIcon geSprite geSprite-unorderedlist'); + elt.setAttribute('title', mxResources.get('bulletedList')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('outdent', false, null); + }), null, 'geIcon geSprite geSprite-outdent'); + elt.setAttribute('title', mxResources.get('decreaseIndent')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('indent', false, null); + }), null, 'geIcon geSprite geSprite-indent'); + elt.setAttribute('title', mxResources.get('increaseIndent')); + })); + + alignMenu.style.position = 'relative'; + alignMenu.style.whiteSpace = 'nowrap'; + alignMenu.style.overflow = 'hidden'; + alignMenu.innerHTML = '
' + this.dropdownImageHtml; + alignMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; + + if (EditorUi.compactUi) + { + alignMenu.getElementsByTagName('img')[0].style.left = '22px'; + alignMenu.getElementsByTagName('img')[0].style.top = '5px'; + } + + var formatMenu = this.addMenuFunction('', mxResources.get('format'), false, mxUtils.bind(this, function(menu) + { + elt = menu.addItem('', null, this.editorUi.actions.get('subscript').funct, + null, 'geIcon geSprite geSprite-subscript'); + elt.setAttribute('title', mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)'); + + elt = menu.addItem('', null, this.editorUi.actions.get('superscript').funct, + null, 'geIcon geSprite geSprite-superscript'); + elt.setAttribute('title', mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)'); + + // KNOWN: IE+FF don't return keyboard focus after color dialog (calling focus doesn't help) + elt = menu.addItem('', null, this.editorUi.actions.get('fontColor').funct, + null, 'geIcon geSprite geSprite-fontcolor'); + elt.setAttribute('title', mxResources.get('fontColor')); + + elt = menu.addItem('', null, this.editorUi.actions.get('backgroundColor').funct, + null, 'geIcon geSprite geSprite-fontbackground'); + elt.setAttribute('title', mxResources.get('backgroundColor')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + document.execCommand('removeformat', false, null); + }), null, 'geIcon geSprite geSprite-removeformat'); + elt.setAttribute('title', mxResources.get('removeFormat')); + })); + + formatMenu.style.position = 'relative'; + formatMenu.style.whiteSpace = 'nowrap'; + formatMenu.style.overflow = 'hidden'; + formatMenu.innerHTML = '
' + + this.dropdownImageHtml; + formatMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; + + if (EditorUi.compactUi) + { + formatMenu.getElementsByTagName('img')[0].style.left = '22px'; + formatMenu.getElementsByTagName('img')[0].style.top = '5px'; + } + + this.addSeparator(); + + this.addButton('geIcon geSprite geSprite-code', mxResources.get('html'), function() + { + graph.cellEditor.toggleViewMode(); + + if (graph.cellEditor.textarea.innerHTML.length > 0 && (graph.cellEditor.textarea.innerHTML != ' ' || !graph.cellEditor.clearOnChange)) + { + window.setTimeout(function() + { + document.execCommand('selectAll', false, null); + }); + } + }); + + this.addSeparator(); + + // FIXME: Uses geButton here and geLabel in main menu + var insertMenu = this.addMenuFunction('', mxResources.get('insert'), true, mxUtils.bind(this, function(menu) + { + menu.addItem(mxResources.get('insertLink'), null, mxUtils.bind(this, function() + { + this.editorUi.actions.get('link').funct(); + })); + + menu.addItem(mxResources.get('insertImage'), null, mxUtils.bind(this, function() + { + this.editorUi.actions.get('image').funct(); + })); + + menu.addItem(mxResources.get('insertHorizontalRule'), null, mxUtils.bind(this, function() + { + document.execCommand('inserthorizontalrule', false, null); + })); + })); + + insertMenu.style.whiteSpace = 'nowrap'; + insertMenu.style.overflow = 'hidden'; + insertMenu.style.position = 'relative'; + insertMenu.innerHTML = '
' + + this.dropdownImageHtml; + insertMenu.style.width = (mxClient.IS_QUIRKS) ? '36px' : '16px'; + + // Fix for item size in kennedy theme + if (EditorUi.compactUi) + { + insertMenu.getElementsByTagName('img')[0].style.left = '24px'; + insertMenu.getElementsByTagName('img')[0].style.top = '5px'; + insertMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; + } + + this.addSeparator(); + + // KNOWN: All table stuff does not work with undo/redo + // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems + // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). + var elt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) + { + var elt = graph.getSelectedElement(); + var cell = graph.getParentByName(elt, 'TD', graph.cellEditor.text2); + var row = graph.getParentByName(elt, 'TR', graph.cellEditor.text2); + + if (row == null) + { + this.editorUi.menus.addInsertTableItem(menu); + } + else + { + var table = graph.getParentByName(row, 'TABLE', graph.cellEditor.text2); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + try + { + graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex : 0)); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + }), null, 'geIcon geSprite geSprite-insertcolumnbefore'); + elt.setAttribute('title', mxResources.get('insertColumnBefore')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + try + { + graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex + 1 : -1)); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + }), null, 'geIcon geSprite geSprite-insertcolumnafter'); + elt.setAttribute('title', mxResources.get('insertColumnAfter')); + + elt = menu.addItem('Delete column', null, mxUtils.bind(this, function() + { + if (cell != null) + { + try + { + graph.deleteColumn(table, cell.cellIndex); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + } + }), null, 'geIcon geSprite geSprite-deletecolumn'); + elt.setAttribute('title', mxResources.get('deleteColumn')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + try + { + graph.selectNode(graph.insertRow(table, row.sectionRowIndex)); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + }), null, 'geIcon geSprite geSprite-insertrowbefore'); + elt.setAttribute('title', mxResources.get('insertRowBefore')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + try + { + graph.selectNode(graph.insertRow(table, row.sectionRowIndex + 1)); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + }), null, 'geIcon geSprite geSprite-insertrowafter'); + elt.setAttribute('title', mxResources.get('insertRowAfter')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + try + { + graph.deleteRow(table, row.sectionRowIndex); + } + catch (e) + { + mxUtils.alert(mxResources.get('error') + ': ' + e.message); + } + }), null, 'geIcon geSprite geSprite-deleterow'); + elt.setAttribute('title', mxResources.get('deleteRow')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + // Converts rgb(r,g,b) values + var color = table.style.borderColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + table.removeAttribute('border'); + table.style.border = ''; + table.style.borderCollapse = ''; + } + else + { + table.setAttribute('border', '1'); + table.style.border = '1px solid ' + newColor; + table.style.borderCollapse = 'collapse'; + } + }); + }), null, 'geIcon geSprite geSprite-strokecolor'); + elt.setAttribute('title', mxResources.get('borderColor')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + // Converts rgb(r,g,b) values + var color = table.style.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + table.style.backgroundColor = ''; + } + else + { + table.style.backgroundColor = newColor; + } + }); + }), null, 'geIcon geSprite geSprite-fillcolor'); + elt.setAttribute('title', mxResources.get('backgroundColor')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + var value = table.getAttribute('cellPadding') || 0; + + var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + if (newValue != null && newValue.length > 0) + { + table.setAttribute('cellPadding', newValue); + } + else + { + table.removeAttribute('cellPadding'); + } + }), mxResources.get('spacing')); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }), null, 'geIcon geSprite geSprite-fit'); + elt.setAttribute('title', mxResources.get('spacing')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + table.setAttribute('align', 'left'); + }), null, 'geIcon geSprite geSprite-left'); + elt.setAttribute('title', mxResources.get('left')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + table.setAttribute('align', 'center'); + }), null, 'geIcon geSprite geSprite-center'); + elt.setAttribute('title', mxResources.get('center')); + + elt = menu.addItem('', null, mxUtils.bind(this, function() + { + table.setAttribute('align', 'right'); + }), null, 'geIcon geSprite geSprite-right'); + elt.setAttribute('title', mxResources.get('right')); + } + })); + + elt.style.position = 'relative'; + elt.style.whiteSpace = 'nowrap'; + elt.style.overflow = 'hidden'; + elt.innerHTML = '
' + this.dropdownImageHtml; + elt.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; + + // Fix for item size in kennedy theme + if (EditorUi.compactUi) + { + elt.getElementsByTagName('img')[0].style.left = '22px'; + elt.getElementsByTagName('img')[0].style.top = '5px'; + } +}; + +/** + * Hides the current menu. + */ +Toolbar.prototype.hideMenu = function() +{ + this.editorUi.hideCurrentMenu(); +}; + +/** + * Adds a label to the toolbar. + */ +Toolbar.prototype.addMenu = function(label, tooltip, showLabels, name, c, showAll) +{ + var menu = this.editorUi.menus.get(name); + var elt = this.addMenuFunction(label, tooltip, showLabels, function() + { + menu.funct.apply(menu, arguments); + }, c, showAll); + + menu.addListener('stateChanged', function() + { + elt.setEnabled(menu.enabled); + }); + + return elt; +}; + +/** + * Adds a label to the toolbar. + */ +Toolbar.prototype.addMenuFunction = function(label, tooltip, showLabels, funct, c, showAll) +{ + return this.addMenuFunctionInContainer((c != null) ? c : this.container, label, tooltip, showLabels, funct, showAll); +}; + +/** + * Adds a label to the toolbar. + */ +Toolbar.prototype.addMenuFunctionInContainer = function(container, label, tooltip, showLabels, funct, showAll) +{ + var elt = (showLabels) ? this.createLabel(label) : this.createButton(label); + this.initElement(elt, tooltip); + this.addMenuHandler(elt, showLabels, funct, showAll); + container.appendChild(elt); + + return elt; +}; + +/** + * Adds a separator to the separator. + */ +Toolbar.prototype.addSeparator = function(c) +{ + c = (c != null) ? c : this.container; + var elt = document.createElement('div'); + elt.className = 'geSeparator'; + c.appendChild(elt); + + return elt; +}; + +/** + * Adds given action item + */ +Toolbar.prototype.addItems = function(keys, c, ignoreDisabled) +{ + var items = []; + + for (var i = 0; i < keys.length; i++) + { + var key = keys[i]; + + if (key == '-') + { + items.push(this.addSeparator(c)); + } + else + { + items.push(this.addItem('geSprite-' + key.toLowerCase(), key, c, ignoreDisabled)); + } + } + + return items; +}; + +/** + * Adds given action item + */ +Toolbar.prototype.addItem = function(sprite, key, c, ignoreDisabled) +{ + var action = this.editorUi.actions.get(key); + var elt = null; + + if (action != null) + { + var tooltip = action.label; + + if (action.shortcut != null) + { + tooltip += ' (' + action.shortcut + ')'; + } + + elt = this.addButton(sprite, tooltip, action.funct, c); + + if (!ignoreDisabled) + { + elt.setEnabled(action.enabled); + + action.addListener('stateChanged', function() + { + elt.setEnabled(action.enabled); + }); + } + } + + return elt; +}; + +/** + * Adds a button to the toolbar. + */ +Toolbar.prototype.addButton = function(classname, tooltip, funct, c) +{ + var elt = this.createButton(classname); + c = (c != null) ? c : this.container; + + this.initElement(elt, tooltip); + this.addClickHandler(elt, funct); + c.appendChild(elt); + + return elt; +}; + +/** + * Initializes the given toolbar element. + */ +Toolbar.prototype.initElement = function(elt, tooltip) +{ + // Adds tooltip + if (tooltip != null) + { + elt.setAttribute('title', tooltip); + } + + this.addEnabledState(elt); +}; + +/** + * Adds enabled state with setter to DOM node (avoids JS wrapper). + */ +Toolbar.prototype.addEnabledState = function(elt) +{ + var classname = elt.className; + + elt.setEnabled = function(value) + { + elt.enabled = value; + + if (value) + { + elt.className = classname; + } + else + { + elt.className = classname + ' mxDisabled'; + } + }; + + elt.setEnabled(true); +}; + +/** + * Adds enabled state with setter to DOM node (avoids JS wrapper). + */ +Toolbar.prototype.addClickHandler = function(elt, funct) +{ + if (funct != null) + { + mxEvent.addListener(elt, 'click', function(evt) + { + if (elt.enabled) + { + funct(evt); + } + + mxEvent.consume(evt); + }); + + if (document.documentMode != null && document.documentMode >= 9) + { + // Prevents focus + mxEvent.addListener(elt, 'mousedown', function(evt) + { + evt.preventDefault(); + }); + } + } +}; + +/** + * Creates and returns a new button. + */ +Toolbar.prototype.createButton = function(classname) +{ + var elt = document.createElement('a'); + elt.setAttribute('href', 'javascript:void(0);'); + elt.className = 'geButton'; + + var inner = document.createElement('div'); + + if (classname != null) + { + inner.className = 'geSprite ' + classname; + } + + elt.appendChild(inner); + + return elt; +}; + +/** + * Creates and returns a new button. + */ +Toolbar.prototype.createLabel = function(label, tooltip) +{ + var elt = document.createElement('a'); + elt.setAttribute('href', 'javascript:void(0);'); + elt.className = 'geLabel'; + mxUtils.write(elt, label); + + return elt; +}; + +/** + * Adds a handler for showing a menu in the given element. + */ +Toolbar.prototype.addMenuHandler = function(elt, showLabels, funct, showAll) +{ + if (funct != null) + { + var graph = this.editorUi.editor.graph; + var menu = null; + var show = true; + + mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) + { + if (show && (elt.enabled == null || elt.enabled)) + { + graph.popupMenuHandler.hideMenu(); + menu = new mxPopupMenu(funct); + menu.div.className += ' geToolbarMenu'; + menu.showDisabled = showAll; + menu.labels = showLabels; + menu.autoExpand = true; + + var offset = mxUtils.getOffset(elt); + menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt); + this.editorUi.setCurrentMenu(menu, elt); + + // Workaround for scrollbar hiding menu items + if (!showLabels && menu.div.scrollHeight > menu.div.clientHeight) + { + menu.div.style.width = '40px'; + } + + menu.hideMenu = mxUtils.bind(this, function() + { + mxPopupMenu.prototype.hideMenu.apply(menu, arguments); + this.editorUi.resetCurrentMenu(); + menu.destroy(); + }); + + // Extends destroy to reset global state + menu.addListener(mxEvent.EVENT_HIDE, mxUtils.bind(this, function() + { + this.currentElt = null; + })); + } + + show = true; + mxEvent.consume(evt); + })); + + // Hides menu if already showing + mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function(evt) + { + show = this.currentElt != elt; + + // Prevents focus + if (document.documentMode != null && document.documentMode >= 9) + { + evt.preventDefault(); + } + })); + } +}; + +/** + * Adds a handler for showing a menu in the given element. + */ +Toolbar.prototype.destroy = function() +{ + if (this.gestureHandler != null) + { + mxEvent.removeGestureListeners(document, this.gestureHandler); + this.gestureHandler = null; + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/arrow.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/arrow.gif new file mode 100644 index 00000000..246478a8 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/arrow.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/cross.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/cross.gif new file mode 100644 index 00000000..0ee9c7ac Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/cross.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hs.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hs.png new file mode 100644 index 00000000..3d94486c Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hs.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hv.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hv.png new file mode 100644 index 00000000..1c5e01f8 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/hv.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/jscolor.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/jscolor.js new file mode 100644 index 00000000..b8093d88 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/jscolor/jscolor.js @@ -0,0 +1,913 @@ +/** + * jscolor, JavaScript Color Picker + * + * @version 1.3.13 + * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html + * @author Jan Odvarko, http://odvarko.cz + * @created 2008-06-15 + * @updated 2012-01-19 + * @link http://jscolor.com + */ + + +var jscolor = { + + + dir : '', // location of jscolor directory (leave empty to autodetect) + bindClass : 'color', // class name + binding : true, // automatic binding via + preloading : true, // use image preloading? + + + install : function() { + //jscolor.addEvent(window, 'load', jscolor.init); + }, + + + init : function() { + if(jscolor.preloading) { + jscolor.preload(); + } + }, + + + getDir : function() { + if(!jscolor.dir) { + var detected = jscolor.detectDir(); + jscolor.dir = detected!==false ? detected : 'jscolor/'; + } + return jscolor.dir; + }, + + + detectDir : function() { + var base = location.href; + + var e = document.getElementsByTagName('base'); + for(var i=0; i vs[a] ? + (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + drawPicker(0, 0); + } + }; + + + this.importColor = function() { + if(!valueElement) { + this.exportColor(); + } else { + if(!this.adjust) { + if(!this.fromString(valueElement.value, leaveValue)) { + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + } + } else if(!this.required && /^\s*$/.test(valueElement.value)) { + valueElement.value = ''; + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + + } else if(this.fromString(valueElement.value)) { + // OK + } else { + this.exportColor(); + } + } + }; + + + this.exportColor = function(flags) { + if(!(flags & leaveValue) && valueElement) { + var value = this.toString(); + if(this.caps) { value = value.toUpperCase(); } + if(this.hash) { value = '#'+value; } + valueElement.value = value; + } + if(!(flags & leaveStyle) && styleElement) { + styleElement.style.backgroundImage = "none"; + styleElement.style.backgroundColor = + '#'+this.toString(); + styleElement.style.color = + 0.213 * this.rgb[0] + + 0.715 * this.rgb[1] + + 0.072 * this.rgb[2] + < 0.5 ? '#FFF' : '#000'; + } + if(!(flags & leavePad) && isPickerOwner()) { + redrawPad(); + } + if(!(flags & leaveSld) && isPickerOwner()) { + redrawSld(); + } + }; + + + this.fromHSV = function(h, s, v, flags) { // null = don't change + h<0 && (h=0) || h>6 && (h=6); + s<0 && (s=0) || s>1 && (s=1); + v<0 && (v=0) || v>1 && (v=1); + this.rgb = HSV_RGB( + h===null ? this.hsv[0] : (this.hsv[0]=h), + s===null ? this.hsv[1] : (this.hsv[1]=s), + v===null ? this.hsv[2] : (this.hsv[2]=v) + ); + this.exportColor(flags); + }; + + + this.fromRGB = function(r, g, b, flags) { // null = don't change + r<0 && (r=0) || r>1 && (r=1); + g<0 && (g=0) || g>1 && (g=1); + b<0 && (b=0) || b>1 && (b=1); + var hsv = RGB_HSV( + r===null ? this.rgb[0] : (this.rgb[0]=r), + g===null ? this.rgb[1] : (this.rgb[1]=g), + b===null ? this.rgb[2] : (this.rgb[2]=b) + ); + if(hsv[0] !== null) { + this.hsv[0] = hsv[0]; + } + if(hsv[2] !== 0) { + this.hsv[1] = hsv[1]; + } + this.hsv[2] = hsv[2]; + this.exportColor(flags); + }; + + + this.fromString = function(hex, flags) { + var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); + if(!m) { + return false; + } else { + if(m[1].length === 6) { // 6-char notation + this.fromRGB( + parseInt(m[1].substr(0,2),16) / 255, + parseInt(m[1].substr(2,2),16) / 255, + parseInt(m[1].substr(4,2),16) / 255, + flags + ); + } else { // 3-char notation + this.fromRGB( + parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, + parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, + parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, + flags + ); + } + return true; + } + }; + + + this.toString = function() { + return ( + (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) + ); + }; + + + function RGB_HSV(r, g, b) { + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if(m === 0) { return [ null, 0, v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ h===6?0:h, m/v, v ]; + } + + + function HSV_RGB(h, s, v) { + if(h === null) { return [ v, v, v ]; } + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = v * (1 - s); + var n = v * (1 - s*f); + switch(i) { + case 6: + case 0: return [v,n,m]; + case 1: return [n,v,m]; + case 2: return [m,v,n]; + case 3: return [m,n,v]; + case 4: return [n,m,v]; + case 5: return [v,m,n]; + } + } + + + function removePicker() { + delete jscolor.picker.owner; + document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); + } + + + function drawPicker(x, y) { + if(!jscolor.picker) { + jscolor.picker = { + box : document.createElement('div'), + boxB : document.createElement('div'), + pad : document.createElement('div'), + padB : document.createElement('div'), + padM : document.createElement('div'), + sld : document.createElement('div'), + sldB : document.createElement('div'), + sldM : document.createElement('div'), + btn : document.createElement('div'), + btnS : document.createElement('span'), + btnT : document.createTextNode(THIS.pickerCloseText) + }; + for(var i=0,segSize=4; i + + + Open Diagram + + + + + +
+ + + + + + + + + + + +
+ +
+
+ + + +
+
+ + diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor.txt b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor.txt new file mode 100644 index 00000000..25c90c15 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor.txt @@ -0,0 +1,321 @@ +# Resources from graph.properties +alreadyConnected=Nodes already connected +cancel=Cancel +close=Close +collapse-expand=Collapse/Expand +containsValidationErrors=Contains validation errors +done=Done +doubleClickOrientation=Doubleclick to Change Orientation +error=Error +errorSavingFile=Error saving file +ok=OK +updatingDocument=Updating Document. Please wait... +updatingSelection=Updating Selection. Please wait... +# Custom resources +about=About +actualSize=Actual Size +add=Add +addLayer=Add Layer +addProperty=Add Property +addToExistingDrawing=Add to Existing Drawing +addWaypoint=Add Waypoint +advanced=Advanced +align=Align +alignment=Alignment +allChangesLost=All changes will be lost! +angle=Angle +apply=Apply +arc=Arc +arrange=Arrange +arrow=Arrow +arrows=Arrows +automatic=Automatic +autosave=Autosave +autosize=Autosize +background=Background +backgroundColor=Background Color +backgroundImage=Background Image +basic=Basic +block=Block +blockquote=Blockquote +bold=Bold +border=Border +borderWidth=Borderwidth +borderColor=Border Color +bottom=Bottom +bottomAlign=Bottom Align +bottomLeft=Bottom Left +bottomRight=Bottom Right +bulletedList=Bulleted List +cannotOpenFile=Cannot open file +center=Center +change=Change +changeOrientation=Change Orientation +circle=Circle +classic=Classic +clearDefaultStyle=Clear Default Style +clearWaypoints=Clear Waypoints +clipart=Clipart +collapse=Collapse +collapseExpand=Collapse/Expand +collapsible=Collapsible +comic=Comic +configure=Configure +connect=Connect +connection=Connection +connectionPoints=Connection points +connectionArrows=Connection arrows +constrainProportions=Constrain Proportions +copy=Copy +copyConnect=Copy on Connect +copySize=Copy Size +create=Create +curved=Curved +custom=Custom +cut=Cut +dashed=Dashed +decreaseIndent=Decrease Indent +default=Default +delete=Delete +deleteColumn=Delete Column +deleteRow=Delete Row +diagram=Diagram +diamond=Diamond +diamondThin=Diamond (thin) +direction=Direction +distribute=Distribute +divider=Divider +documentProperties=Document Properties +dotted=Dotted +drawing=Drawing{1} +drawingEmpty=Drawing is empty +drawingTooLarge=Drawing is too large +duplicate=Duplicate +duplicateIt=Duplicate {1} +east=East +edit=Edit +editData=Edit Data +editDiagram=Edit Diagram +editImage=Edit Image +editLink=Edit Link +editStyle=Edit Style +editTooltip=Edit Tooltip +enterGroup=Enter Group +enterValue=Enter Value +enterName=Enter Name +enterPropertyName=Enter Property Name +entityRelation=Entity Relation +exitGroup=Exit Group +expand=Expand +export=Export +extras=Extras +file=File +fileNotFound=File not found +filename=Filename +fill=Fill +fillColor=Fill Color +fitPage=One Page +fitPageWidth=Page Width +fitTwoPages=Two Pages +fitWindow=Fit Window +flip=Flip +flipH=Flip Horizontal +flipV=Flip Vertical +font=Font +fontFamily=Font Family +fontColor=Font Color +fontSize=Font Size +format=Format +formatPanel=Format Panel +general=Allgemein +formatPdf=PDF +formatPng=PNG +formatGif=GIF +formatJpg=JPEG +formatSvg=SVG +formatXml=XML +formatted=Formatted +formattedText=Formatted Text +gap=Gap +glass=Glass +general=General +global=Global +gradient=Gradient +gradientColor=Color +grid=Grid +gridSize=Grid Size +group=Group +guides=Guides +heading=Heading +height=Height +help=Help +hide=Hide +hideIt=Hide {1} +hidden=Hidden +home=Home +horizontal=Horizontal +horizontalFlow=Horizontal Flow +horizontalTree=Horizontal Tree +html=HTML +id=ID +image=Image +images=Images +import=Import +increaseIndent=Increase Indent +insert=Insert +insertColumnBefore=Insert Column Left +insertColumnAfter=Insert Column Right +insertHorizontalRule=Insert Horizontal Rule +insertImage=Insert Image +insertLink=Insert Link +insertRowBefore=Insert Row Above +insertRowAfter=Insert Row Below +invalidName=Invalid name +invalidOrMissingFile=Invalid or missing file +isometric=Isometric +italic=Italic +layers=Layers +landscape=Landscape +laneColor=Lanecolor +layout=Layout +left=Left +leftAlign=Left Align +leftToRight=Left to Right +line=Line +link=Link +lineJumps=Line jumps +lineend=Line End +lineheight=Line Height +linestart=Line Start +linewidth=Linewidth +loading=Loading +lockUnlock=Lock/Unlock +manual=Manual +middle=Middle +misc=Misc +more=More +moreResults=More Results +move=Move +moveSelectionTo=Move Selection to {1} +navigation=Navigation +new=New +noColor=No Color +noFiles=No files +noMoreResults=No more results +none=None +noResultsFor=No results for '{1}' +normal=Normal +north=North +numberedList=Numbered List +opacity=Opacity +open=Open +openArrow=Open Arrow +openFile=Open File +openLink=Open Link +openSupported=Supported format is .XML files saved from this software +openInNewWindow=Open in New Window +openInThisWindow=Open in this Window +options=Options +organic=Organic +orthogonal=Orthogonal +outline=Outline +oval=Oval +pages=Pages +pageView=Page View +pageScale=Page Scale +pageSetup=Page Setup +panTooltip=Space+Drag to Scroll +paperSize=Paper Size +paste=Paste +pasteHere=Paste Here +pasteSize=Paste Size +pattern=Pattern +perimeter=Perimeter +placeholders=Placeholders +plusTooltip=Click to connect and clone (ctrl+click to clone, shift+click to connect). Drag to connect (ctrl+drag to clone). +portrait=Portrait +position=Position +posterPrint=Poster Print +preview=Preview +print=Print +radialTree=Radial Tree +redo=Redo +removeFormat=Clear Formatting +removeFromGroup=Remove from Group +removeIt=Remove {1} +removeWaypoint=Remove Waypoint +rename=Rename +renameIt=Rename {1} +replace=Replace +replaceIt={1} already exists. Do you want to replace it? +replaceExistingDrawing=Replace existing drawing +reset=Reset +resetView=Reset View +reverse=Reverse +right=Right +rightAlign=Right Align +rightToLeft=Right to Left +rotate=Rotate +rotateTooltip=Click and drag to rotate, click to turn by 90 degrees +rotation=Rotation +rounded=Rounded +save=Save +saveAs=Save as +saved=Saved +scrollbars=Scrollbars +search=Search +searchShapes=Search Shapes +selectAll=Select All +selectEdges=Select Edges +selectFont=Select a Font +selectNone=Select None +selectVertices=Select Vertices +setAsDefaultStyle=Set as Default Style +shadow=Shadow +shape=Shape +sharp=Sharp +sidebarTooltip=Click to expand. Drag and drop shapes into the diagram. Shift+click to change selection. Alt+click to insert and connect. +simple=Simple +simpleArrow=Simple Arrow +size=Size +solid=Solid +sourceSpacing=Source Spacing +south=South +spacing=Spacing +straight=Straight +strokeColor=Line Color +style=Style +subscript=Subscript +superscript=Superscript +table=Table +targetSpacing=Target Spacing +text=Text +textAlignment=Text Alignment +textOpacity=Text Opacity +toBack=To Back +toFront=To Front +tooltips=Tooltips +top=Top +topAlign=Top Align +topLeft=Top Left +topRight=Top Right +transparent=Transparent +turn=Rotate 90° +uml=UML +underline=Underline +undo=Undo +ungroup=Ungroup +url=URL +untitledLayer=Untitled Layer +vertical=Vertical +verticalFlow=Vertical Flow +verticalTree=Vertical Tree +view=View +waypoints=Waypoints +west=West +width=Width +wordWrap=Word Wrap +writingDirection=Writing Direction +zoom=Zoom +zoomIn=Zoom In +zoomOut=Zoom Out diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_de.txt b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_de.txt new file mode 100644 index 00000000..9755f885 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_de.txt @@ -0,0 +1,320 @@ +# Resources from graph.properties +alreadyConnected=Knoten schon verbunden +cancel=Abbrechen +close=Schliessen +collapse-expand=Einklappen/Ausklappen +containsValidationErrors=Enthält Validierungsfehler +done=Fertig +doubleClickOrientation=Doppelklicken um Orientierung zu ändern +error=Fehler +errorSavingFile=Fehler beim Speichern der Datei +ok=OK +updatingDocument=Aktualisiere Dokument. Bitte warten... +updatingSelection=Aktualisiere Markierung. Bitte warten... +# Custom resources +about=Über +actualSize=Tatsächliche Grösse +add=Hinzufügen +addLayer=Ebene einfügen +addProperty=Eigenschaft einfügen +addToExistingDrawing=In vorhandene Zeichnung einfügen +addWaypoint=Wegpunkt einfügen +advanced=Erweitert +align=Ausrichten +alignment=Ausrichtung +allChangesLost=Alle Änderungen gehen verloren! +angle=Winkel +apply=Anwenden +arc=Bogen +arrange=Anordnen +arrow=Pfeil +arrows=Pfeile +automatic=Automatisch +autosave=Automatisch Speichern +autosize=Grösse anpassen +background=Hintergrund +backgroundColor=Hintergrundfarbe +backgroundImage=Hintergrundbild +basic=Einfach +block=Block +blockquote=Zitat +bold=Fett +border=Rahmen +borderWidth=Rahmenbreite +borderColor=Rahmenfarbe +bottom=Unten +bottomAlign=Unten +bottomLeft=Unten links +bottomRight=Unten rechts +bulletedList=Aufzählungsliste +cannotOpenFile=Kann Datei nicht öffnen +center=Zentriert +change=Ändern +changeOrientation=Orientierung ändern +circle=Kreis +classic=Klassisch +clearDefaultStyle=Standardstyle löschen +clearWaypoints=Wegpunkte löschen +clipart=Clipart +collapse=Einklappen +collapseExpand=Ein-/Ausklappen +collapsible=Einklappbar +comic=Comic +connect=Verbinden +connection=Verbindung +connectionPoints=Verbindungspunkte +connectionArrows=Verbindungspfeile +constrainProportions=Proportionen beibehalten +copy=Kopieren +copyConnect=Beim Verbinden kopieren +copySize=Grösse kopieren +create=Erstellen +curved=Gebogen +custom=Benutzerdefiniert +cut=Ausschneiden +dashed=Gestrichelt +decreaseIndent=Einzug verringern +default=Vorgegeben +delete=Löschen +deleteColumn=Spalte löschen +deleteRow=Zeile löschen +diagram=Diagramm +diamond=Diamant +diamondThin=Diamant (Schmal) +direction=Richtung +distribute=Verteilen +divider=Treelinie +dotted=Punktiert +documentProperties=Dokumenteigenschaften +drawing=Zeichnung{1} +drawingEmpty=Zeichnung ist leer +drawingTooLarge=Zeichnung ist zu gross +duplicate=Duplizieren +duplicateIt={1} duplizieren +east=Ost +edit=Bearbeiten +editData=Metadaten bearbeiten +editDiagram=Diagramm bearbeiten +editImage=Bild bearbeiten +editLink=Link bearbeiten +editStyle=Style bearbeiten +editTooltip=Tooltip bearbeiten +enterGroup=In Gruppe hinein +enterValue=Wert eingeben +enterName=Namen eingeben +enterPropertyName=Eigenschaftsname eingeben +entityRelation=Entity Relation +exitGroup=Aus Gruppe heraus +expand=Ausklappen +export=Exportieren +extras=Extras +file=Datei +fileNotFound=Datei nicht gefunden +filename=Dateiname +fill=Füllen +fillColor=Füllfarbe +fitPage=Ganze Seite +fitPageWidth=Seitenbreite +fitTwoPages=Zwei Seiten +fitWindow=An Fenstergrösse anpassen +flip=Spiegeln +flipH=Horizontal Spiegeln +flipV=Vertikal Spiegeln +font=Schrift +fontFamily=Schriftart +fontColor=Schriftfarbe +fontSize=Schriftgrösse +format=Format +formatPanel=Bereich "Formatieren" +general=Allgemein +formatPdf=PDF +formatPng=PNG +formatGif=GIF +formatJpg=JPEG +formatSvg=SVG +formatXml=XML +formatted=Formatiert +formattedText=Formatierter Text +gap=Gap +glass=Glas +general=Allgemein +global=Global +gradient=Verlauf +gradientColor=Farbe +grid=Gitternetz +gridSize=Gitternetzgrösse +group=Gruppieren +guides=Führungslinien +heading=Überschrift +height=Höhe +help=Hilfe +hide=Verstecken +hideIt={1} verstecken +hidden=Versteckt +home=Ursprung +horizontal=Horizontal +horizontalFlow=Horizontaler Fluss +horizontalTree=Horizontaler Baum +html=HTML +id=ID +image=Bild +images=Bilder +import=Importieren +increaseIndent=Einzug vergrössern +insert=Einfügen +insertColumnBefore=Spalte links einfügen +insertColumnAfter=Spalte rechts einfügen +insertHorizontalRule=Horizontale Linie einfügen +insertImage=Bild einfügen +insertLink=Link einfügen +insertRowBefore=Zeile oberhalb einfügen +insertRowAfter=Zeile unterhalb einfügen +invalidName=Ungültiger Name +invalidOrMissingFile=Ungültige oder fehlende Datei +isometric=Isometrisch +italic=Kursiv +layers=Ebenen +landscape=Querformat +laneColor=Lane-Farbe +layout=Layout +left=Links +leftAlign=Links +leftToRight=Von links nach rechts +line=Linie +lineJumps=Liniensprünge +link=Link +lineend=Linienende +lineheight=Zeilenhöhe +linestart=Linienanfang +linewidth=Linienbreite +loading=Wird geladen +lockUnlock=Sperren/Entsperren +manual=Manuell +middle=Mitte +misc=Verschiedenes +more=Mehr +moreResults=Mehr Resultate +move=Verschieben +moveSelectionTo=Markierung in {1} einfügen +navigation=Navigation +new=Neu +noColor=Keine Farbe +noFiles=Keine Dateien +noMoreResults=Keine Weiteren Resultate +none=Ohne +noResultsFor=Keine Resultate für '{1}' +normal=Normal +north=Nord +numberedList=Nummerierte Liste +opacity=Deckkraft +open=Öffnen +openArrow=Offen +openFile=Datei öffnen +openLink=Link öffnen +openSupported=Unterstützte Formate sind mit dieser Anwendung erstellte .XML Dateien +openInNewWindow=In neuem Fenster öffnen +openInThisWindow=In diesem Fenster öffnen +options=Optionen +organic=Organisch +orthogonal=Orthogonal +outline=Übersicht +oval=Oval +pages=Seiten +pageView=Seitenansicht +pageScale=Seitenskalierung +pageSetup=Seite einrichten +panTooltip=Leertaste+Ziehen um zu scrollen +paperSize=Papiergrösse +paste=Einfügen +pasteHere=Hier einfügen +pasteSize=Grösse einfügen +pattern=Muster +perimeter=Umfang +placeholders=Platzhalter +plusTooltip=Klicken zum Verbinden und Klonen (Ctrl-Taste gedrückt halten zum Klonen, Shift+Klick zum Verbinden). Ziehen zum verbinden (Ctrl-Taste gedrückt halten zum Klonen). +portrait=Hochformat +position=Position +posterPrint=Posterdruck +preview=Vorschau +print=Drucken +radialTree=Radialer Baum +redo=Wiederherstellen +removeFormat=Formatierung entfernen +removeFromGroup=Aus Gruppe entfernen +removeIt={1} entfernen +removeWaypoint=Wegpunkt entfernen +rename=Umbenennen +renameIt={1} umbenennen +replace=Ersetzen +replaceIt={1} existiert bereits. Soll die Datei überschrieben werden? +replaceExistingDrawing=Vorhandene Zeichnung ersetzen +reset=Zurücksetzen +resetView=Ansicht zurücksetzen +reverse=Umdrehen +right=Rechts +rightAlign=Rechts +rightToLeft=Von rechts nach links +rotate=Rotieren +rotateTooltip=Klicken und ziehen um zu rotieren, klicken für 90 Grad Drehung +rotation=Rotation +rounded=Abgerundet +save=Speichern +saveAs=Speichern unter +saved=Gespeichert +scrollbars=Scrollbars +search=Suchen +searchShapes=Formen suchen +selectAll=Alles markieren +selectEdges=Kanten markieren +selectFont=Schriftart wählen +selectNone=Markierung aufheben +selectVertices=Knoten markieren +setAsDefaultStyle=Als Standardstyle festlegen +shadow=Schatten +shape=Shape +sharp=Eckig +sidebarTooltip=Klicken um zu erweitern. Objekte per Drag & Drop ins Diagramm einfügen. Shift+Klick um die Markierung zu ändern. Alt+Klick zum Einfügen und Verbinden. +simple=Einfach +simpleArrow=Einfacher Pfeil +size=Grösse +solid=Durchgehend +sourceSpacing=Anfangsabstand +south=Süd +spacing=Abstand +straight=Gerade +strokeColor=Linienfarbe +style=Style +subscript=Tiefgestellt +superscript=Hochgestellt +table=Tabelle +targetSpacing=Endabstand +text=Text +textAlignment=Text Ausrichtung +textOpacity=Text Deckkraft +toBack=Nach hinten +toFront=Nach vorne +tooltips=Tooltips +top=Oben +topLeft=Oben links +topRight=Oben rechts +topAlign=Oben +transparent=Transparent +turn=Drehen 90° +uml=UML +underline=Unterstrichen +undo=Rückgängig +ungroup=Gruppierung aufheben +untitledLayer=Unbenannte Ebene +url=URL +vertical=Vertikal +verticalFlow=Vertikaler Fluss +verticalTree=Vertikaler Baum +view=Ansicht +waypoints=Wegpunkte +west=West +width=Breite +wordWrap=Autom. Zeilenumbruch +writingDirection=Textrichtung +zoom=Zoom +zoomIn=Hineinzoomen +zoomOut=Herauszoomen diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_zh.txt b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_zh.txt new file mode 100644 index 00000000..6eb84556 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/grapheditor_zh.txt @@ -0,0 +1,321 @@ +# Resources from graph.properties +alreadyConnected=Nodes already connected +cancel=取消 +close=关闭 +collapse-expand=折叠/展开 +containsValidationErrors=Contains validation errors +done=完成 +doubleClickOrientation=双击改变方向 +error=错误 +errorSavingFile=保存文件时发生错误 +ok=确定 +updatingDocument=正在更新文档. 请等待 ... +updatingSelection=正在更新选择的内容. 请等待 ... +# Custom resources +about=关于 +actualSize=实际大小 +add=添加 +addLayer=添加层 +addProperty=添加属性 +addToExistingDrawing=添加到当前图表中 +addWaypoint=添加路径点 +advanced=高级 +align=对齐 +alignment=对齐 +allChangesLost=将丢失所有更改! +angle=角度 +apply=应用 +arc=Arc +arrange=排列 +arrow=Arrow +arrows=Arrows +automatic=自动 +autosave=自动保存 +autosize=自动大小 +background=背景 +backgroundColor=背景色 +backgroundImage=背景图片 +basic=基本 +block=块 +blockquote=块引用 +bold=粗体 +border=边框 +borderWidth=边框宽度 +borderColor=边框颜色 +bottom=下 +bottomAlign=底部对齐 +bottomLeft=左下 +bottomRight=右下 +bulletedList=Bulleted List +cannotOpenFile=不能打开文件 +center=水平居中 +change=改变 +changeOrientation=改变方向 +circle=圆 +classic=经典 +clearDefaultStyle=清除默认样式 +clearWaypoints=清除路径点 +clipart=Clipart +collapse=折叠 +collapseExpand=折叠/展开 +collapsible=可折叠 +comic=手稿 +configure=配置 +connect=连接 +connection=连接 +connectionPoints=鼠标移动到组件上时,显示连接点 +connectionArrows=鼠标移动到组件上时,显示连接箭头 +constrainProportions=强制比例 +copy=复制 +copyConnect=Copy on Connect +copySize=复制大小 +create=创建 +curved=弧形 +custom=定制 +cut=剪切 +dashed=虚线 +decreaseIndent=减少缩进 +default=默认 +delete=删除 +deleteColumn=删除列 +deleteRow=删除行 +diagram=图表 +diamond=菱形 +diamondThin=Diamond (thin) +direction=Direction +distribute=Distribute +divider=Divider +documentProperties=文档属性 +dotted=Dotted +drawing=Drawing{1} +drawingEmpty=Drawing is empty +drawingTooLarge=Drawing is too large +duplicate=Duplicate +duplicateIt=Duplicate {1} +east=东 +edit=编辑 +editData=编辑数据 +editDiagram=编辑图表XML +editImage=编辑图片 +editLink=编辑链接 +editStyle=编辑样式 +editTooltip=Edit Tooltip +enterGroup=Enter Group +enterValue=Enter Value +enterName=Enter Name +enterPropertyName=Enter Property Name +entityRelation=Entity Relation +exitGroup=Exit Group +expand=Expand +export=导出 +extras=Extras +file=文件 +fileNotFound=文件未发现 +filename=文件名 +fill=填充 +fillColor=填充颜色 +fitPage=一页 +fitPageWidth=页宽度 +fitTwoPages=两页 +fitWindow=适合窗口 +flip=翻转 +flipH=Flip Horizontal +flipV=Flip Vertical +font=字体 +fontFamily=字体族 +fontColor=字体颜色 +fontSize=字体大小 +format=格式 +formatPanel=格式面板 +general=Allgemein +formatPdf=PDF +formatPng=PNG +formatGif=GIF +formatJpg=JPEG +formatSvg=SVG +formatXml=XML +formatted=Formatted +formattedText=格式化文本 +gap=Gap +glass=高斯模糊 +general=General +global=全局 +gradient=渐变 +gradientColor=渐变色 +grid=网格 +gridSize=网格大小 +group=Group +guides=显示对齐参考线 +heading=Heading +height=高度 +help=帮助 +hide=隐藏 +hideIt=Hide {1} +hidden=隐藏 +home=Home +horizontal=水平 +horizontalFlow=Horizontal Flow +horizontalTree=Horizontal Tree +html=HTML +id=ID +image=图片 +images=Images +import=Import +increaseIndent=增加缩进 +insert=插入 +insertColumnBefore=在左边插入列 +insertColumnAfter=在右边插入列 +insertHorizontalRule=Insert Horizontal Rule +insertImage=插入图片 +insertLink=插入链接 +insertRowBefore=在上面插入行 +insertRowAfter=在下面插入行 +invalidName=不合法的名称 +invalidOrMissingFile=不合法或缺少文件 +isometric=Isometric +italic=斜体 +layers=Layers +landscape=横向 +laneColor=Lanecolor +layout=布局 +left=左 +leftAlign=左对齐 +leftToRight=从左到右 +line=线条 +link=链接 +lineJumps=相交线跳跃 +lineend=终点 +lineheight=Line Height +linestart=起点 +linewidth=Linewidth +loading=正在加载 +lockUnlock=锁定/解锁 +manual=手工 +middle=垂直居中 +misc=Misc +more=更多 +moreResults=更多结果 +move=移动 +moveSelectionTo=Move Selection to {1} +navigation=Navigation +new=新建 +noColor=No Color +noFiles=No files +noMoreResults=No more results +none=None +noResultsFor=No results for '{1}' +normal=Normal +north=North +numberedList=Numbered List +opacity=透明粗 +open=打开 +openArrow=Open Arrow +openFile=Open File +openLink=Open Link +openSupported=Supported format is .XML files saved from this software +openInNewWindow=在新窗口打开 +openInThisWindow=在当前窗口打开 +options=选项 +organic=Organic +orthogonal=Orthogonal +outline=Outline +oval=Oval +pages=Pages +pageView=页面视图 +pageScale=Page Scale +pageSetup=Page Setup +panTooltip=Space+Drag to Scroll +paperSize=纸张大小 +paste=Paste +pasteHere=Paste Here +pasteSize=Paste Size +pattern=Pattern +perimeter=Perimeter +placeholders=Placeholders +plusTooltip=Click to connect and clone (ctrl+click to clone, shift+click to connect). Drag to connect (ctrl+drag to clone). +portrait=纵向 +position=位置 +posterPrint=Poster Print +preview=Preview +print=Print +radialTree=Radial Tree +redo=重做 +removeFormat=Clear Formatting +removeFromGroup=Remove from Group +removeIt=Remove {1} +removeWaypoint=Remove Waypoint +rename=Rename +renameIt=Rename {1} +replace=替换 +replaceIt={1} already exists. Do you want to replace it? +replaceExistingDrawing=替换当前图表 +reset=Reset +resetView=Reset View +reverse=反转 +right=右 +rightAlign=右对齐 +rightToLeft=从右到左 +rotate=Rotate +rotateTooltip=Click and drag to rotate, click to turn by 90 degrees +rotation=Rotation +rounded=圆角 +save=保存 +saveAs=Save as +saved=Saved +scrollbars=Scrollbars +search=Search +searchShapes=搜索组件 +selectAll=Select All +selectEdges=Select Edges +selectFont=Select a Font +selectNone=Select None +selectVertices=Select Vertices +setAsDefaultStyle=设置为默认样式 +shadow=阴影 +shape=Shape +sharp=直角 +sidebarTooltip=点击展开.拖拽组件到编辑器中. Shift+click 改变选择的组件. Alt+click 插入新组件并和选择的组件进行连接. +simple=Simple +simpleArrow=Simple Arrow +size=大小 +solid=Solid +sourceSpacing=Source Spacing +south=South +spacing=空白 +straight=Straight +strokeColor=线条颜色 +style=样式 +subscript=Subscript +superscript=Superscript +table=Table +targetSpacing=Target Spacing +text=文本 +textAlignment=Text Alignment +textOpacity=Text Opacity +toBack=置于底层 +toFront=置于顶层 +tooltips=Tooltips +top=上 +topAlign=顶部对齐 +topLeft=左上 +topRight=右上 +transparent=Transparent +turn=顺时针旋转 90° +uml=UML +underline=下划线 +undo=撤销 +ungroup=Ungroup +url=URL +untitledLayer=Untitled Layer +vertical=垂直 +verticalFlow=Vertical Flow +verticalTree=Vertical Tree +view=视图 +waypoints=路径 +west=West +width=宽度 +wordWrap=自动换行 +writingDirection=文本方向 +zoom=缩放 +zoomIn=放大 +zoomOut=缩小 diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help.html b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help.html new file mode 100644 index 00000000..4d2ecbcb --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help.html @@ -0,0 +1,20 @@ + + + + Graph Editor Help + + + + +

Graph Editor Help

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy + eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam + voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet + clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit + amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed + diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor + sit amet.

+ + diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help_de.html b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help_de.html new file mode 100644 index 00000000..165f552c --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/resources/help_de.html @@ -0,0 +1,20 @@ + + + + Graph Editor Hilfe + + + + +

Graph Editor Hilfe

+

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy + eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam + voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet + clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit + amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed + diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. + Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor + sit amet.

+ + diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/sanitizer/sanitizer.min.js b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/sanitizer/sanitizer.min.js new file mode 100644 index 00000000..6e7d30d5 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/sanitizer/sanitizer.min.js @@ -0,0 +1,91 @@ +// NOTE: Modified to support data URIs for images, ie. data:image/* +(function(){var c=void 0,n=!0,s=null,C=!1,J=["aliceblue,antiquewhite,aqua,aquamarine,azure,beige,bisque,black,blanchedalmond,blue,blueviolet,brown,burlywood,cadetblue,chartreuse,chocolate,coral,cornflowerblue,cornsilk,crimson,cyan,darkblue,darkcyan,darkgoldenrod,darkgray,darkgreen,darkkhaki,darkmagenta,darkolivegreen,darkorange,darkorchid,darkred,darksalmon,darkseagreen,darkslateblue,darkslategray,darkturquoise,darkviolet,deeppink,deepskyblue,dimgray,dodgerblue,firebrick,floralwhite,forestgreen,fuchsia,gainsboro,ghostwhite,gold,goldenrod,gray,green,greenyellow,honeydew,hotpink,indianred,indigo,ivory,khaki,lavender,lavenderblush,lawngreen,lemonchiffon,lightblue,lightcoral,lightcyan,lightgoldenrodyellow,lightgreen,lightgrey,lightpink,lightsalmon,lightseagreen,lightskyblue,lightslategray,lightsteelblue,lightyellow,lime,limegreen,linen,magenta,maroon,mediumaquamarine,mediumblue,mediumorchid,mediumpurple,mediumseagreen,mediumslateblue,mediumspringgreen,mediumturquoise,mediumvioletred,midnightblue,mintcream,mistyrose,moccasin,navajowhite,navy,oldlace,olive,olivedrab,orange,orangered,orchid,palegoldenrod,palegreen,paleturquoise,palevioletred,papayawhip,peachpuff,peru,pink,plum,powderblue,purple,red,rosybrown,royalblue,saddlebrown,salmon,sandybrown,seagreen,seashell,sienna,silver,skyblue,slateblue,slategray,snow,springgreen,steelblue,tan,teal,thistle,tomato,transparent,turquoise,violet,wheat,white,whitesmoke,yellow,yellowgreen".split(","), +"all-scroll,col-resize,crosshair,default,e-resize,hand,help,move,n-resize,ne-resize,no-drop,not-allowed,nw-resize,pointer,progress,row-resize,s-resize,se-resize,sw-resize,text,vertical-text,w-resize,wait".split(","),"armenian,decimal,decimal-leading-zero,disc,georgian,lower-alpha,lower-greek,lower-latin,lower-roman,square,upper-alpha,upper-latin,upper-roman".split(","),"100,200,300,400,500,600,700,800,900,bold,bolder,lighter".split(","),"block-level,inline-level,table-caption,table-cell,table-column,table-column-group,table-footer-group,table-header-group,table-row,table-row-group".split(","), +"condensed,expanded,extra-condensed,extra-expanded,narrower,semi-condensed,semi-expanded,ultra-condensed,ultra-expanded,wider".split(","),"inherit,inline,inline-block,inline-box,inline-flex,inline-grid,inline-list-item,inline-stack,inline-table,run-in".split(","),"behind,center-left,center-right,far-left,far-right,left-side,leftwards,right-side,rightwards".split(","),"large,larger,small,smaller,x-large,x-small,xx-large,xx-small".split(","),"dashed,dotted,double,groove,outset,ridge,solid".split(","), +"ease,ease-in,ease-in-out,ease-out,linear,step-end,step-start".split(","),"at,closest-corner,closest-side,ellipse,farthest-corner,farthest-side".split(","),"baseline,middle,sub,super,text-bottom,text-top".split(","),"caption,icon,menu,message-box,small-caption,status-bar".split(","),"fast,faster,slow,slower,x-fast,x-slow".split(","),["above","below","higher","level","lower"],["cursive","fantasy","monospace","sans-serif","serif"],["loud","silent","soft","x-loud","x-soft"],["no-repeat","repeat-x","repeat-y", +"round","space"],["blink","line-through","overline","underline"],["block","flex","grid","table"],["high","low","x-high","x-low"],["nowrap","pre","pre-line","pre-wrap"],["absolute","relative","static"],["alternate","alternate-reverse","reverse"],["border-box","content-box","padding-box"],["capitalize","lowercase","uppercase"],["child","female","male"],["=","opacity"],["backwards","forwards"],["bidi-override","embed"],["bottom","top"],["break-all","keep-all"],["clip","ellipsis"],["contain","cover"], +["continuous","digits"],["end","start"],["flat","preserve-3d"],["hide","show"],["horizontal","vertical"],["inside","outside"],["italic","oblique"],["left","right"],["ltr","rtl"],["no-content","no-display"],["paused","running"],["suppress","unrestricted"],["thick","thin"],[","],["/"],["all"],["always"],["auto"],["avoid"],["both"],["break-word"],["center"],["circle"],["code"],["collapse"],["contents"],["fixed"],["hidden"],["infinite"],["inset"],["invert"],["justify"],["list-item"],["local"],["medium"], +["mix"],["none"],["normal"],["once"],["repeat"],["scroll"],["separate"],["small-caps"],["spell-out"],["to"],["visible"]],L={animation:{cssPropBits:517,cssLitGroup:[J[10],J[24],J[29],J[45],J[48],J[54],J[63],J[71],J[72]],cssFns:["cubic-bezier()","steps()"]},"animation-delay":{cssPropBits:5,cssLitGroup:[J[48]],cssFns:[]},"animation-direction":{cssPropBits:0,cssLitGroup:[J[24],J[48],J[72]],cssFns:[]},"animation-duration":"animation-delay","animation-fill-mode":{cssPropBits:0,cssLitGroup:[J[29],J[48], +J[54],J[71]],cssFns:[]},"animation-iteration-count":{cssPropBits:5,cssLitGroup:[J[48],J[63]],cssFns:[]},"animation-name":{cssPropBits:512,cssLitGroup:[J[48],J[71]],cssFns:[]},"animation-play-state":{cssPropBits:0,cssLitGroup:[J[45],J[48]],cssFns:[]},"animation-timing-function":{cssPropBits:0,cssLitGroup:[J[10],J[48]],cssFns:["cubic-bezier()","steps()"]},appearance:{cssPropBits:0,cssLitGroup:[J[71]],cssFns:[]},azimuth:{cssPropBits:5,cssLitGroup:[J[7],J[42],J[56]],cssFns:[]},"backface-visibility":{cssPropBits:0, +cssLitGroup:[J[59],J[62],J[80]],cssFns:[]},background:{cssPropBits:23,cssLitGroup:[J[0],J[18],J[25],J[31],J[34],J[42],J[48],J[49],J[52],J[56],J[61],J[68],J[71],J[74],J[75]],cssFns:"image(),linear-gradient(),radial-gradient(),repeating-linear-gradient(),repeating-radial-gradient(),rgb(),rgba()".split(",")},"background-attachment":{cssPropBits:0,cssLitGroup:[J[48],J[61],J[68],J[75]],cssFns:[]},"background-color":{cssPropBits:2,cssLitGroup:[J[0]],cssFns:["rgb()","rgba()"]},"background-image":{cssPropBits:16, +cssLitGroup:[J[48],J[71]],cssFns:["image()","linear-gradient()","radial-gradient()","repeating-linear-gradient()","repeating-radial-gradient()"]},"background-position":{cssPropBits:5,cssLitGroup:[J[31],J[42],J[48],J[56]],cssFns:[]},"background-repeat":{cssPropBits:0,cssLitGroup:[J[18],J[48],J[74]],cssFns:[]},"background-size":{cssPropBits:5,cssLitGroup:[J[34],J[48],J[52]],cssFns:[]},border:{cssPropBits:7,cssLitGroup:[J[0],J[9],J[47],J[62],J[64],J[69],J[71]],cssFns:["rgb()","rgba()"]},"border-bottom":"border", +"border-bottom-color":"background-color","border-bottom-left-radius":{cssPropBits:5,cssFns:[]},"border-bottom-right-radius":"border-bottom-left-radius","border-bottom-style":{cssPropBits:0,cssLitGroup:[J[9],J[62],J[64],J[71]],cssFns:[]},"border-bottom-width":{cssPropBits:5,cssLitGroup:[J[47],J[69]],cssFns:[]},"border-collapse":{cssPropBits:0,cssLitGroup:[J[59],J[76]],cssFns:[]},"border-color":"background-color","border-left":"border","border-left-color":"background-color","border-left-style":"border-bottom-style", +"border-left-width":"border-bottom-width","border-radius":{cssPropBits:5,cssLitGroup:[J[49]],cssFns:[]},"border-right":"border","border-right-color":"background-color","border-right-style":"border-bottom-style","border-right-width":"border-bottom-width","border-spacing":"border-bottom-left-radius","border-style":"border-bottom-style","border-top":"border","border-top-color":"background-color","border-top-left-radius":"border-bottom-left-radius","border-top-right-radius":"border-bottom-left-radius", +"border-top-style":"border-bottom-style","border-top-width":"border-bottom-width","border-width":"border-bottom-width",bottom:{cssPropBits:5,cssLitGroup:[J[52]],cssFns:[]},box:{cssPropBits:0,cssLitGroup:[J[60],J[71],J[72]],cssFns:[]},"box-shadow":{cssPropBits:7,cssLitGroup:[J[0],J[48],J[64],J[71]],cssFns:["rgb()","rgba()"]},"box-sizing":{cssPropBits:0,cssLitGroup:[J[25]],cssFns:[]},"caption-side":{cssPropBits:0,cssLitGroup:[J[31]],cssFns:[]},clear:{cssPropBits:0,cssLitGroup:[J[42],J[54],J[71]],cssFns:[]}, +clip:{cssPropBits:0,cssLitGroup:[J[52]],cssFns:["rect()"]},color:"background-color",content:{cssPropBits:8,cssLitGroup:[J[71],J[72]],cssFns:[]},cue:{cssPropBits:16,cssLitGroup:[J[71]],cssFns:[]},"cue-after":"cue","cue-before":"cue",cursor:{cssPropBits:16,cssLitGroup:[J[1],J[48],J[52]],cssFns:[]},direction:{cssPropBits:0,cssLitGroup:[J[43]],cssFns:[]},display:{cssPropBits:0,cssLitGroup:[J[4],J[6],J[20],J[52],J[67],J[71]],cssFns:[]},"display-extras":{cssPropBits:0,cssLitGroup:[J[67],J[71]],cssFns:[]}, +"display-inside":{cssPropBits:0,cssLitGroup:[J[20],J[52]],cssFns:[]},"display-outside":{cssPropBits:0,cssLitGroup:[J[4],J[71]],cssFns:[]},elevation:{cssPropBits:5,cssLitGroup:[J[15]],cssFns:[]},"empty-cells":{cssPropBits:0,cssLitGroup:[J[38]],cssFns:[]},filter:{cssPropBits:0,cssFns:["alpha()"]},"float":{cssPropBits:0,cssLitGroup:[J[42],J[71]],cssFns:[]},font:{cssPropBits:73,cssLitGroup:[J[3],J[8],J[13],J[16],J[41],J[48],J[49],J[69],J[72],J[77]],cssFns:[]},"font-family":{cssPropBits:72,cssLitGroup:[J[16], +J[48]],cssFns:[]},"font-size":{cssPropBits:1,cssLitGroup:[J[8],J[69]],cssFns:[]},"font-stretch":{cssPropBits:0,cssLitGroup:[J[5],J[72]],cssFns:[]},"font-style":{cssPropBits:0,cssLitGroup:[J[41],J[72]],cssFns:[]},"font-variant":{cssPropBits:0,cssLitGroup:[J[72],J[77]],cssFns:[]},"font-weight":{cssPropBits:0,cssLitGroup:[J[3],J[72]],cssFns:[]},height:"bottom",left:"bottom","letter-spacing":{cssPropBits:5,cssLitGroup:[J[72]],cssFns:[]},"line-height":{cssPropBits:1,cssLitGroup:[J[72]],cssFns:[]},"list-style":{cssPropBits:16, +cssLitGroup:[J[2],J[40],J[57],J[71]],cssFns:["image()","linear-gradient()","radial-gradient()","repeating-linear-gradient()","repeating-radial-gradient()"]},"list-style-image":{cssPropBits:16,cssLitGroup:[J[71]],cssFns:["image()","linear-gradient()","radial-gradient()","repeating-linear-gradient()","repeating-radial-gradient()"]},"list-style-position":{cssPropBits:0,cssLitGroup:[J[40]],cssFns:[]},"list-style-type":{cssPropBits:0,cssLitGroup:[J[2],J[57],J[71]],cssFns:[]},margin:"bottom","margin-bottom":"bottom", +"margin-left":"bottom","margin-right":"bottom","margin-top":"bottom","max-height":{cssPropBits:1,cssLitGroup:[J[52],J[71]],cssFns:[]},"max-width":"max-height","min-height":{cssPropBits:1,cssLitGroup:[J[52]],cssFns:[]},"min-width":"min-height",opacity:{cssPropBits:1,cssFns:[]},outline:{cssPropBits:7,cssLitGroup:[J[0],J[9],J[47],J[62],J[64],J[65],J[69],J[71]],cssFns:["rgb()","rgba()"]},"outline-color":{cssPropBits:2,cssLitGroup:[J[0],J[65]],cssFns:["rgb()","rgba()"]},"outline-style":"border-bottom-style", +"outline-width":"border-bottom-width",overflow:{cssPropBits:0,cssLitGroup:[J[52],J[62],J[75],J[80]],cssFns:[]},"overflow-wrap":{cssPropBits:0,cssLitGroup:[J[55],J[72]],cssFns:[]},"overflow-x":{cssPropBits:0,cssLitGroup:[J[44],J[52],J[62],J[75],J[80]],cssFns:[]},"overflow-y":"overflow-x",padding:"opacity","padding-bottom":"opacity","padding-left":"opacity","padding-right":"opacity","padding-top":"opacity","page-break-after":{cssPropBits:0,cssLitGroup:[J[42],J[51],J[52],J[53]],cssFns:[]},"page-break-before":"page-break-after", +"page-break-inside":{cssPropBits:0,cssLitGroup:[J[52],J[53]],cssFns:[]},pause:"border-bottom-left-radius","pause-after":"border-bottom-left-radius","pause-before":"border-bottom-left-radius",perspective:{cssPropBits:5,cssLitGroup:[J[71]],cssFns:[]},"perspective-origin":{cssPropBits:5,cssLitGroup:[J[31],J[42],J[56]],cssFns:[]},pitch:{cssPropBits:5,cssLitGroup:[J[21],J[69]],cssFns:[]},"pitch-range":"border-bottom-left-radius","play-during":{cssPropBits:16,cssLitGroup:[J[52],J[70],J[71],J[74]],cssFns:[]}, +position:{cssPropBits:0,cssLitGroup:[J[23]],cssFns:[]},quotes:{cssPropBits:8,cssLitGroup:[J[71]],cssFns:[]},resize:{cssPropBits:0,cssLitGroup:[J[39],J[54],J[71]],cssFns:[]},richness:"border-bottom-left-radius",right:"bottom",speak:{cssPropBits:0,cssLitGroup:[J[71],J[72],J[78]],cssFns:[]},"speak-header":{cssPropBits:0,cssLitGroup:[J[51],J[73]],cssFns:[]},"speak-numeral":{cssPropBits:0,cssLitGroup:[J[35]],cssFns:[]},"speak-punctuation":{cssPropBits:0,cssLitGroup:[J[58],J[71]],cssFns:[]},"speech-rate":{cssPropBits:5, +cssLitGroup:[J[14],J[69]],cssFns:[]},stress:"border-bottom-left-radius","table-layout":{cssPropBits:0,cssLitGroup:[J[52],J[61]],cssFns:[]},"text-align":{cssPropBits:0,cssLitGroup:[J[42],J[56],J[66]],cssFns:[]},"text-decoration":{cssPropBits:0,cssLitGroup:[J[19],J[71]],cssFns:[]},"text-indent":"border-bottom-left-radius","text-overflow":{cssPropBits:8,cssLitGroup:[J[33]],cssFns:[]},"text-shadow":"box-shadow","text-transform":{cssPropBits:0,cssLitGroup:[J[26],J[71]],cssFns:[]},"text-wrap":{cssPropBits:0, +cssLitGroup:[J[46],J[71],J[72]],cssFns:[]},top:"bottom",transform:{cssPropBits:0,cssLitGroup:[J[71]],cssFns:"matrix(),perspective(),rotate(),rotate3d(),rotatex(),rotatey(),rotatez(),scale(),scale3d(),scalex(),scaley(),scalez(),skew(),skewx(),skewy(),translate(),translate3d(),translatex(),translatey(),translatez()".split(",")},"transform-origin":"perspective-origin","transform-style":{cssPropBits:0,cssLitGroup:[J[37]],cssFns:[]},transition:{cssPropBits:1029,cssLitGroup:[J[10],J[48],J[50],J[71]],cssFns:["cubic-bezier()", +"steps()"]},"transition-delay":"animation-delay","transition-duration":"animation-delay","transition-property":{cssPropBits:1024,cssLitGroup:[J[48],J[50]],cssFns:[]},"transition-timing-function":"animation-timing-function","unicode-bidi":{cssPropBits:0,cssLitGroup:[J[30],J[72]],cssFns:[]},"vertical-align":{cssPropBits:5,cssLitGroup:[J[12],J[31]],cssFns:[]},visibility:"backface-visibility","voice-family":{cssPropBits:8,cssLitGroup:[J[27],J[48]],cssFns:[]},volume:{cssPropBits:1,cssLitGroup:[J[17],J[69]], +cssFns:[]},"white-space":{cssPropBits:0,cssLitGroup:[J[22],J[72]],cssFns:[]},width:"min-height","word-break":{cssPropBits:0,cssLitGroup:[J[32],J[72]],cssFns:[]},"word-spacing":"letter-spacing","word-wrap":"overflow-wrap","z-index":"bottom",zoom:"line-height","cubic-bezier()":"animation-delay","steps()":{cssPropBits:5,cssLitGroup:[J[36],J[48]],cssFns:[]},"image()":{cssPropBits:18,cssLitGroup:[J[0],J[48]],cssFns:["rgb()","rgba()"]},"linear-gradient()":{cssPropBits:7,cssLitGroup:[J[0],J[31],J[42],J[48], +J[79]],cssFns:["rgb()","rgba()"]},"radial-gradient()":{cssPropBits:7,cssLitGroup:[J[0],J[11],J[31],J[42],J[48],J[56],J[57]],cssFns:["rgb()","rgba()"]},"repeating-linear-gradient()":"linear-gradient()","repeating-radial-gradient()":"radial-gradient()","rgb()":{cssPropBits:1,cssLitGroup:[J[48]],cssFns:[]},"rgba()":"rgb()","rect()":{cssPropBits:5,cssLitGroup:[J[48],J[52]],cssFns:[]},"alpha()":{cssPropBits:1,cssLitGroup:[J[28]],cssFns:[]},"matrix()":"animation-delay","perspective()":"border-bottom-left-radius", +"rotate()":"border-bottom-left-radius","rotate3d()":"animation-delay","rotatex()":"border-bottom-left-radius","rotatey()":"border-bottom-left-radius","rotatez()":"border-bottom-left-radius","scale()":"animation-delay","scale3d()":"animation-delay","scalex()":"border-bottom-left-radius","scaley()":"border-bottom-left-radius","scalez()":"border-bottom-left-radius","skew()":"animation-delay","skewx()":"border-bottom-left-radius","skewy()":"border-bottom-left-radius","translate()":"animation-delay","translate3d()":"animation-delay", +"translatex()":"border-bottom-left-radius","translatey()":"border-bottom-left-radius","translatez()":"border-bottom-left-radius"},O;for(O in L)"string"===typeof L[O]&&Object.hasOwnProperty.call(L,O)&&(L[O]=L[L[O]]);"undefined"!==typeof window&&(window.cssSchema=L);var U,X; +(function(){function g(a){var f=parseInt(a.substring(1),16);return 65535>10),56320+(f&1023))):f==f?String.fromCharCode(f):" ">a[1]?"":a[1]}function w(a,f){return'"'+a.replace(/[\u0000-\u001f\\\"<>]/g,f)+'"'}function M(a){return E[a]||(E[a]="\\"+a.charCodeAt(0).toString(16)+" ")}function x(a){return e[a]||(e[a]=("\u0010">a?"%0":"%")+a.charCodeAt(0).toString(16))}var E={"\\":"\\\\"},e={"\\":"%5c"},v=RegExp("\\uFEFF|U[+][0-9A-F?]{1,6}(?:-[0-9A-F]{1,6})?|url[(][\\t\\n\\f ]*(?:\"(?:'|[^'\"\\n\\f\\\\]|\\\\[\\s\\S])*\"|'(?:\"|[^'\"\\n\\f\\\\]|\\\\[\\s\\S])*'|(?:[\\t\\x21\\x23-\\x26\\x28-\\x5b\\x5d-\\x7e]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))*)[\\t\\n\\f ]*[)]|(?!url[(])-?(?:[a-zA-Z_]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))(?:[a-zA-Z0-9_-]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))*[(]|(?:@?-?(?:[a-zA-Z_]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))|#)(?:[a-zA-Z0-9_-]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))*|\"(?:'|[^'\"\\n\\f\\\\]|\\\\[\\s\\S])*\"|'(?:\"|[^'\"\\n\\f\\\\]|\\\\[\\s\\S])*'|[-+]?(?:[0-9]+(?:[.][0-9]+)?|[.][0-9]+)(?:%|-?(?:[a-zA-Z_]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))(?:[a-zA-Z0-9_-]|[\\u0080-\\ud7ff\\ue000-\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]|\\\\(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff]))*)?|<\!--|--\>|[\\t\\n\\f ]+|/(?:[*][^*]*[*]+(?:[^/][^*]*[*]+)*/|/[^\\n\\f]*)|[~|^$*]=|[^\"'\\\\/]|/(?![/*])","gi"), +b=RegExp("\\\\(?:(?:[0-9a-fA-F]{1,6}[\\t\\n\\f ]?|[\\u0020-\\u007e\\u0080-\\ud7ff\\ue000\\ufffd]|[\\ud800-\\udbff][\\udc00-\\udfff])|[\\n\\f])","g"),a=RegExp("^url\\([\\t\\n\\f ]*[\"']?|[\"']?[\\t\\n\\f ]*\\)$","gi");X=function(a){return a.replace(b,g)};U=function(b){for(var b=(""+b).replace(/\r\n?/g,"\n").match(v)||[],f=0,h=" ",d=0,y=b.length;d"==l||"<\!--"==l||"\ufeff"==l||32>=g?" ": +/url\(/i.test(l)?"url("+w(l.replace(a,""),x)+")":l;if(h!=l||" "!=l)b[f++]=h=l}b.length=f;return b}})();"undefined"!==typeof window&&(window.lexCss=U,window.decodeCss=X);var Y=function(){function g(d){d=(""+d).match(k);return!d?s:new e(v(d[1]),v(d[2]),v(d[3]),v(d[4]),v(d[5]),v(d[6]),v(d[7]))}function w(d,a){return"string"==typeof d?encodeURI(d).replace(a,M):s}function M(d){d=d.charCodeAt(0);return"%"+"0123456789ABCDEF".charAt(d>>4&15)+"0123456789ABCDEF".charAt(d&15)}function x(d){if(d===s)return s;for(var d=d.replace(/(^|\/)\.(?:\/|$)/g,"$1").replace(/\/{2,}/g,"/"),a=b,h;(h=d.replace(a,"$1"))!=d;d=h);return d}function E(d,h){var b=d.T(),f=h.K();f?b.ga(h.j):f=h.X(); +f?b.da(h.n):f=h.Y();f?b.ea(h.k):f=h.$();var g=h.g,k=x(g);if(f)b.ca(h.V()),k=k&&k.replace(a,"");else if(f=!!g){if(47!==k.charCodeAt(0))var k=x(b.g||"").replace(a,""),e=k.lastIndexOf("/")+1,k=x((e?k.substring(0,e):"")+x(g)).replace(a,"")}else k=k&&k.replace(a,""),k!==g&&b.G(k);f?b.G(k):f=h.aa();f?b.O(h.l):f=h.Z();f&&b.fa(h.o);return b}function e(d,a,h,f,b,g,k){this.j=d;this.n=a;this.k=h;this.h=f;this.g=b;this.l=g;this.o=k}function v(d){return"string"==typeof d&&0]/g,v={"\n":"%0a","\u000c":"%0c","\r":"%0d",'"':"%22","'":"%27","(":"%28",")":"%29","*":"%2a","<":"%3c",">":"%3e"}, +b=/^(?:([^:/?# ]+):)?/,a=/^(?:https?|mailto|data)$/i;aa=function(){var a={};return function y(f,b,k,e,N){var f=E(f),u=L[f];if(!u||"object"!==typeof u)b.length=0;else{for(var i=u.cssPropBits,q=i&80,B=i&1536,F=NaN,r=0,o=0;r=I)j=i&1?j:"";else if(R=j.charCodeAt(1),v=j.charCodeAt(2),P=48<=R&&57>=R,S=48<=v&&57>=v,43===I&&(P||46===R&&S))j=i&1?(P?"":"0")+j.substring(1):"";else if(45===I&&(P||46===R&&S))j=i&4?(P?"-":"-0")+j.substring(1):i&1?"0":"";else if(46===I&&P)j=i&1?"0"+j:"";else if('url("'===j.substring(0,5))j=k&&i&16?g(x(M(e,b[r].substring(5,j.length-2)),f,k)): +"";else if("("===j.charAt(j.length-1))a:{D=b;G=r;j=1;K=G+1;for(I=D.length;K"]= +f["+"]=f["~"]=f;ba=function(a,d,b){function g(i,r){function o(b,f,g){var y,e,i,l,o,m=n;y="";if(bh&&" "===b[e-1]&&--e;f.startAtrule&&f.startAtrule(b[d].toLowerCase(),b.slice(h,e));a="{"===b[a]?w(b,a,g,f):a+1;f.endAtrule&&f.endAtrule()}return a}function w(b,a,k,f){++a;for(f.startBlock&&f.startBlock();ad)return d=~d,d===h?d+1:d;var y=b[d];if("{"!==y)return d===h?d+1:d;a=d+1;d>h&&" "===b[d-1]&&--d;for(f.startRuleset&&f.startRuleset(b.slice(h,d));ad)d=~d;else{for(var e=[],l=0,w=a;w])/g;f=b+"";if(G)f=f.split(H);else{for(var e=[],h=0,j;(j=H.exec(f))!==s;)e.push(f.substring(h,j.index)),e.push(j[0]),h=j.index+j[0].length;e.push(f.substring(h));f=e}a(d,f,0,{r:C,C:C},g)}}function b(b,d,g,f,t){return function(){a(b,d,g,f,t)}}function a(a,d,p,e,t){try{a.H&&0==p&&a.H(t);for(var h, +z,j,i=d.length;p"===d[p+1])p+=2,j=h[1].toLowerCase(),a.t&&a.t(j,t,A,b(a,d,p,e,t));else{var m=d,q=p,r=a,u=t,v=A,y=e,w=f(m,q);w?(r.t&&r.t(w.name,u,v,b(r,m,q,y,u)),p=w.next):p=m.length}else a.e&&a.e("</",t,A,b(a,d,p,e,t));break;case "<":if(h=/^([-\w:]+)\s*\/?/.exec(l))if(h[0].length===l.length&& +">"===d[p+1]){p+=2;j=h[1].toLowerCase();a.w&&a.w(j,[],t,A,b(a,d,p,e,t));var B=g.f[j];B&K&&(p=k(d,{name:j,next:p,c:B},a,t,A,e))}else{var m=d,q=a,r=t,u=A,v=e,x=f(m,p);x?(q.w&&q.w(x.name,x.R,r,u,b(q,m,x.next,v,r)),p=x.c&K?k(m,x,q,r,u,v):x.next):p=m.length}else a.e&&a.e("<",t,A,b(a,d,p,e,t));break;case "<\!--":if(!e.C){for(z=p+1;z"===d[z]&&/--$/.test(d[z-1]));z++);if(z"!==d[z];z++);z"!==d[z];z++);z":a.e&&a.e(">",t,A,b(a,d,p,e,t));break;case "":break;default:a.e&&a.e(o,t,A,b(a,d,p,e,t))}}a.B&&a.B(t)}catch(E){if(E!==A)throw E;}}function k(a,d,f,h,t,j){var z=a.length;T.hasOwnProperty(d.name)|| +(T[d.name]=RegExp("^"+d.name+"(?:[\\s\\/]|$)","i"));for(var i=T[d.name],k=d.next,l=d.next+1;l"!==a[h];h++)e+=a[h];if(!(j<=h)){for(var l=[];""!== +e;)if(d=ja.exec(e))if(d[4]&&!d[5]||d[6]&&!d[7]){for(var d=d[4]||d[6],i=C,e=[e,a[h++]];h"===a[h])break}else 0<=a[h].indexOf(d)&&(i=n);e.push(a[h])}if(j<=h)break;e=e.join("")}else{var i=d[1].toLowerCase(),k;if(d[2]){k=d[3];var m=k.charCodeAt(0);if(34===m||39===m)k=k.substr(1,k.length-2);k=x(k.replace(o,""))}else k="";l.push(i,k);e=e.substr(d[0].length)}else e=e.replace(/^[\s\S][^a-z\s]*/,"");f.R=l;f.next=h+1;return f}}function h(a){function b(a,d){f||d.push(a)}var d,f;return v({startDoc:function(){d= +[];f=C},startTag:function(b,e,h){if(!f&&g.f.hasOwnProperty(b)){var j=g.f[b];if(!(j&g.c.FOLDABLE)){var k=a(b,e);if(k){if("object"!==typeof k)throw Error("tagPolicy did not return object (old API?)");if("attribs"in k)e=k.attribs;else throw Error("tagPolicy gave no attribs");var i;"tagName"in k?(i=k.tagName,k=g.f[i]):(i=b,k=j);if(j&g.c.OPTIONAL_ENDTAG){var l=d[d.length-1];l&&l.D===b&&(l.v!==i||b!==i)&&h.push("")}j&g.c.EMPTY||d.push({D:b,v:i});h.push("<",i);b=0;for(l=e.length;b");j&g.c.EMPTY&&!(k&g.c.EMPTY)&&h.push("")}else f=!(j&g.c.EMPTY)}}},endTag:function(a,b){if(f)f=C;else if(g.f.hasOwnProperty(a)){var e=g.f[a];if(!(e&(g.c.EMPTY|g.c.FOLDABLE))){if(e&g.c.OPTIONAL_ENDTAG)for(e=d.length;0<=--e;){var h=d[e].D;if(h===a)break;if(!(g.f[h]&g.c.OPTIONAL_ENDTAG))return}else for(e=d.length;0<=--e&&d[e].D!==a;);if(!(0>e)){for(h=d.length;--h>e;){var j=d[h].v;g.f[j]&g.c.OPTIONAL_ENDTAG||b.push("")}e< +d.length&&(a=d[e].v);d.length=e;b.push("")}}}},pcdata:b,rcdata:b,cdata:b,endDoc:function(a){for(;d.length;d.length--)a.push("")}})}function d(a,b,d,f,e){if(!e)return s;try{var g=Y.parse(""+a);if(g&&(!g.K()||ka.test(g.W()))){var h=e(g,b,d,f);return h?h.toString():s}}catch(j){}return s}function y(a,b,d,f,e){d||a(b+" removed",{S:"removed",tagName:b});if(f!==e){var g="changed";f&&!e?g="removed":!f&&e&&(g="added");a(b+"."+d+" "+g,{S:g,tagName:b,la:d,oldValue:f,newValue:e})}} +function l(a,b,d){b=b+"::"+d;if(a.hasOwnProperty(b))return a[b];b="*::"+d;if(a.hasOwnProperty(b))return a[b]}function V(a,b,f,e,h){for(var j=0;j",GT:">",amp:"&",AMP:"&",quot:'"',apos:"'",nbsp:"\u00a0"},q=/^#(\d+)$/,B=/^#x([0-9A-Fa-f]+)$/,F=/^[A-Za-z][A-za-z0-9]+$/,r="undefined"!==typeof window&& +window.document?window.document.createElement("textarea"):s,o=/\0/g,j=/&(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/g,I=/^(#[0-9]+|#[xX][0-9A-Fa-f]+|\w+);/,R=/&/g,ia=/&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi,P=/[<]/g,S=/>/g,D=/\"/g,ja=/^\s*([-.:\w]+)(?:\s*(=)\s*((")[^"]*("|$)|(')[^']*('|$)|(?=[a-z][-\w]*\s*=)|[^"'\s]*))?/i,G=3==="a,b".split(/(,)/).length,K=g.c.CDATA|g.c.RCDATA,A={},T={},ka=/^(?:https?|mailto|data)$/i,m={};m.pa=m.escapeAttrib=E;m.ra=m.makeHtmlSanitizer=h;m.sa=m.makeSaxParser=v;m.ta=m.makeTagPolicy= +ea;m.wa=m.normalizeRCData=e;m.xa=m.sanitize=function(a,b,d,e){return Q(a,ea(b,d,e))};m.ya=m.sanitizeAttribs=V;m.za=m.sanitizeWithPolicy=Q;m.Ba=m.unescapeEntities=x;return m}($),la=ha.sanitize;"undefined"!==typeof window&&(window.html=ha,window.html_sanitize=la);})(); \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/arrows.xml b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/arrows.xml new file mode 100644 index 00000000..5e39b3c1 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/arrows.xml @@ -0,0 +1,849 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/basic.xml b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/basic.xml new file mode 100644 index 00000000..fe866c85 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/basic.xml @@ -0,0 +1,895 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/bpmn.xml b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/bpmn.xml new file mode 100644 index 00000000..af8918d9 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/bpmn.xml @@ -0,0 +1,1162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Credit_Card_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Credit_Card_128x128.png new file mode 100644 index 00000000..d280580c Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Credit_Card_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Database_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Database_128x128.png new file mode 100644 index 00000000..4add7147 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Database_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Doctor1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Doctor1_128x128.png new file mode 100644 index 00000000..d5a4bb7b Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Doctor1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Earth_globe_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Earth_globe_128x128.png new file mode 100644 index 00000000..438d4146 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Earth_globe_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Email_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Email_128x128.png new file mode 100644 index 00000000..af81e0ba Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Email_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Empty_Folder_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Empty_Folder_128x128.png new file mode 100644 index 00000000..85d5b0f7 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Empty_Folder_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Firewall_02_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Firewall_02_128x128.png new file mode 100644 index 00000000..0cf90fdd Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Firewall_02_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Full_Folder_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Full_Folder_128x128.png new file mode 100644 index 00000000..6a6496ea Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Full_Folder_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Gear_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Gear_128x128.png new file mode 100644 index 00000000..a44e376c Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Gear_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Graph_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Graph_128x128.png new file mode 100644 index 00000000..7f20b4e4 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Graph_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Laptop_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Laptop_128x128.png new file mode 100644 index 00000000..ab7ea659 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Laptop_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Lock_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Lock_128x128.png new file mode 100644 index 00000000..d815f0eb Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Lock_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/MacBook_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/MacBook_128x128.png new file mode 100644 index 00000000..0f6802cc Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/MacBook_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Monitor_Tower_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Monitor_Tower_128x128.png new file mode 100644 index 00000000..223b27ad Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Monitor_Tower_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Piggy_Bank_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Piggy_Bank_128x128.png new file mode 100644 index 00000000..89f51c15 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Piggy_Bank_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Pilot1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Pilot1_128x128.png new file mode 100644 index 00000000..5b7dea03 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Pilot1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Printer_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Printer_128x128.png new file mode 100644 index 00000000..3cee0427 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Printer_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Router_Icon_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Router_Icon_128x128.png new file mode 100644 index 00000000..49345e54 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Router_Icon_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Safe_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Safe_128x128.png new file mode 100644 index 00000000..51115e8e Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Safe_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Security1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Security1_128x128.png new file mode 100644 index 00000000..9cf20a29 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Security1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Server_Tower_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Server_Tower_128x128.png new file mode 100644 index 00000000..a0ec7b16 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Server_Tower_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Shopping_Cart_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Shopping_Cart_128x128.png new file mode 100644 index 00000000..ec1e1844 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Shopping_Cart_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Software_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Software_128x128.png new file mode 100644 index 00000000..a3deb93e Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Software_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Soldier1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Soldier1_128x128.png new file mode 100644 index 00000000..27f4b4bd Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Soldier1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit1_128x128.png new file mode 100644 index 00000000..f6c0a8de Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit2_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit2_128x128.png new file mode 100644 index 00000000..a8b714de Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit2_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit3_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit3_128x128.png new file mode 100644 index 00000000..68a1f1c6 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Suit3_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Tech1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Tech1_128x128.png new file mode 100644 index 00000000..95e41101 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Tech1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Telesales1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Telesales1_128x128.png new file mode 100644 index 00000000..6ef7b8a7 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Telesales1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virtual_Machine_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virtual_Machine_128x128.png new file mode 100644 index 00000000..75895ea6 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virtual_Machine_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virus_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virus_128x128.png new file mode 100644 index 00000000..f0dfbcb7 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Virus_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Wireless_Router_N_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Wireless_Router_N_128x128.png new file mode 100644 index 00000000..9bace317 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Wireless_Router_N_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Worker1_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Worker1_128x128.png new file mode 100644 index 00000000..c69c9bd4 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Worker1_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Workstation_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Workstation_128x128.png new file mode 100644 index 00000000..9aeef9e1 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/Workstation_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iMac_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iMac_128x128.png new file mode 100644 index 00000000..ccdb5141 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iMac_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iPad_128x128.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iPad_128x128.png new file mode 100644 index 00000000..88dc8070 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/clipart/iPad_128x128.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/flowchart.xml b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/flowchart.xml new file mode 100644 index 00000000..e6ec9d24 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/stencils/flowchart.xml @@ -0,0 +1,920 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/default.xml b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/default.xml new file mode 100644 index 00000000..6c124e8a --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/default.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/down.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/down.gif new file mode 100644 index 00000000..f6c47ca1 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/down.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/grapheditor.css b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/grapheditor.css new file mode 100644 index 00000000..400d7489 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/grapheditor.css @@ -0,0 +1,730 @@ +.geEditor { + font-family:Helvetica Neue,Helvetica,Arial Unicode MS,Arial; + font-size:10pt; + border:none; + margin:0px; +} +.geEditor input[type=text]::-ms-clear { + display: none; +} +.geMenubarContainer .geItem, .geToolbar .geButton, .geToolbar .geLabel, .geSidebarContainer .geTitle { + cursor:pointer !important; +} +.geBackgroundPage { + -webkit-box-shadow:0px 0px 3px 0px #d9d9d9; + -moz-box-shadow:0px 0px 3px 0px #d9d9d9; + box-shadow:0px 0px 3px 0px #d9d9d9; +} +.geSidebarContainer a, .geMenubarContainer a, .geToolbar a { + color:#000000; + text-decoration:none; +} +.geMenubarContainer, .geToolbarContainer, .geDiagramContainer, .geSidebarContainer, .geFooterContainer, .geHsplit, .geVsplit { + overflow:hidden; + position:absolute; + cursor:default; +} +.geFormatContainer { + background-color: whiteSmoke !important; + overflow-x: hidden !important; + overflow-y: auto !important; + font-size:12px; +} +.geDiagramContainer { + background-color:#ffffff; + border: 1px solid #e5e5e5; + outline:none; +} +.geMenubar, .geToolbar { + white-space:nowrap; + display:block; + width:100%; +} +.geMenubarContainer .geItem, .geToolbar .geButton, .geToolbar .geLabel, .geSidebar, .geSidebarContainer .geTitle, .geSidebar .geItem, .mxPopupMenuItem { + -webkit-transition: all 0.1s ease-in-out; + -moz-transition: all 0.1s ease-in-out; + -o-transition: all 0.1s ease-in-out; + -ms-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +} +.geHint { + background-color: #ffffff; + border: 1px solid gray; + padding: 4px 16px 4px 16px; + border-radius:3px; + -webkit-box-shadow: 1px 1px 2px 0px #ddd; + -moz-box-shadow: 1px 1px 2px 0px #ddd; + box-shadow: 1px 1px 2px 0px #ddd; + opacity:0.8; + filter:alpha(opacity=80); +} +.geStatusAlert { + white-space:nowrap; + margin-top:-5px; + font-size:12px; + padding:4px 6px 4px 6px; + background-color:#f2dede; + border:1px solid #ebccd1; + color:#a94442 !important; + border-radius:3px; +} +.geStatusAlert:hover { + background-color:#f1d8d8; + border-color:#d6b2b8; +} +.geStatusMessage { + white-space:nowrap; + margin-top:-5px; + padding:4px 6px 4px 6px; + font-size:12px; + background-image: -webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%); + background-image: -o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%); + background-image: -webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc)); + background-image: linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); + background-repeat: repeat-x; + border:1px solid #b2dba1; + border-radius:3px; + color:#3c763d !important; +} +.geStatusMessage:hover { + background:#c8e5bc; + border-color:#b2dba1; +} +.geAlert { + position:absolute; + white-space:nowrap; + padding:14px; + background-color:#f2dede; + border:1px solid #ebccd1; + color:#a94442; + border-radius:3px; + -webkit-box-shadow: 2px 2px 3px 0px #ddd; + -moz-box-shadow: 2px 2px 3px 0px #ddd; + box-shadow: 2px 2px 3px 0px #ddd; +} +.geBtn, .mxWindow .geBtn { + background-image: none; + background-color: #f5f5f5; + border-radius: 2px; + border: 1px solid #d8d8d8; + color: #333; + cursor: default; + font-size: 11px; + font-weight: bold; + height: 29px; + line-height: 27px; + margin: 0 0 0 8px; + min-width: 72px; + outline: 0; + padding: 0 8px; + cursor: pointer; +} +.geBtn:hover, .geBtn:focus { + -webkit-box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + -moz-box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + border: 1px solid #c6c6c6; + background-color: #f8f8f8; + background-image: linear-gradient(#f8f8f8 0px,#f1f1f1 100%); + color: #111; +} +.geBtn:disabled { + opacity: .5; +} +.geBtnUp { + background-image: url(data:image/gif;base64,R0lGODlhCgAGAJECAGZmZtXV1f///wAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0QzM3ODJERjg4NUQxMUU0OTFEQ0E2MzRGQzcwNUY3NCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0QzM3ODJFMDg4NUQxMUU0OTFEQ0E2MzRGQzcwNUY3NCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRDMzc4MkREODg1RDExRTQ5MURDQTYzNEZDNzA1Rjc0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRDMzc4MkRFODg1RDExRTQ5MURDQTYzNEZDNzA1Rjc0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAgAsAAAAAAoABgAAAg6UjwiQBhGYglCKhXFLBQA7); + _background-image: url(up.gif); + background-position: center center; + background-repeat: no-repeat; +} +.geBtnUp:active { + background-color: #4d90fe; + background-image: linear-gradient(#4d90fe 0px,#357ae8 100%); +} +.geBtnDown { + background-image: url(data:image/gif;base64,R0lGODlhCgAGAJECANXV1WZmZv///wAAACH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0QzM3ODJEQjg4NUQxMUU0OTFEQ0E2MzRGQzcwNUY3NCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0QzM3ODJEQzg4NUQxMUU0OTFEQ0E2MzRGQzcwNUY3NCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjRDMzc4MkQ5ODg1RDExRTQ5MURDQTYzNEZDNzA1Rjc0IiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjRDMzc4MkRBODg1RDExRTQ5MURDQTYzNEZDNzA1Rjc0Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAgAsAAAAAAoABgAAAg6UjxLLewEiCAnOZBzeBQA7); + _background-image: url(down.gif); + background-position: center center; + background-repeat: no-repeat; +} +.geBtnDown:active { + background-color: #4d90fe; + background-image: linear-gradient(#4d90fe 0px,#357ae8 100%); +} +.geColorBtn { + background-color: #f5f5f5; + background-image: linear-gradient(#f5f5f5 0px,#e1e1e1 100%); + border-radius: 4px; + border: 1px solid rgba(0,0,0,0.5); + color: #333; + cursor: default; + margin: 0px; + outline: 0; + padding: 0px; + cursor: pointer; +} +.geColorBtn:hover { + -webkit-box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + -moz-box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + box-shadow: 0px 1px 1px rgba(0,0,0,0.1); + border: 1px solid rgba(0,0,0,0.7); +} +.geColorBtn:active { + background-color: #4d90fe; + background-image: linear-gradient(#4d90fe 0px,#357ae8 100%); + border: 1px solid #2f5bb7; + color: #fff; +} +.geColorBtn:disabled { + opacity: .5; +} +.gePrimaryBtn, .mxWindow .gePrimaryBtn { + background-color: #4d90fe; + background-image: linear-gradient(#4d90fe 0px,#4787ed 100%); + border: 1px solid #3079ed; + color: #fff; +} +.gePrimaryBtn:hover, .gePrimaryBtn:focus { + background-color: #357ae8; + background-image: linear-gradient(#4d90fe 0px,#357ae8 100%); + border: 1px solid #2f5bb7; + color: #fff; +} +.gePrimaryBtn:disabled { + opacity: .5; +} +.geAlertLink { + color:#843534; + font-weight:700; + text-decoration:none; +} +.geMenubarContainer { + background-color:#ffffff; +} +.geMenubar { + padding:0px 2px 0px 2px; + vertical-align:middle; +} +.geMenubarContainer .geItem, .geToolbar .geItem { + padding:6px 8px 6px 8px; + cursor:default; +} +.geMenubarContainer .geItem:hover { + background:#eeeeee; +} +.mxDisabled:hover { + background:inherit !important; +} +.geMenubar a.geStatus { + color:#b3b3b3; + padding-left:6px; + display:inline-block; + cursor:default !important; +} +.geMenubar a.geStatus:hover { + background:transparent; +} +.geMenubarMenu { + border:1px solid #d5d5d5 !important; +} +.geToolbarContainer { + background:whiteSmoke; + border-bottom:1px solid #e0e0e0; +} +.geSidebarContainer .geToolbarContainer { + background:transparent; + border-bottom:none; +} +.geSidebarContainer button { + text-overflow:ellipsis; + overflow:hidden; +} +.geToolbar { + padding:1px 0px 0px 6px; + border-top:1px solid #e0e0e0; + -webkit-box-shadow: inset 0 1px 0 0 #fff; + -moz-box-shadow: inset 0 1px 0 0 #fff; + box-shadow: inset 0 1px 0 0 #fff; +} +.geToolbarContainer .geSeparator { + float:left; + width:1px; + height:34px; + background:#e5e5e5; + margin-left:6px; + margin-right:6px; + margin-top:-2px; +} +.geToolbarContainer .geButton { + float:left; + width:20px; + height:20px; + padding:0px 2px 4px 2px; + margin:2px; + border:1px solid transparent; + cursor:pointer; + opacity:0.6; + filter:alpha(opacity=60); +} +.geToolbarContainer .geButton:hover { + border:1px solid gray; + border-radius:2px; + opacity:1; + filter:none !important; +} +.geToolbarContainer .geButton:active { + border:1px solid black; +} +div.mxWindow .geButton { + margin: -1px 2px 2px 2px; + padding: 1px 2px 2px 1px; +} +.geToolbarContainer .geLabel { + float:left; + margin:2px; + cursor:pointer; + padding:3px 5px 3px 5px; + border:1px solid transparent; + opacity:0.6; + filter:alpha(opacity=60); +} +.geToolbarContainer .geLabel:hover { + border:1px solid gray; + border-radius:2px; + opacity:0.9; + filter:alpha(opacity=90) !important; +} +.geToolbarContainer .geLabel:active { + border:1px solid black; + opacity:1; + filter:none !important; +} +.geToolbarContainer .mxDisabled:hover { + border:1px solid transparent !important; + opacity:0.2 !important; + filter:alpha(opacity=20) !important; +} +.geToolbarMenu { + border:3px solid #e0e0e0 !important; + -webkit-box-shadow:none !important; + -moz-box-shadow:none !important; + box-shadow:none !important; + filter:none !important; +} +.geDiagramBackdrop { + background-color: #ebebeb; +} +.geSidebarContainer { + background:#ffffff; + overflow:hidden; + position:absolute; + border-top:1px solid #e5e5e5; + overflow:auto; +} +.geSidebar { + background:whiteSmoke; + border-bottom:1px solid #e5e5e5; + padding:5px; + _padding:1px; + padding-bottom:12px; + overflow:hidden; +} +.geSidebarContainer .geTitle { + display:block; + font-size:9pt; + border-bottom:1px solid #e5e5e5; + font-weight:normal; + padding:6px 0px 6px 14px; + margin:0px; + cursor:default; + background:#eeeeee; + white-space:nowrap; + overflow:hidden; + text-overflow:ellipsis; + line-height:1.4em; +} +.geSidebarContainer .geTitle:hover { + background:#e5e5e5; +} +.geTitle img { + opacity:0.5; + _filter:alpha(opacity=50); +} +.geTitle img:hover { + opacity:1; + _filter:alpha(opacity=100); +} +.geTitle .geButton { + border:1px solid transparent; + padding:3px; + border-radius:2px; +} +.geTitle .geButton:hover { + border:1px solid gray; +} +.geSidebar .geItem { + display:inline-block; + background-repeat:no-repeat; + background-position:50% 50%; + border:1px solid transparent; + border-radius:2px; + cursor: move; +} +.geSidebar .geItem:hover { + border:1px solid gray !important; +} +.geItem { + vertical-align: top; + display: inline-block; +} +.geSidebarTooltip { + position:absolute; + background:white; + overflow:hidden; + border:1px solid gray; + border-radius:8px; + -webkit-box-shadow:0px 0px 2px 2px #d5d5d5; + -moz-box-shadow:0px 0px 2px 2px #d5d5d5; + box-shadow:0px 0px 2px 2px #d5d5d5; + _filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=2, OffY=2, Color='#d5d5d5', Positive='true'); +} +.geFooterContainer { + background:#e5e5e5; + border-top:1px solid #c0c0c0; +} +.geFooterContainer a { + display:inline-block; + box-sizing:border-box; + width:100%; + white-space:nowrap; + font-size:14px; + color:#235695; + font-weight:bold; + text-decoration:none; +} +.geFooterContainer table { + border-collapse:collapse; + margin:0 auto; +} +.geFooterContainer td { + border-left:1px solid #c0c0c0; + border-right:1px solid #c0c0c0; +} +.geFooterContainer td:hover { + background-color: #b3b3b3; +} +.geHsplit { + cursor:col-resize; + background-color:#e5e5e5; + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAHBAMAAADdS/HjAAAAGFBMVEUzMzP///9tbW1QUFCKiopBQUF8fHxfX1/IXlmXAAAAHUlEQVQImWMQEGAQFWUQFmYQF2cQEmIQE2MQEQEACy4BF67hpEwAAAAASUVORK5CYII=); + _background-image:url('thumb_vertical.png'); + background-repeat:no-repeat; + background-position:center center; +} +.geVsplit { + font-size:1pt; + cursor:row-resize; + background-color:#e5e5e5; + background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAEBAMAAACw6DhOAAAAGFBMVEUzMzP///9tbW1QUFCKiopBQUF8fHxfX1/IXlmXAAAAFElEQVQImWNgNVdzYBAUFBRggLMAEzYBy29kEPgAAAAASUVORK5CYII=); + _background-image:url('thumb_horz.png'); + background-repeat:no-repeat; + background-position:center center; +} +.geHsplit:hover, .geVsplit:hover { + background-color:#d5d5d5; +} +.geDialog { + position:absolute; + background:white; + line-height:1em; + overflow:hidden; + padding:30px; + border:1px solid #acacac; + -webkit-box-shadow:0px 0px 2px 2px #d5d5d5; + -moz-box-shadow:0px 0px 2px 2px #d5d5d5; + box-shadow:0px 0px 2px 2px #d5d5d5; + _filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=2, OffY=2, Color='#d5d5d5', Positive='true'); + z-index: 2; +} +.geDialogClose { + position:absolute; + width:9px; + height:9px; + opacity:0.5; + cursor:pointer; + _filter:alpha(opacity=50); +} +.geDialogClose:hover { + opacity:1; +} +.geDialogTitle { + box-sizing:border-box; + white-space:nowrap; + background:rgb(229, 229, 229); + border-bottom:1px solid rgb(192, 192, 192); + font-size:15px; + font-weight:bold; + text-align:center; + color:rgb(35, 86, 149); +} +.geDialogFooter { + background:whiteSmoke; + white-space:nowrap; + text-align:right; + box-sizing:border-box; + border-top:1px solid #e5e5e5; + color:darkGray; +} +.geSprite { + background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAABeQCAMAAAByzXKPAAAA1VBMVEUAAAAzMzIzMzIzMzE1NTUzMzIzMzIzMzEzMzIyMjIzMzIzMzIzMzIzMzE6OjoyMjIzMzIzMzIzMzEzMzI0NDA0NDIwMDAyMjEzMzIzMzEzMzE0NDAzMzMzMzIyMjIzMzI0NDE0NDQyMjIzMzIzMzIzMzIzMzM5OTkAAAC1tbX///9mZmYwMDAQEBAmJiZTiPJ7e3v/Z2RwcHAgICB+fn5hYWFzc3NAQECoqKiampqMjIxTU1NGRkY3NzcbGxsNDQ3w8PBQUFDg4ODAwMCwsLCgoKBPT09W+xp5AAAAKHRSTlMAh4DDEd187+mljnhvVge5nmhMQi4hA8uxk2IlHOOYc102M/jW088J/2liTQAACt5JREFUeNrs3O1um0AQheG5nIPCoIq1Q7+r/Mn9X1K9xtEAM9RLQmODzyO1cV+NtzjShhjHkRkAgn9DMCaYnRXIGOQdtWmk1zRWm0vNN6az+Y+v2fXZ5iTX/kZ4ZGvUNT6/RJuCM/VVRKFB7YBwtnY1k3h2wboaHy8RFQC2VGuc1D730WWLS2GIZ27aM4yUT+9tXaLNw9m0mI9uokepRLeC+ZyCfFCoqwckqV3WBHFZaxHLFrNxVpgUVRWjg/jli2WbfH4WyxYnWcVV0Rz7FYj26X9ezQcuE3VYkXzFSYpm9eq6pqSiuNYAaj+r0GiFxKv5tEOqxVfV0utrCkZF/HCXTuOdGz2IHKbDR5wdJ6PdeRmIsfviED0C5ZV7ojf487v59fPHsT18//a1Yn1HJdoSjIUXBG5XgaBejhMYVmT9B1dRUONj4KtyRHNmNkULDYeD2CGVjiZ0paOK1jVuYKIpjJTO7m9dovvEn3bz/DH4440f2+d9fvlVh+4LLuK6cM8GVcKKkmq3ZqqBfbhaef4n+pijBudNZH3H+OW3No+rbTmVXjfciPbs/GVQ7QU6tWqUW5lonefHfn5f6xI9DAxdqQU3n55uU821mj2ZuUq0SYo2ivA5tThxk3rK6u+urYtZiqKuGiWMEkbp4/OFLITIvyrfekt35WGvjvP6OlERwP6+VU39z+ansp+0SThP8jk+0SeoAehRjObmdha0f2YwfucMss7uaPVF3rThN/EH6fH3YhEt3bD1zMlwGtu8/8a5zrHJuR6NyrkKMK5N5qqf9ev6Y4hfGfSPrc7ZWZqPQrQPa+x5Cfe8FO55m3Xrcs8T3TFUYQ0zqj5jTPoMmagQz4brFh8D3x5L9OngyRy+IEa0JgBRlaBWAIJYyVYjYDF47PwNV/TwAIRfB+BiFcyeolchija6+P7xd//IveI+JlpwnrdZP7xexdD6/4N/PDzP06NZdc8vP4H7WKFXLbqjQcU9T7QZvFhHdN/+sndmy6lCQRQ9KOA8Ro1TxjttYuUlX5BK5f+/6YJIWjlNFREkpLJXGQuWO01e2mPaU4pA17qH7iEeKfRsrrqh4/t0hJQPEJSokULPFpJse0Iu0PNQNVSNnOu8ZHPWZc8TUhkBgECRikZMrp4Xq9W1NPubkIIUm4hnrtyikSIjq+jck3bOBQkpnSBrkU97ALl73pJqXfFc5AlJqN3cXvoTEKIzJcu5PSEFqHiGp6ahz+33Z3rWtpzhEfK16DO8XXi3S2vIvfUCHnpWrcsZHiHVAFUG0KQJoEgjGjGRFG1l9bq25gyPkIoANBcEab9DEPf27iCk40VbWa0uP86WkMsTQHPQHBSnJJHCytp1dW9Uz2cBQoo0PEqVes/r2bM0131CLtLzUCVUidw9n6uuaPY8IdnUYMet2BTccUtIfShnz60mBe65JaTunL/tVqTAbbeE1ImCc3vl16McIEiWc3tCClD5DM9Ak7ZFZCBkZEVzhkfI5/n6Hbdp+wF33BJSH8rZc6vISB/gnltCas/Z225FStdz2y0hZXE19lrt5p177NyR11+OHb/THhzJP86wP2uYrjvz1h92eTseNEzDbB2nd/OY1Py9WNw6/qjnN+fmvnmwnYkxjf1t+mAW7XlsbzaJ3a7DzH1sf3Udp7m/dcOf615sW26SdfvGrCaxbV4l9nEan0X0xqEaRrbvmnlrGFu3PTN3ndUoLOuapW8ODLzudLVomMHA71z/MwmT9mTmN+bOZnS9NcJDs+V53t+WPzQnbNa9/nRoCPl2AKqObKFvltEBoPvcVwNwmavxOy3IDwFAlkWCWPBqhJDC8GtsCPlGYI8ciQyRI+3/CLHHscysXvf0ynzWIOQsPr3wWllkxNQskD+b82/Ihi8qCCm150XpObXnc2RFs+cJqRhAE5AHpI8BOZbH5TQdlXB8JAUEIC4AvkFPSMEl/dQ+v74+2/bl6enFtm/v72+W/c/eHSW3CUNRGNZyjgiZNHE6fW2b6f63VGScCHSvI7CxjfH/tYnTU43CywFiAfnT/On+lunH3274R5G2zbv03rTj9F+z92+U7pqPX52PZjdM35uf6vxs3ofp799Kulf2B8CEc2JVjvJm6OIT5CO9PekvD/8T767XgTc2z1umnEdggyT5eX2s+k9yGpvH1kqvI523IVfSAzdlW2gbu3zn5+6j/JFcfIft0lBOi4/J6cbmBTZJTdPo9fV1/3pamqTUFCalOVkunVdNTU5bSa2Nn7ULjl0A7o5/aGt6Z6TKUpVC7/VLSrWzqTo7b+yzO+9i28shHtugl5cXXS9NLnyYHVZ+Nz79UG7y4in7Aqza9po+tBsXP1B8wCW3m01yVqq9G3w3q/X+/1lpZ5WEbKdOTnNsxiYtd+ngjl28Fw+zhwGwLA0/mDeIS46AxWnO2MVnUFD627+sasuAxyJpTsp3A+6WgnplGpILpL1JR21lZt5k2ZSrFPE41AteEy+Q9qrn58qW77lNM877sXXq+fcGLp/2giETZO5tTYumHObxKDQ0aezhxb2feNE0MWlvqZS1SzwsJZdslu3x9fYae8HFXYeAKcIVejxwzgwc0YE6jTzuWOAuqOS+GTY3rc+rmDxAKn7VEPAt+amm/7Yu8ev0gfnkHckljT8nSoaf/RkeNgUejaKburGiYt696FNIcXrt/3yJh8Qba+advg0BwJCk66Qamp3WvxtX1gMzSDZya+BU8y2OR+ogyk7w5h+Nox+/6S04pNunwKqpt9Db9yemA2GokjqThGR+ms1JeYMe92hu5y2Zbs5O5be7mkru2Hlpgc5j434M1NPiq0qqgXoaDkwqTU21V0s7dgY/TfLX9XMT1udx56RJqTqf5aqlR3vVqtOWaYpbp4NtW2tmPaXbwAZI0U1zroI/tiRW9oBVOtL5QT6uu2TH0nlgnXRSqsLkGXg+JnBTCjopVeGceXk2FnAtColkUynGMn1SrHXejq3Py4U5wHXZvtluJtXO27H+vFyMB9xElFIxNQ6fUmFj9Xm6Sr67SrA6b94Gfp4HLsb2rSM53VSMpseK1c7bsXbechs4zgO3EiUvs6kK7tgsUmlglaLkZiZVStzOBzoPrJHOS1VgVR5YuTPXz1VgXR5YM/+u9xglkyZRT2WqghlbnVc8CA+4mtxRp5vB7XxW7bw3LwvywI0pr5MN6HNNrUi/fUZO4o6tzxuiuO0GuLzUUdu3GOV3M6raeTu2Nm8aR+eBlVD1BnoVjowt0HBgnY51PrqdT/9yxtJ5YHWkeGKswoxJuMcGuIYoKdowKOQ4Xxov+4Zb/khU8Mf681a3gZ/0gYt1PjGdV1J2/mBS5z/58zrbQOeBE6zpGZgA7smRzgc6D2xKlORlCiUVKrfSslMAVulI56PX+XHv81g6D6yPFM+MVWCJDliLJdfoMhVYowPWYsk1ukwF1uiAu1R/Hh5v2wNb8r+9O0pBGAaiKLqdm/1vUCiFSCdhTIjWhntAwefY5sMxRW1ToJnZ89KjQTnuGzGUWB1DLkJtul0ofocn3SOf5wMu3mu97q30GN2e99he2gKEpj6Dkvwjj4tebb5d8EBAuhuUJJ87f96f4aT/1OlNz7GRfgg+WheCUFsfE16cpEFNx5exIUmHx09zd34AaRdACCDp+TVLSlFv1blzgKR2egx5zx/see0pn+djbR4FIVofz4/UeV67G3+vr3niC+H04Oz/nbwA7lqtm+wByfQAAAAASUVORK5CYII=') no-repeat; + _background:url('sprites.png') no-repeat top left; + width:21px; + height:21px; +} +.geBaseButton { + padding:10px; + border-radius:6px; + border:1px solid #c0c0c0; + cursor:pointer; + background-color:#ececec; + background-image:linear-gradient(#ececec 0%, #fcfcfc 100%); +} +.geBaseButton:hover { + background:#ececec; +} +.geBigButton { + color:#ffffff; + border: none; + padding:10px; + font-size:14pt; + white-space: nowrap; + border-radius:6px; + text-shadow: rgb(41, 89, 137) 0px 1px 0px; + background-color:#428bca; + background-image:linear-gradient(rgb(70, 135, 206) 0px, rgb(48, 104, 162) 100%); + -webkit-box-shadow: rgba(255, 255, 255, 0.0980392) 0px 1px 0px 0px inset, rgba(0, 0, 0, 0.2) 0px 1px 1px 0px; + -moz-box-shadow: rgba(255, 255, 255, 0.0980392) 0px 1px 0px 0px inset, rgba(0, 0, 0, 0.2) 0px 1px 1px 0px; + box-shadow: rgba(255, 255, 255, 0.0980392) 0px 1px 0px 0px inset, rgba(0, 0, 0, 0.2) 0px 1px 1px 0px; +} +.geBigButton:hover { + background-color:#2d6ca2; + background-image: linear-gradient(rgb(90, 148, 211) 0px, rgb(54, 115, 181) 100%); +} +.geBigButton:active { + background-color: rgb(54, 115, 181); + background-image: none; +} +@media print { + div.geNoPrint { display: none !important; } +} +.geSprite-actualsize { background-position: 0 0; } +.geSprite-bold { background-position: 0 -46px; } +.geSprite-bottom { background-position: 0 -92px; } +.geSprite-center { background-position: 0 -138px; } +.geSprite-delete { background-position: 0 -184px; } +.geSprite-fillcolor { background-position: 0 -229px; } +.geSprite-fit { background-position: 0 -277px; } +.geSprite-fontcolor { background-position: 0 -322px; } +.geSprite-gradientcolor { background-position: 0 -368px; } +.geSprite-image { background-position: 0 -414px; } +.geSprite-italic { background-position: 0 -460px; } +.geSprite-left { background-position: 0 -505px; } +.geSprite-middle { background-position: 0 -552px; } +.geSprite-print { background-position: 0 -598px; } +.geSprite-redo { background-position: 0 -644px; } +.geSprite-right { background-position: 0 -689px; } +.geSprite-shadow { background-position: 0 -735px; } +.geSprite-strokecolor { background-position: 0 -782px; } +.geSprite-top { background-position: 0 -828px; } +.geSprite-underline { background-position: 0 -874px; } +.geSprite-undo { background-position: 0 -920px; } +.geSprite-zoomin { background-position: 0 -966px; } +.geSprite-zoomout { background-position: 0 -1012px; } +.geSprite-arrow { background-position: 0 -1059px; } +.geSprite-linkedge { background-position: 0 -1105px; } +.geSprite-straight { background-position: 0 -1150px; } +.geSprite-entity { background-position: 0 -1196px; } +.geSprite-orthogonal { background-position: 0 -1242px; } +.geSprite-curved { background-position: 0 -1288px; } +.geSprite-noarrow { background-position: 0 -1334px; } +.geSprite-endclassic { background-position: 0 -1380px; } +.geSprite-endopen { background-position: 0 -1426px; } +.geSprite-endblock { background-position: 0 -1472px; } +.geSprite-endoval { background-position: 0 -1518px; } +.geSprite-enddiamond { background-position: 0 -1564px; } +.geSprite-endthindiamond { background-position: 0 -1610px; } +.geSprite-endclassictrans { background-position: 0 -1656px; } +.geSprite-endblocktrans { background-position: 0 -1702px; } +.geSprite-endovaltrans { background-position: 0 -1748px; } +.geSprite-enddiamondtrans { background-position: 0 -1794px; } +.geSprite-endthindiamondtrans { background-position: 0 -1840px; } +.geSprite-startclassic { background-position: 0 -1886px; } +.geSprite-startopen { background-position: 0 -1932px; } +.geSprite-startblock { background-position: 0 -1978px; } +.geSprite-startoval { background-position: 0 -2024px; } +.geSprite-startdiamond { background-position: 0 -2070px; } +.geSprite-startthindiamond { background-position: 0 -2116px; } +.geSprite-startclassictrans { background-position: 0 -2162px; } +.geSprite-startblocktrans { background-position: 0 -2208px; } +.geSprite-startovaltrans { background-position: 0 -2254px; } +.geSprite-startdiamondtrans { background-position: 0 -2300px; } +.geSprite-startthindiamondtrans { background-position: 0 -2346px; } +.geSprite-globe { background-position: 0 -2392px; } +.geSprite-orderedlist { background-position: 0 -2438px; } +.geSprite-unorderedlist { background-position: 0 -2484px; } +.geSprite-horizontalrule { background-position: 0 -2530px; } +.geSprite-link { background-position: 0 -2576px; } +.geSprite-indent { background-position: 0 -2622px; } +.geSprite-outdent { background-position: 0 -2668px; } +.geSprite-code { background-position: 0 -2714px; } +.geSprite-fontbackground { background-position: 0 -2760px; } +.geSprite-removeformat { background-position: 0 -2806px; } +.geSprite-superscript { background-position: 0 -2852px; } +.geSprite-subscript { background-position: 0 -2898px; } +.geSprite-table { background-position: 0 -2944px; } +.geSprite-deletecolumn { background-position: 0 -2990px; } +.geSprite-deleterow { background-position: 0 -3036px; } +.geSprite-insertcolumnafter { background-position: 0 -3082px; } +.geSprite-insertcolumnbefore { background-position: 0 -3128px; } +.geSprite-insertrowafter { background-position: 0 -3174px; } +.geSprite-insertrowbefore { background-position: 0 -3220px; } +.geSprite-grid { background-position: 0 -3272px; } +.geSprite-guides { background-position: 0 -3324px; } +.geSprite-dots { background-position: 0 -3370px; } +.geSprite-alignleft { background-position: 0 -3416px; } +.geSprite-alignright { background-position: 0 -3462px; } +.geSprite-aligncenter { background-position: 0 -3508px; } +.geSprite-aligntop { background-position: 0 -3554px; } +.geSprite-alignbottom { background-position: 0 -3600px; } +.geSprite-alignmiddle { background-position: 0 -3646px; } +.geSprite-justifyfull { background-position: 0 -3692px; } +.geSprite-formatpanel { background-position: 0 -3738px; } +.geSprite-connection { background-position: 0 -3784px; } +.geSprite-vertical { background-position: 0 -3830px; } +.geSprite-simplearrow { background-position: 0 -3876px; } +.geSprite-plus { background-position: 0 -3922px; } +.geSprite-rounded { background-position: 0 -3968px; } +.geSprite-toback { background-position: 0 -4014px; } +.geSprite-tofront { background-position: 0 -4060px; } +.geSprite-duplicate { background-position: 0 -4106px; } +.geSprite-insert { background-position: 0 -4152px; } +.geSprite-endblockthin { background-position: 0 -4201px; } +.geSprite-endblockthintrans { background-position: 0 -4247px; } +.geSprite-enderone { background-position: 0 -4293px; } +.geSprite-enderonetoone { background-position: 0 -4339px; } +.geSprite-enderonetomany { background-position: 0 -4385px; } +.geSprite-endermany { background-position: 0 -4431px; } +.geSprite-enderoneopt { background-position: 0 -4477px; } +.geSprite-endermanyopt { background-position: 0 -4523px; } +.geSprite-endclassicthin { background-position: 0 -4938px; } +.geSprite-endclassicthintrans { background-position: 0 -4984px; } +.geSprite-enddash { background-position: 0 -5029px; } +.geSprite-endcircleplus { background-position: 0 -5075px; } +.geSprite-endcircle { background-position: 0 -5121px; } +.geSprite-endasync { background-position: 0 -5167px; } +.geSprite-endasynctrans { background-position: 0 -5213px; } +.geSprite-startblockthin { background-position: 0 -4569px; } +.geSprite-startblockthintrans { background-position: 0 -4615px; } +.geSprite-starterone { background-position: 0 -4661px; } +.geSprite-starteronetoone { background-position: 0 -4707px; } +.geSprite-starteronetomany { background-position: 0 -4753px; } +.geSprite-startermany { background-position: 0 -4799px; } +.geSprite-starteroneopt { background-position: 0 -4845px; } +.geSprite-startermanyopt { background-position: 0 -4891px; } +.geSprite-startclassicthin { background-position: 0 -5259px; } +.geSprite-startclassicthintrans { background-position: 0 -5305px; } +.geSprite-startdash { background-position: 0 -5351px; } +.geSprite-startcircleplus { background-position: 0 -5397px; } +.geSprite-startcircle { background-position: 0 -5443px; } +.geSprite-startasync { background-position: 0 -5489px; } +.geSprite-startasynctrans { background-position: 0 -5535px; } +.geSprite-startcross { background-position: 0 -5581px; } +.geSprite-startopenthin { background-position: 0 -5627px; } +.geSprite-startopenasync { background-position: 0 -5673px; } +.geSprite-endcross { background-position: 0 -5719px; } +.geSprite-endopenthin { background-position: 0 -5765px; } +.geSprite-endopenasync { background-position: 0 -5811px; } +.geSprite-verticalelbow { background-position: 0 -5857px; } +.geSprite-horizontalelbow { background-position: 0 -5903px; } +.geSprite-horizontalisometric { background-position: 0 -5949px; } +.geSprite-verticalisometric { background-position: 0 -5995px; } +html div.mxRubberband { + border-color:#0000DD; + background:#99ccff; +} +td.mxPopupMenuIcon div { + width:16px; + height:16px; +} +html div.mxPopupMenu { + -webkit-box-shadow:2px 2px 3px #d5d5d5; + -moz-box-shadow:2px 2px 3px #d5d5d5; + box-shadow:2px 2px 3px #d5d5d5; + _filter:progid:DXImageTransform.Microsoft.DropShadow(OffX=2, OffY=2, Color='#d0d0d0', Positive='true'); + background:white; + position:absolute; + border:3px solid #e7e7e7; + padding:3px; +} +html table.mxPopupMenu { + border-collapse:collapse; + margin:0px; +} +html td.mxPopupMenuItem { + padding:7px 30px 7px 30px; + font-family:Helvetica Neue,Helvetica,Arial Unicode MS,Arial; + font-size:10pt; +} +html td.mxPopupMenuIcon { + background-color:white; + padding:0px; +} +td.mxPopupMenuIcon .geIcon { + padding:2px; + padding-bottom:4px; + margin:2px; + border:1px solid transparent; + opacity:0.5; + _width:26px; + _height:26px; +} +td.mxPopupMenuIcon .geIcon:hover { + border:1px solid gray; + border-radius:2px; + opacity:1; +} +html tr.mxPopupMenuItemHover { + background-color: #eeeeee; + color: black; +} +table.mxPopupMenu hr { + color:#cccccc; + background-color:#cccccc; + border:none; + height:1px; +} +table.mxPopupMenu tr { + font-size:4pt; +} +html td.mxWindowTitle { + font-family:Helvetica Neue,Helvetica,Arial Unicode MS,Arial; + text-align:left; + font-size:12px; + color:rgb(112, 112, 112); + padding:4px; +} + +/* + * begin add by wsp + */ +.geSprite-save{ + background: url('../../graph-editor-ext/images/save.png') no-repeat; +} + +.geSprite-editdiagram{ + background: url('../../graph-editor-ext/images/xml.png') no-repeat; +} +/* + * end add by wsp + */ \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/help.css b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/help.css new file mode 100644 index 00000000..20014a83 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/help.css @@ -0,0 +1,4 @@ +body { + font-family:Arial Unicode MS,Arial,Helvetica; + font-size:10pt; +} diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/sprites.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/sprites.png new file mode 100644 index 00000000..23e1b491 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/sprites.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_horz.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_horz.png new file mode 100644 index 00000000..f2896f5c Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_horz.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_vertical.png b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_vertical.png new file mode 100644 index 00000000..e0a22e02 Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/thumb_vertical.png differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/up.gif b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/up.gif new file mode 100644 index 00000000..1665d4ad Binary files /dev/null and b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/styles/up.gif differ diff --git a/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/viewer.html b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/viewer.html new file mode 100644 index 00000000..beb91b98 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/static/tools/graph-editor/viewer.html @@ -0,0 +1,71 @@ + + + + + Grapheditor viewer + + + + + + + + + + Input: +
+ +
+ +
+ + + diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Actions.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Actions.js new file mode 100644 index 00000000..3ceb90b5 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Actions.js @@ -0,0 +1,1416 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs the actions object for the given UI. + */ +function Actions(editorUi) +{ + this.editorUi = editorUi; + this.actions = new Object(); + this.init(); +}; + +/** + * Adds the default actions. + */ +Actions.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var isGraphEnabled = function() + { + return Action.prototype.isEnabled.apply(this, arguments) && graph.isEnabled(); + }; + + // File actions + this.addAction('new...', function() { graph.openLink(ui.getUrl()); }); + this.addAction('open...', function() + { + window.openNew = true; + window.openKey = 'open'; + + ui.openFile(); + }); + this.addAction('import...', function() + { + window.openNew = false; + window.openKey = 'import'; + + // Closes dialog after open + window.openFile = new OpenFile(mxUtils.bind(this, function() + { + ui.hideDialog(); + })); + + window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) + { + try + { + var doc = mxUtils.parseXml(xml); + editor.graph.setSelectionCells(editor.graph.importGraphModel(doc.documentElement)); + } + catch (e) + { + mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); + } + })); + + // Removes openFile if dialog is closed + ui.showDialog(new OpenDialog(this).container, 320, 220, true, true, function() + { + window.openFile = null; + }); + }).isEnabled = isGraphEnabled; + this.addAction('save', function() { ui.saveFile(false); }, null, null, Editor.ctrlKey + '+S').isEnabled = isGraphEnabled; + this.addAction('saveAs...', function() { ui.saveFile(true); }, null, null, Editor.ctrlKey + '+Shift+S').isEnabled = isGraphEnabled; + this.addAction('export...', function() { ui.showDialog(new ExportDialog(ui).container, 300, 230, true, true); }); + this.addAction('editDiagram...', function() + { + var dlg = new EditDiagramDialog(ui); + //begin modify by wsp + //ui.showDialog(dlg.container, 620, 420, true, false); + ui.showDialog(dlg.container, 570, 310, true, false); + //end modify by wsp + dlg.init(); + }); + this.addAction('pageSetup...', function() { ui.showDialog(new PageSetupDialog(ui).container, 320, 220, true, true); }).isEnabled = isGraphEnabled; + this.addAction('print...', function() { ui.showDialog(new PrintDialog(ui).container, 300, 180, true, true); }, null, 'sprite-print', Editor.ctrlKey + '+P'); + this.addAction('preview', function() { mxUtils.show(graph, null, 10, 10); }); + + // Edit actions + this.addAction('undo', function() { ui.undo(); }, null, 'sprite-undo', Editor.ctrlKey + '+Z'); + this.addAction('redo', function() { ui.redo(); }, null, 'sprite-redo', (!mxClient.IS_WIN) ? Editor.ctrlKey + '+Shift+Z' : Editor.ctrlKey + '+Y'); + this.addAction('cut', function() { mxClipboard.cut(graph); }, null, 'sprite-cut', Editor.ctrlKey + '+X'); + this.addAction('copy', function() { mxClipboard.copy(graph); }, null, 'sprite-copy', Editor.ctrlKey + '+C'); + this.addAction('paste', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + mxClipboard.paste(graph); + } + }, false, 'sprite-paste', Editor.ctrlKey + '+V'); + this.addAction('pasteHere', function(evt) + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.getModel().beginUpdate(); + try + { + var cells = mxClipboard.paste(graph); + + if (cells != null) + { + var includeEdges = true; + + for (var i = 0; i < cells.length && includeEdges; i++) + { + includeEdges = includeEdges && graph.model.isEdge(cells[i]); + } + + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + var bb = null; + + if (cells.length == 1 && includeEdges) + { + var geo = graph.getCellGeometry(cells[0]); + + if (geo != null) + { + bb = geo.getTerminalPoint(true); + } + } + + bb = (bb != null) ? bb : graph.getBoundingBoxFromGeometry(cells, includeEdges); + + if (bb != null) + { + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + graph.cellsMoved(cells, x - bb.x, y - bb.y); + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + + this.addAction('copySize', function(evt) + { + var cell = graph.getSelectionCell(); + + if (graph.isEnabled() && cell != null && graph.getModel().isVertex(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + ui.copiedSize = new mxRectangle(geo.x, geo.y, geo.width, geo.height); + } + } + }, null, null, 'Alt+Shit+X'); + + this.addAction('pasteSize', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty() && ui.copiedSize != null) + { + graph.getModel().beginUpdate(); + + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + geo.width = ui.copiedSize.width; + geo.height = ui.copiedSize.height; + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shit+V'); + + + function deleteCells(includeEdges) + { + // Cancels interactive operations + graph.escape(); + var cells = graph.getDeletableCells(graph.getSelectionCells()); + + if (cells != null && cells.length > 0) + { + var parents = graph.model.getParents(cells); + graph.removeCells(cells, includeEdges); + + // Selects parents for easier editing of groups + if (parents != null) + { + var select = []; + + for (var i = 0; i < parents.length; i++) + { + if (graph.model.contains(parents[i]) && + (graph.model.isVertex(parents[i]) || + graph.model.isEdge(parents[i]))) + { + select.push(parents[i]); + } + } + + graph.setSelectionCells(select); + } + } + }; + + this.addAction('delete', function(evt) + { + deleteCells(evt != null && mxEvent.isShiftDown(evt)); + }, null, null, 'Delete'); + this.addAction('deleteAll', function() + { + deleteCells(true); + }, null, null, Editor.ctrlKey + '+Delete'); + this.addAction('duplicate', function() + { + graph.setSelectionCells(graph.duplicateCells()); + }, null, null, Editor.ctrlKey + '+D'); + this.put('turn', new Action(mxResources.get('turn') + ' / ' + mxResources.get('reverse'), function() + { + graph.turnShapes(graph.getSelectionCells()); + }, null, null, Editor.ctrlKey + '+R')); + this.addAction('selectVertices', function() { graph.selectVertices(); }, null, null, Editor.ctrlKey + '+Shift+I'); + this.addAction('selectEdges', function() { graph.selectEdges(); }, null, null, Editor.ctrlKey + '+Shift+E'); + this.addAction('selectAll', function() { graph.selectAll(null, true); }, null, null, Editor.ctrlKey + '+A'); + this.addAction('selectNone', function() { graph.clearSelection(); }, null, null, Editor.ctrlKey + '+Shift+A'); + this.addAction('lockUnlock', function() + { + if (!graph.isSelectionEmpty()) + { + graph.getModel().beginUpdate(); + try + { + var defaultValue = graph.isCellMovable(graph.getSelectionCell()) ? 1 : 0; + graph.toggleCellStyles(mxConstants.STYLE_MOVABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_RESIZABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_ROTATABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_DELETABLE, defaultValue); + graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, defaultValue); + graph.toggleCellStyles('connectable', defaultValue); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+L'); + + // Navigation actions + this.addAction('home', function() { graph.home(); }, null, null, 'Home'); + this.addAction('exitGroup', function() { graph.exitGroup(); }, null, null, Editor.ctrlKey + '+Shift+Home'); + this.addAction('enterGroup', function() { graph.enterGroup(); }, null, null, Editor.ctrlKey + '+Shift+End'); + this.addAction('collapse', function() { graph.foldCells(true); }, null, null, Editor.ctrlKey + '+Home'); + this.addAction('expand', function() { graph.foldCells(false); }, null, null, Editor.ctrlKey + '+End'); + + // Arrange actions + this.addAction('toFront', function() { graph.orderCells(false); }, null, null, Editor.ctrlKey + '+Shift+F'); + this.addAction('toBack', function() { graph.orderCells(true); }, null, null, Editor.ctrlKey + '+Shift+B'); + this.addAction('group', function() + { + if (graph.getSelectionCount() == 1) + { + graph.setCellStyles('container', '1'); + } + else + { + graph.setSelectionCell(graph.groupCells(null, 0)); + } + }, null, null, Editor.ctrlKey + '+G'); + this.addAction('ungroup', function() + { + if (graph.getSelectionCount() == 1 && graph.getModel().getChildCount(graph.getSelectionCell()) == 0) + { + graph.setCellStyles('container', '0'); + } + else + { + graph.setSelectionCells(graph.ungroupCells()); + } + }, null, null, Editor.ctrlKey + '+Shift+U'); + this.addAction('removeFromGroup', function() { graph.removeCellsFromParent(); }); + // Adds action + this.addAction('edit', function() + { + if (graph.isEnabled()) + { + graph.startEditingAtCell(); + } + }, null, null, 'F2/Enter'); + this.addAction('editData...', function() + { + var cell = graph.getSelectionCell() || graph.getModel().getRoot(); + ui.showDataDialog(cell); + }, null, null, Editor.ctrlKey + '+M'); + this.addAction('editTooltip...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var tooltip = ''; + + if (mxUtils.isNode(cell.value)) + { + var tmp = cell.value.getAttribute('tooltip'); + + if (tmp != null) + { + tooltip = tmp; + } + } + + var dlg = new TextareaDialog(ui, mxResources.get('editTooltip') + ':', tooltip, function(newValue) + { + graph.setTooltipForCell(cell, newValue); + }); + ui.showDialog(dlg.container, 320, 200, true, true); + dlg.init(); + } + }, null, null, 'Alt+Shift+T'); + this.addAction('openLink', function() + { + var link = graph.getLinkForCell(graph.getSelectionCell()); + + if (link != null) + { + graph.openLink(link); + } + }); + this.addAction('editLink...', function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + var cell = graph.getSelectionCell(); + var value = graph.getLinkForCell(cell) || ''; + + ui.showLinkDialog(value, mxResources.get('apply'), function(link) + { + link = mxUtils.trim(link); + graph.setLinkForCell(cell, (link.length > 0) ? link : null); + }); + } + }, null, null, 'Alt+Shift+L'); + this.addAction('insertLink...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + ui.showLinkDialog('', mxResources.get('insert'), function(link, docs) + { + link = mxUtils.trim(link); + + if (link.length > 0) + { + var icon = null; + var title = graph.getLinkTitle(link); + + if (docs != null && docs.length > 0) + { + icon = docs[0].iconUrl; + title = docs[0].name || docs[0].type; + title = title.charAt(0).toUpperCase() + title.substring(1); + + if (title.length > 30) + { + title = title.substring(0, 30) + '...'; + } + } + + var pt = graph.getFreeInsertPoint(); + var linkCell = new mxCell(title, new mxGeometry(pt.x, pt.y, 100, 40), + 'fontColor=#0000EE;fontStyle=4;rounded=1;overflow=hidden;' + ((icon != null) ? + 'shape=label;imageWidth=16;imageHeight=16;spacingLeft=26;align=left;image=' + icon : + 'spacing=10;')); + linkCell.vertex = true; + + graph.setLinkForCell(linkCell, link); + graph.cellSizeUpdated(linkCell, true); + + graph.getModel().beginUpdate(); + try + { + linkCell = graph.addCell(linkCell); + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [linkCell])); + } + finally + { + graph.getModel().endUpdate(); + } + + graph.setSelectionCell(linkCell); + graph.scrollCellToVisible(graph.getSelectionCell()); + } + }); + } + }).isEnabled = isGraphEnabled; + this.addAction('link...', mxUtils.bind(this, function() + { + var graph = ui.editor.graph; + + if (graph.isEnabled()) + { + if (graph.cellEditor.isContentEditing()) + { + var elt = graph.getSelectedElement(); + var link = graph.getParentByName(elt, 'A', graph.cellEditor.textarea); + var oldValue = ''; + + // Workaround for FF returning the outermost selected element after double + // click on a DOM hierarchy with a link inside (but not as topmost element) + if (link == null && elt != null && elt.getElementsByTagName != null) + { + // Finds all links in the selected DOM and uses the link + // where the selection text matches its text content + var links = elt.getElementsByTagName('a'); + + for (var i = 0; i < links.length && link == null; i++) + { + if (links[i].textContent == elt.textContent) + { + graph.selectNode(links[i]); + link = links[i]; + } + } + } + + if (link != null && link.nodeName == 'A') + { + oldValue = link.getAttribute('href') || ''; + } + + var selState = graph.cellEditor.saveSelection(); + + ui.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value) + { + graph.cellEditor.restoreSelection(selState); + + if (value != null) + { + graph.insertLink(value); + } + })); + } + else if (graph.isSelectionEmpty()) + { + this.get('insertLink').funct(); + } + else + { + this.get('editLink').funct(); + } + } + })).isEnabled = isGraphEnabled; + this.addAction('autosize', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().getChildCount(cell)) + { + graph.updateGroupBounds([cell], 20); + } + else + { + var state = graph.view.getState(cell); + var geo = graph.getCellGeometry(cell); + + if (graph.getModel().isVertex(cell) && state != null && state.text != null && + geo != null && graph.isWrapping(cell)) + { + geo = geo.clone(); + geo.height = state.text.boundingBox.height / graph.view.scale; + graph.getModel().setGeometry(cell, geo); + } + else + { + graph.updateCellSize(cell); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, Editor.ctrlKey + '+Shift+Y'); + this.addAction('formattedText', function() + { + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + var value = '1'; + graph.stopEditing(); + + graph.getModel().beginUpdate(); + try + { + if (state.style['html'] == '1') + { + value = null; + var label = graph.convertValueToString(state.cell); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Removes newlines from HTML and converts breaks to newlines + // to match the HTML output in plain text + label = label.replace(/\n/g, '').replace(//g, '\n'); + } + + // Removes HTML tags + var temp = document.createElement('div'); + temp.innerHTML = label; + label = mxUtils.extractTextWithWhitespace(temp.childNodes); + + graph.cellLabelChanged(state.cell, label); + } + else + { + // Converts HTML tags to text + var label = mxUtils.htmlEntities(graph.convertValueToString(state.cell), false); + + if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') + { + // Converts newlines in plain text to breaks in HTML + // to match the plain text output + label = label.replace(/\n/g, '
'); + } + + graph.cellLabelChanged(state.cell, graph.sanitizeHtml(label)); + } + + graph.setCellStyles('html', value); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['html'], + 'values', [(value != null) ? value : '0'], 'cells', + graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('wordWrap', function() + { + var state = graph.getView().getState(graph.getSelectionCell()); + var value = 'wrap'; + + graph.stopEditing(); + + if (state != null && state.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') + { + value = null; + } + + graph.setCellStyles(mxConstants.STYLE_WHITE_SPACE, value); + }); + this.addAction('rotation', function() + { + var value = '0'; + var state = graph.getView().getState(graph.getSelectionCell()); + + if (state != null) + { + value = state.style[mxConstants.STYLE_ROTATION] || value; + } + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), function(newValue) + { + if (newValue != null && newValue.length > 0) + { + graph.setCellStyles(mxConstants.STYLE_ROTATION, newValue); + } + }, mxResources.get('enterValue') + ' (' + mxResources.get('rotation') + ' 0-360)'); + + ui.showDialog(dlg.container, 375, 80, true, true); + dlg.init(); + }); + // View actions + this.addAction('resetView', function() + { + graph.zoomTo(1); + ui.resetScrollbars(); + }, null, null, Editor.ctrlKey + '+H'); + this.addAction('zoomIn', function(evt) { graph.zoomIn(); }, null, null, Editor.ctrlKey + ' + (Numpad) / Alt+Mousewheel'); + this.addAction('zoomOut', function(evt) { graph.zoomOut(); }, null, null, Editor.ctrlKey + ' - (Numpad) / Alt+Mousewheel'); + this.addAction('fitWindow', function() { graph.fit(); }, null, null, Editor.ctrlKey + '+Shift+H'); + this.addAction('fitPage', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + var scale = Math.floor(20 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = pad.y * graph.view.scale - 1; + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, (graph.container.scrollWidth - graph.container.clientWidth) / 2) - 1; + } + }), null, null, Editor.ctrlKey + '+J'); + this.addAction('fitTwoPages', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + var ch = graph.container.clientHeight - 10; + + var scale = Math.floor(20 * Math.min(cw / (2 * fmt.width) / ps, ch / fmt.height / ps)) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollTop = Math.min(pad.y, (graph.container.scrollHeight - graph.container.clientHeight) / 2); + graph.container.scrollLeft = Math.min(pad.x, (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + }), null, null, Editor.ctrlKey + '+Shift+J'); + this.addAction('fitPageWidth', mxUtils.bind(this, function() + { + if (!graph.pageVisible) + { + this.get('pageView').funct(); + } + + var fmt = graph.pageFormat; + var ps = graph.pageScale; + var cw = graph.container.clientWidth - 10; + + var scale = Math.floor(20 * cw / fmt.width / ps) / 20; + graph.zoomTo(scale); + + if (mxUtils.hasScrollbars(graph.container)) + { + var pad = graph.getPagePadding(); + graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, + (graph.container.scrollWidth - graph.container.clientWidth) / 2); + } + })); + this.put('customZoom', new Action(mxResources.get('custom') + '...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.getView().getScale() * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + graph.zoomTo(val / 100); + } + }), mxResources.get('zoom') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + }), null, null, Editor.ctrlKey + '+0')); + this.addAction('pageScale...', mxUtils.bind(this, function() + { + var dlg = new FilenameDialog(this.editorUi, parseInt(graph.pageScale * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + var val = parseInt(newValue); + + if (!isNaN(val) && val > 0) + { + ui.setPageScale(val / 100); + } + }), mxResources.get('pageScale') + ' (%)'); + this.editorUi.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + })); + + // Option actions + var action = null; + action = this.addAction('grid', function() + { + graph.setGridEnabled(!graph.isGridEnabled()); + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, null, null, Editor.ctrlKey + '+Shift+G'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.isGridEnabled(); }); + action.setEnabled(false); + + action = this.addAction('guides', function() + { + graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled; + ui.fireEvent(new mxEventObject('guidesEnabledChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; }); + action.setEnabled(false); + + action = this.addAction('tooltips', function() + { + graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); }); + + action = this.addAction('collapseExpand', function() + { + var change = new ChangePageSetup(ui); + change.ignoreColor = true; + change.ignoreImage = true; + change.foldingEnabled = !graph.foldingEnabled; + + graph.model.execute(change); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.foldingEnabled; }); + action.isEnabled = isGraphEnabled; + action = this.addAction('scrollbars', function() + { + ui.setScrollbars(!ui.hasScrollbars()); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.scrollbars; }); + action = this.addAction('pageView', mxUtils.bind(this, function() + { + ui.setPageVisible(!graph.pageVisible); + })); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.pageVisible; }); + action = this.addAction('connectionArrows', function() + { + graph.connectionArrowsEnabled = !graph.connectionArrowsEnabled; + ui.fireEvent(new mxEventObject('connectionArrowsChanged')); + }, null, null, 'Alt+Shift+A'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionArrowsEnabled; }); + action = this.addAction('connectionPoints', function() + { + graph.setConnectable(!graph.connectionHandler.isEnabled()); + ui.fireEvent(new mxEventObject('connectionPointsChanged')); + }, null, null, 'Alt+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); }); + action = this.addAction('copyConnect', function() + { + graph.connectionHandler.setCreateTarget(!graph.connectionHandler.isCreateTarget()); + ui.fireEvent(new mxEventObject('copyConnectChanged')); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return graph.connectionHandler.isCreateTarget(); }); + action.isEnabled = isGraphEnabled; + action = this.addAction('autosave', function() + { + ui.editor.setAutosave(!ui.editor.autosave); + }); + action.setToggleAction(true); + action.setSelectedCallback(function() { return ui.editor.autosave; }); + action.isEnabled = isGraphEnabled; + action.visible = false; + + // Help actions + this.addAction('help', function() + { + var ext = ''; + + if (mxResources.isLanguageSupported(mxClient.language)) + { + ext = '_' + mxClient.language; + } + + graph.openLink(RESOURCES_PATH + '/help' + ext + '.html'); + }); + + var showingAbout = false; + + this.put('about', new Action(mxResources.get('about') + ' Graph Editor...', function() + { + if (!showingAbout) + { + ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true, function() + { + showingAbout = false; + }); + + showingAbout = true; + } + }, null, null, 'F1')); + + // Font style actions + var toggleFontStyle = mxUtils.bind(this, function(key, style, fn, shortcut) + { + return this.addAction(key, function() + { + if (fn != null && graph.cellEditor.isContentEditing()) + { + fn(); + } + else + { + graph.stopEditing(false); + + graph.getModel().beginUpdate(); + try + { + graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style); + + // Removes bold and italic tags and CSS styles inside labels + if ((style & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontWeight = null; + + if (elt.nodeName == 'B') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontStyle = null; + + if (elt.nodeName == 'I') + { + graph.replaceElement(elt); + } + }); + } + else if ((style & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.textDecoration = null; + + if (elt.nodeName == 'U') + { + graph.replaceElement(elt); + } + }); + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, shortcut); + }); + + toggleFontStyle('bold', mxConstants.FONT_BOLD, function() { document.execCommand('bold', false, null); }, Editor.ctrlKey + '+B'); + toggleFontStyle('italic', mxConstants.FONT_ITALIC, function() { document.execCommand('italic', false, null); }, Editor.ctrlKey + '+I'); + toggleFontStyle('underline', mxConstants.FONT_UNDERLINE, function() { document.execCommand('underline', false, null); }, Editor.ctrlKey + '+U'); + + // Color actions + this.addAction('fontColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR, 'forecolor', '000000'); }); + this.addAction('strokeColor...', function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); }); + this.addAction('fillColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); }); + this.addAction('gradientColor...', function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); }); + this.addAction('backgroundColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'backcolor'); }); + this.addAction('borderColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); }); + + // Format actions + this.addAction('vertical', function() { ui.menus.toggleStyle(mxConstants.STYLE_HORIZONTAL, true); }); + this.addAction('shadow', function() { ui.menus.toggleStyle(mxConstants.STYLE_SHADOW); }); + this.addAction('solid', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, null); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', [null, null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dashed', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', null], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('dotted', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); + graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, '1 4'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], + 'values', ['1', '1 4'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('sharp', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('rounded', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '1'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['1', '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('toggleRounded', function() + { + if (!graph.isSelectionEmpty() && graph.isEnabled()) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + var state = graph.view.getState(cells[0]); + var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); + var value = (mxUtils.getValue(style, mxConstants.STYLE_ROUNDED, '0') == '1') ? '0' : '1'; + + graph.setCellStyles(mxConstants.STYLE_ROUNDED, value); + graph.setCellStyles(mxConstants.STYLE_CURVED, null); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', [value, '0'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }); + this.addAction('curved', function() + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); + graph.setCellStyles(mxConstants.STYLE_CURVED, '1'); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], + 'values', ['0', '1'], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); + this.addAction('collapsible', function() + { + var state = graph.view.getState(graph.getSelectionCell()); + var value = '1'; + + if (state != null && graph.getFoldingImage(state) != null) + { + value = '0'; + } + + graph.setCellStyles('collapsible', value); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['collapsible'], + 'values', [value], 'cells', graph.getSelectionCells())); + }); + this.addAction('editStyle...', mxUtils.bind(this, function() + { + var cells = graph.getSelectionCells(); + + if (cells != null && cells.length > 0) + { + var model = graph.getModel(); + + var dlg = new TextareaDialog(this.editorUi, mxResources.get('editStyle') + ':', + model.getStyle(cells[0]) || '', function(newValue) + { + if (newValue != null) + { + graph.setCellStyle(mxUtils.trim(newValue), cells); + } + }, null, null, 400, 220); + this.editorUi.showDialog(dlg.container, 420, 300, true, true); + dlg.init(); + } + }), null, null, Editor.ctrlKey + '+E'); + this.addAction('setAsDefaultStyle', function() + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + ui.setDefaultStyle(graph.getSelectionCell()); + } + }, null, null, Editor.ctrlKey + '+Shift+D'); + this.addAction('clearDefaultStyle', function() + { + if (graph.isEnabled()) + { + ui.clearDefaultStyle(); + } + }, null, null, Editor.ctrlKey + '+Shift+R'); + this.addAction('addWaypoint', function() + { + var cell = graph.getSelectionCell(); + + if (cell != null && graph.getModel().isEdge(cell)) + { + var handler = editor.graph.selectionCellsHandler.getHandler(cell); + + if (handler instanceof mxEdgeHandler) + { + var t = graph.view.translate; + var s = graph.view.scale; + var dx = t.x; + var dy = t.y; + + var parent = graph.getModel().getParent(cell); + var pgeo = graph.getCellGeometry(parent); + + while (graph.getModel().isVertex(parent) && pgeo != null) + { + dx += pgeo.x; + dy += pgeo.y; + + parent = graph.getModel().getParent(parent); + pgeo = graph.getCellGeometry(parent); + } + + var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); + var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); + + handler.addPointAt(handler.state, x, y); + } + } + }); + this.addAction('removeWaypoint', function() + { + // TODO: Action should run with "this" set to action + var rmWaypointAction = ui.actions.get('removeWaypoint'); + + if (rmWaypointAction.handler != null) + { + // NOTE: Popupevent handled and action updated in Menus.createPopupMenu + rmWaypointAction.handler.removePoint(rmWaypointAction.handler.state, rmWaypointAction.index); + } + }); + this.addAction('clearWaypoints', function() + { + var cells = graph.getSelectionCells(); + + if (cells != null) + { + cells = graph.addAllEdges(cells); + + graph.getModel().beginUpdate(); + try + { + for (var i = 0; i < cells.length; i++) + { + var cell = cells[i]; + + if (graph.getModel().isEdge(cell)) + { + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.points = null; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + } + }, null, null, 'Alt+Shift+C'); + action = this.addAction('subscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('subscript', false, null); + } + }), null, null, Editor.ctrlKey + '+,'); + action = this.addAction('superscript', mxUtils.bind(this, function() + { + if (graph.cellEditor.isContentEditing()) + { + document.execCommand('superscript', false, null); + } + }), null, null, Editor.ctrlKey + '+.'); + this.addAction('image...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + var title = mxResources.get('image') + ' (' + mxResources.get('url') + '):'; + var state = graph.getView().getState(graph.getSelectionCell()); + var value = ''; + + if (state != null) + { + value = state.style[mxConstants.STYLE_IMAGE] || value; + } + + var selectionState = graph.cellEditor.saveSelection(); + + ui.showImageDialog(title, value, function(newValue, w, h) + { + // Inserts image into HTML text + if (graph.cellEditor.isContentEditing()) + { + graph.cellEditor.restoreSelection(selectionState); + graph.insertImage(newValue, w, h); + } + else + { + var cells = graph.getSelectionCells(); + + if (newValue != null && (newValue.length > 0 || cells.length > 0)) + { + var select = null; + + graph.getModel().beginUpdate(); + try + { + // Inserts new cell if no cell is selected + if (cells.length == 0) + { + var pt = graph.getFreeInsertPoint(); + cells = [graph.insertVertex(graph.getDefaultParent(), null, '', pt.x, pt.y, w, h, + 'shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;')]; + select = cells; + graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); + } + + graph.setCellStyles(mxConstants.STYLE_IMAGE, (newValue.length > 0) ? newValue : null, cells); + + // Sets shape only if not already shape with image (label or image) + var state = graph.view.getState(cells[0]); + var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); + + if (style[mxConstants.STYLE_SHAPE] != 'image' && style[mxConstants.STYLE_SHAPE] != 'label') + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, 'image', cells); + } + else if (newValue.length == 0) + { + graph.setCellStyles(mxConstants.STYLE_SHAPE, null, cells); + } + + if (graph.getSelectionCount() == 1) + { + if (w != null && h != null) + { + var cell = cells[0]; + var geo = graph.getModel().getGeometry(cell); + + if (geo != null) + { + geo = geo.clone(); + geo.width = w; + geo.height = h; + graph.getModel().setGeometry(cell, geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + if (select != null) + { + graph.setSelectionCells(select); + graph.scrollCellToVisible(select[0]); + } + } + } + }, graph.cellEditor.isContentEditing(), !graph.cellEditor.isContentEditing()); + } + }).isEnabled = isGraphEnabled; + this.addAction('insertImage...', function() + { + if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) + { + graph.clearSelection(); + ui.actions.get('image').funct(); + } + }).isEnabled = isGraphEnabled; + action = this.addAction('layers', mxUtils.bind(this, function() + { + if (this.layersWindow == null) + { + // LATER: Check outline window for initial placement + this.layersWindow = new LayersWindow(ui, document.body.offsetWidth - 280, 120, 220, 180); + this.layersWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('layers')); + }); + this.layersWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('layers')); + } + else + { + this.layersWindow.window.setVisible(!this.layersWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+L'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.layersWindow != null && this.layersWindow.window.isVisible(); })); + action = this.addAction('formatPanel', mxUtils.bind(this, function() + { + ui.toggleFormatPanel(); + }), null, null, Editor.ctrlKey + '+Shift+P'); + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return ui.formatWidth > 0; })); + action = this.addAction('outline', mxUtils.bind(this, function() + { + if (this.outlineWindow == null) + { + // LATER: Check layers window for initial placement + this.outlineWindow = new OutlineWindow(ui, document.body.offsetWidth - 260, 100, 180, 180); + this.outlineWindow.window.addListener('show', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.addListener('hide', function() + { + ui.fireEvent(new mxEventObject('outline')); + }); + this.outlineWindow.window.setVisible(true); + ui.fireEvent(new mxEventObject('outline')); + } + else + { + this.outlineWindow.window.setVisible(!this.outlineWindow.window.isVisible()); + } + }), null, null, Editor.ctrlKey + '+Shift+O'); + + action.setToggleAction(true); + action.setSelectedCallback(mxUtils.bind(this, function() { return this.outlineWindow != null && this.outlineWindow.window.isVisible(); })); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut) +{ + var title; + + if (key.substring(key.length - 3) == '...') + { + key = key.substring(0, key.length - 3); + title = mxResources.get(key) + '...'; + } + else + { + title = mxResources.get(key); + } + + return this.put(key, new Action(title, funct, enabled, iconCls, shortcut)); +}; + +/** + * Registers the given action under the given name. + */ +Actions.prototype.put = function(name, action) +{ + this.actions[name] = action; + + return action; +}; + +/** + * Returns the action for the given name or null if no such action exists. + */ +Actions.prototype.get = function(name) +{ + return this.actions[name]; +}; + +/** + * Constructs a new action for the given parameters. + */ +function Action(label, funct, enabled, iconCls, shortcut) +{ + mxEventSource.call(this); + this.label = label; + this.funct = this.createFunction(funct); + this.enabled = (enabled != null) ? enabled : true; + this.iconCls = iconCls; + this.shortcut = shortcut; + this.visible = true; +}; + +// Action inherits from mxEventSource +mxUtils.extend(Action, mxEventSource); + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.createFunction = function(funct) +{ + return funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setEnabled = function(value) +{ + if (this.enabled != value) + { + this.enabled = value; + this.fireEvent(new mxEventObject('stateChanged')); + } +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isEnabled = function() +{ + return this.enabled; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setToggleAction = function(value) +{ + this.toggleAction = value; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.setSelectedCallback = function(funct) +{ + this.selectedCallback = funct; +}; + +/** + * Sets the enabled state of the action and fires a stateChanged event. + */ +Action.prototype.isSelected = function() +{ + return this.selectedCallback(); +}; diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Dialogs.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Dialogs.js new file mode 100644 index 00000000..14f9d7d0 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Dialogs.js @@ -0,0 +1,2603 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +/** + * Constructs a new open dialog. + */ +var OpenDialog = function() +{ + var iframe = document.createElement('iframe'); + iframe.style.backgroundColor = 'transparent'; + iframe.allowTransparency = 'true'; + iframe.style.borderStyle = 'none'; + iframe.style.borderWidth = '0px'; + iframe.style.overflow = 'hidden'; + iframe.frameBorder = '0'; + + // Adds padding as a workaround for box model in older IE versions + var dx = (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) ? 20 : 0; + + iframe.setAttribute('width', (((Editor.useLocalStorage) ? 640 : 320) + dx) + 'px'); + iframe.setAttribute('height', (((Editor.useLocalStorage) ? 480 : 220) + dx) + 'px'); + iframe.setAttribute('src', OPEN_FORM); + + this.container = iframe; +}; + +/** + * Constructs a new color dialog. + */ +var ColorDialog = function(editorUi, color, apply, cancelFn) +{ + this.editorUi = editorUi; + + var input = document.createElement('input'); + input.style.marginBottom = '10px'; + input.style.width = '216px'; + + // Required for picker to render in IE + if (mxClient.IS_IE) + { + input.style.marginTop = '10px'; + document.body.appendChild(input); + } + + this.init = function() + { + if (!mxClient.IS_TOUCH) + { + input.focus(); + } + }; + + var picker = new jscolor.color(input); + picker.pickerOnfocus = false; + picker.showPicker(); + + var div = document.createElement('div'); + jscolor.picker.box.style.position = 'relative'; + jscolor.picker.box.style.width = '230px'; + jscolor.picker.box.style.height = '100px'; + jscolor.picker.box.style.paddingBottom = '10px'; + div.appendChild(jscolor.picker.box); + + var center = document.createElement('center'); + + function createRecentColorTable() + { + var table = addPresets((ColorDialog.recentColors.length == 0) ? ['FFFFFF'] : + ColorDialog.recentColors, 11, 'FFFFFF', true); + table.style.marginBottom = '8px'; + + return table; + }; + + function addPresets(presets, rowLength, defaultColor, addResetOption) + { + rowLength = (rowLength != null) ? rowLength : 12; + var table = document.createElement('table'); + table.style.borderCollapse = 'collapse'; + table.setAttribute('cellspacing', '0'); + table.style.marginBottom = '20px'; + table.style.cellSpacing = '0px'; + var tbody = document.createElement('tbody'); + table.appendChild(tbody); + + var rows = presets.length / rowLength; + + for (var row = 0; row < rows; row++) + { + var tr = document.createElement('tr'); + + for (var i = 0; i < rowLength; i++) + { + (function(clr) + { + var td = document.createElement('td'); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + + if (clr == null) + { + clr = defaultColor; + } + + if (clr == 'none') + { + td.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')'; + } + else + { + td.style.backgroundColor = '#' + clr; + } + + tr.appendChild(td); + + if (clr != null) + { + td.style.cursor = 'pointer'; + + mxEvent.addListener(td, 'click', function() + { + if (clr == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(clr); + } + }); + } + })(presets[row * rowLength + i]); + } + + tbody.appendChild(tr); + } + + if (addResetOption) + { + var td = document.createElement('td'); + td.setAttribute('title', mxResources.get('reset')); + td.style.border = '1px solid black'; + td.style.padding = '0px'; + td.style.width = '16px'; + td.style.height = '16px'; + td.style.backgroundImage = 'url(\'' + Dialog.prototype.closeImage + '\')'; + td.style.backgroundPosition = 'center center'; + td.style.backgroundRepeat = 'no-repeat'; + td.style.cursor = 'pointer'; + + tr.appendChild(td); + + mxEvent.addListener(td, 'click', function() + { + ColorDialog.resetRecentColors(); + table.parentNode.replaceChild(createRecentColorTable(), table); + }); + } + + center.appendChild(table); + + return table; + }; + + div.appendChild(input); + mxUtils.br(div); + + // Adds recent colors + createRecentColorTable(); + + // Adds presets + var table = addPresets(this.presetColors); + table.style.marginBottom = '8px'; + table = addPresets(this.defaultColors); + table.style.marginBottom = '16px'; + + div.appendChild(center); + + var buttons = document.createElement('div'); + buttons.style.textAlign = 'right'; + buttons.style.whiteSpace = 'nowrap'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + var applyFunction = (apply != null) ? apply : this.createApplyFunction(); + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + var color = input.value; + ColorDialog.addRecentColor(color, 12); + + if (color != 'none' && color.charAt(0) != '#') + { + color = '#' + color; + } + + applyFunction(color); + editorUi.hideDialog(); + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + buttons.appendChild(applyBtn); + + if (!editorUi.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + } + + if (color != null) + { + if (color == 'none') + { + picker.fromString('ffffff'); + input.value = 'none'; + } + else + { + picker.fromString(color); + } + } + + div.appendChild(buttons); + this.picker = picker; + this.colorInput = input; + + // LATER: Only fires if input if focused, should always + // fire if this dialog is showing. + mxEvent.addListener(div, 'keydown', function(e) + { + if (e.keyCode == 27) + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + + mxEvent.consume(e); + } + }); + + this.container = div; +}; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.presetColors = ['E6D0DE', 'CDA2BE', 'B5739D', 'E1D5E7', 'C3ABD0', 'A680B8', 'D4E1F5', 'A9C4EB', '7EA6E0', 'D5E8D4', '9AC7BF', '67AB9F', 'D5E8D4', 'B9E0A5', '97D077', 'FFF2CC', 'FFE599', 'FFD966', 'FFF4C3', 'FFCE9F', 'FFB570', 'F8CECC', 'F19C99', 'EA6B66']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.defaultColors = ['none', 'FFFFFF', 'E6E6E6', 'CCCCCC', 'B3B3B3', '999999', '808080', '666666', '4D4D4D', '333333', '1A1A1A', '000000', 'FFCCCC', 'FFE6CC', 'FFFFCC', 'E6FFCC', 'CCFFCC', 'CCFFE6', 'CCFFFF', 'CCE5FF', 'CCCCFF', 'E5CCFF', 'FFCCFF', 'FFCCE6', + 'FF9999', 'FFCC99', 'FFFF99', 'CCFF99', '99FF99', '99FFCC', '99FFFF', '99CCFF', '9999FF', 'CC99FF', 'FF99FF', 'FF99CC', 'FF6666', 'FFB366', 'FFFF66', 'B3FF66', '66FF66', '66FFB3', '66FFFF', '66B2FF', '6666FF', 'B266FF', 'FF66FF', 'FF66B3', 'FF3333', 'FF9933', 'FFFF33', + '99FF33', '33FF33', '33FF99', '33FFFF', '3399FF', '3333FF', '9933FF', 'FF33FF', 'FF3399', 'FF0000', 'FF8000', 'FFFF00', '80FF00', '00FF00', '00FF80', '00FFFF', '007FFF', '0000FF', '7F00FF', 'FF00FF', 'FF0080', 'CC0000', 'CC6600', 'CCCC00', '66CC00', '00CC00', '00CC66', + '00CCCC', '0066CC', '0000CC', '6600CC', 'CC00CC', 'CC0066', '990000', '994C00', '999900', '4D9900', '009900', '00994D', '009999', '004C99', '000099', '4C0099', '990099', '99004D', '660000', '663300', '666600', '336600', '006600', '006633', '006666', '003366', '000066', + '330066', '660066', '660033', '330000', '331A00', '333300', '1A3300', '003300', '00331A', '003333', '001933', '000033', '190033', '330033', '33001A']; + +/** + * Creates function to apply value + */ +ColorDialog.prototype.createApplyFunction = function() +{ + return mxUtils.bind(this, function(color) + { + var graph = this.editorUi.editor.graph; + + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(this.currentColorKey, color); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [this.currentColorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }); +}; + +/** + * + */ +ColorDialog.recentColors = []; + +/** + * Adds recent color for later use. + */ +ColorDialog.addRecentColor = function(color, max) +{ + if (color != null) + { + mxUtils.remove(color, ColorDialog.recentColors); + ColorDialog.recentColors.splice(0, 0, color); + + if (ColorDialog.recentColors.length >= max) + { + ColorDialog.recentColors.pop(); + } + } +}; + +/** + * Adds recent color for later use. + */ +ColorDialog.resetRecentColors = function() +{ + ColorDialog.recentColors = []; +}; + +/** + * Constructs a new about dialog. + */ +var AboutDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.setAttribute('align', 'center'); + var h3 = document.createElement('h3'); + mxUtils.write(h3, mxResources.get('about') + ' GraphEditor'); + div.appendChild(h3); + var img = document.createElement('img'); + img.style.border = '0px'; + img.setAttribute('width', '176'); + img.setAttribute('width', '151'); + img.setAttribute('src', IMAGE_PATH + '/logo.png'); + div.appendChild(img); + mxUtils.br(div); + mxUtils.write(div, 'Powered by mxGraph ' + mxClient.VERSION); + mxUtils.br(div); + var link = document.createElement('a'); + link.setAttribute('href', 'http://www.jgraph.com/'); + link.setAttribute('target', '_blank'); + mxUtils.write(link, 'www.jgraph.com'); + div.appendChild(link); + mxUtils.br(div); + mxUtils.br(div); + var closeBtn = mxUtils.button(mxResources.get('close'), function() + { + editorUi.hideDialog(); + }); + closeBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(closeBtn); + + this.container = div; +}; + +/** + * Constructs a new filename dialog. + */ +var FilenameDialog = function(editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn) +{ + closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.style.marginTop = '8px'; + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.whiteSpace = 'nowrap'; + td.style.fontSize = '10pt'; + td.style.width = '120px'; + mxUtils.write(td, (label || mxResources.get('filename')) + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', filename || ''); + nameInput.style.marginLeft = '4px'; + nameInput.style.width = '180px'; + + var genericBtn = mxUtils.button(buttonText, function() + { + if (validateFn == null || validateFn(nameInput.value)) + { + if (closeOnBtn) + { + editorUi.hideDialog(); + } + + fn(nameInput.value); + } + }); + genericBtn.className = 'geBtn gePrimaryBtn'; + + this.init = function() + { + if (label == null && content != null) + { + return; + } + + nameInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) + { + nameInput.select(); + } + else + { + document.execCommand('selectAll', false, null); + } + + // Installs drag and drop handler for links + if (Graph.fileSupport) + { + // Setup the dnd listeners + var dlg = table.parentNode; + var graph = editorUi.editor.graph; + var dropElt = null; + + mxEvent.addListener(dlg, 'dragleave', function(evt) + { + if (dropElt != null) + { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function(evt) + { + // IE 10 does not implement pointer-events so it can't have a drop highlight + if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10)) + { + dropElt = nameInput; + dropElt.style.backgroundColor = '#ebf2f9'; + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + + mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function(evt) + { + if (dropElt != null) + { + dropElt.style.backgroundColor = ''; + dropElt = null; + } + + if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) + { + nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list')); + genericBtn.click(); + } + + evt.stopPropagation(); + evt.preventDefault(); + })); + } + }; + + td = document.createElement('td'); + td.appendChild(nameInput); + row.appendChild(td); + + if (label != null || content == null) + { + tbody.appendChild(row); + } + + if (content != null) + { + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.appendChild(content); + row.appendChild(td); + tbody.appendChild(row); + } + + row = document.createElement('tr'); + td = document.createElement('td'); + td.colSpan = 2; + td.style.paddingTop = '20px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (helpLink != null) + { + var helpBtn = mxUtils.button(mxResources.get('help'), function() + { + editorUi.editor.graph.openLink(helpLink); + }); + + helpBtn.className = 'geBtn'; + td.appendChild(helpBtn); + } + + mxEvent.addListener(nameInput, 'keypress', function(e) + { + if (e.keyCode == 13) + { + genericBtn.click(); + } + }); + + td.appendChild(genericBtn); + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + + this.container = table; +}; + +/** + * Constructs a new textarea dialog. + */ +var TextareaDialog = function(editorUi, title, url, fn, cancelFn, cancelTitle, w, h, addButtons, noHide, noWrap, applyTitle) +{ + w = (w != null) ? w : 300; + h = (h != null) ? h : 120; + noHide = (noHide != null) ? noHide : false; + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, title); + + row.appendChild(td); + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + + var nameInput = document.createElement('textarea'); + + if (noWrap) + { + nameInput.setAttribute('wrap', 'off'); + } + + nameInput.setAttribute('spellcheck', 'false'); + nameInput.setAttribute('autocorrect', 'off'); + nameInput.setAttribute('autocomplete', 'off'); + nameInput.setAttribute('autocapitalize', 'off'); + + mxUtils.write(nameInput, url || ''); + nameInput.style.resize = 'none'; + nameInput.style.width = w + 'px'; + nameInput.style.height = h + 'px'; + + this.textarea = nameInput; + + this.init = function() + { + nameInput.focus(); + nameInput.scrollTop = 0; + }; + + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.style.paddingTop = '14px'; + td.style.whiteSpace = 'nowrap'; + td.setAttribute('align', 'right'); + + var cancelBtn = mxUtils.button(cancelTitle || mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + + if (cancelFn != null) + { + cancelFn(); + } + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + if (addButtons != null) + { + addButtons(td); + } + + if (fn != null) + { + var genericBtn = mxUtils.button(applyTitle || mxResources.get('apply'), function() + { + if (!noHide) + { + editorUi.hideDialog(); + } + + fn(nameInput.value); + }); + + genericBtn.className = 'geBtn gePrimaryBtn'; + td.appendChild(genericBtn); + } + + if (!editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Constructs a new edit file dialog. + */ +var EditDiagramDialog = function(editorUi) +{ + var div = document.createElement('div'); + div.style.textAlign = 'right'; + var textarea = document.createElement('textarea'); + textarea.setAttribute('wrap', 'off'); + textarea.setAttribute('spellcheck', 'false'); + textarea.setAttribute('autocorrect', 'off'); + textarea.setAttribute('autocomplete', 'off'); + textarea.setAttribute('autocapitalize', 'off'); + textarea.style.overflow = 'auto'; + textarea.style.resize = 'none'; + //begin modify by wsp + //textarea.style.width = '600px'; + //textarea.style.height = '360px'; + textarea.style.width = '550px'; + textarea.style.height = '250px'; + //end modify by wsp + textarea.style.marginBottom = '16px'; + + textarea.value = mxUtils.getPrettyXml(editorUi.editor.getGraphXml()); + div.appendChild(textarea); + + this.init = function() + { + textarea.focus(); + }; + + // Enables dropping files + if (Graph.fileSupport) + { + function handleDrop(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + + if (evt.dataTransfer.files.length > 0) + { + var file = evt.dataTransfer.files[0]; + var reader = new FileReader(); + + reader.onload = function(e) + { + textarea.value = e.target.result; + }; + + reader.readAsText(file); + } + else + { + textarea.value = editorUi.extractGraphModelFromEvent(evt); + } + }; + + function handleDragOver(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }; + + // Setup the dnd listeners. + textarea.addEventListener('dragover', handleDragOver, false); + textarea.addEventListener('drop', handleDrop, false); + } + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + var select = document.createElement('select'); + select.style.width = '180px'; + select.className = 'geBtn'; + + if (editorUi.editor.graph.isEnabled()) + { + var replaceOption = document.createElement('option'); + replaceOption.setAttribute('value', 'replace'); + mxUtils.write(replaceOption, mxResources.get('replaceExistingDrawing')); + select.appendChild(replaceOption); + } + + var newOption = document.createElement('option'); + newOption.setAttribute('value', 'new'); + mxUtils.write(newOption, mxResources.get('openInNewWindow')); + + if (EditDiagramDialog.showNewWindowOption) + { + select.appendChild(newOption); + } + + if (editorUi.editor.graph.isEnabled()) + { + var importOption = document.createElement('option'); + importOption.setAttribute('value', 'import'); + mxUtils.write(importOption, mxResources.get('addToExistingDrawing')); + select.appendChild(importOption); + } + + div.appendChild(select); + + var okBtn = mxUtils.button(mxResources.get('ok'), function() + { + // Removes all illegal control characters before parsing + var data = editorUi.editor.graph.zapGremlins(mxUtils.trim(textarea.value)); + var error = null; + + if (select.value == 'new') + { + window.openFile = new OpenFile(function() + { + editorUi.hideDialog(); + window.openFile = null; + }); + + window.openFile.setData(data, null); + editorUi.editor.graph.openLink(editorUi.getUrl()); + } + else if (select.value == 'replace') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + editorUi.editor.setGraphXml(mxUtils.parseXml(data).documentElement); + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + else if (select.value == 'import') + { + editorUi.editor.graph.model.beginUpdate(); + try + { + var doc = mxUtils.parseXml(data); + var model = new mxGraphModel(); + var codec = new mxCodec(doc); + codec.decode(doc.documentElement, model); + + var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); + editorUi.editor.graph.setSelectionCells(editorUi.editor.graph.importCells(children)); + + // LATER: Why is hideDialog between begin-/endUpdate faster? + editorUi.hideDialog(); + } + catch (e) + { + error = e; + } + finally + { + editorUi.editor.graph.model.endUpdate(); + } + } + + if (error != null) + { + mxUtils.alert(error.message); + } + }); + okBtn.className = 'geBtn gePrimaryBtn'; + div.appendChild(okBtn); + + if (!editorUi.editor.cancelFirst) + { + div.appendChild(cancelBtn); + } + + this.container = div; +}; + +/** + * + */ +EditDiagramDialog.showNewWindowOption = true; + + +//begin add by wsp ----------------------------------- +var CodemirrorDiagramDialog = function(editorUi,codemirrorHeight,targetTextareaWidget,okCallback) +{ + var div = document.createElement('div'); + + var codemirrorContainer =document.createElement('div'); + codemirrorContainer.style.border='1px solid #CCCCCC'; + div.appendChild(codemirrorContainer); + + mxUtils.br(div); + + var actionContainer =document.createElement('div'); + actionContainer.style ='text-align:right'; + div.appendChild(actionContainer); + + var codemirror =null; + + this.init = function() + { + codemirror =CodeMirror(codemirrorContainer,{ + mode: 'groovy', + lineWrapping: false, + lineNumbers: false, + extraKeys:{ + 'F1': 'autocomplete' + } + }); + codemirror.setSize('100%',codemirrorHeight + 'px'); + codemirror.setValue(targetTextareaWidget.value || ''); + codemirror.focus(); + }; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + actionContainer.appendChild(cancelBtn); + + var okBtn = mxUtils.button(mxResources.get('ok'), function() + { + editorUi.hideDialog(); + targetTextareaWidget.value =codemirror.getValue(); + okCallback(); + }); + okBtn.className = 'geBtn gePrimaryBtn'; + actionContainer.appendChild(okBtn); + this.container = div; +}; + +CodemirrorDiagramDialog.showNewWindowOption = true; +//end add by wsp ----------------------------------- + + + +/** + * Constructs a new export dialog. + */ +var ExportDialog = function(editorUi) +{ + var graph = editorUi.editor.graph; + var bounds = graph.getGraphBounds(); + var scale = graph.view.scale; + + var width = Math.ceil(bounds.width / scale); + var height = Math.ceil(bounds.height / scale); + + var row, td; + + var table = document.createElement('table'); + var tbody = document.createElement('tbody'); + table.setAttribute('cellpadding', (mxClient.IS_SF) ? '0' : '2'); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + td.style.width = '100px'; + mxUtils.write(td, mxResources.get('filename') + ':'); + + row.appendChild(td); + + var nameInput = document.createElement('input'); + nameInput.setAttribute('value', editorUi.editor.getOrCreateFilename()); + nameInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(nameInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('format') + ':'); + + row.appendChild(td); + + var imageFormatSelect = document.createElement('select'); + imageFormatSelect.style.width = '180px'; + + var pngOption = document.createElement('option'); + pngOption.setAttribute('value', 'png'); + mxUtils.write(pngOption, mxResources.get('formatPng')); + imageFormatSelect.appendChild(pngOption); + + var gifOption = document.createElement('option'); + + if (ExportDialog.showGifOption) + { + gifOption.setAttribute('value', 'gif'); + mxUtils.write(gifOption, mxResources.get('formatGif')); + imageFormatSelect.appendChild(gifOption); + } + + var jpgOption = document.createElement('option'); + jpgOption.setAttribute('value', 'jpg'); + mxUtils.write(jpgOption, mxResources.get('formatJpg')); + imageFormatSelect.appendChild(jpgOption); + + var pdfOption = document.createElement('option'); + pdfOption.setAttribute('value', 'pdf'); + mxUtils.write(pdfOption, mxResources.get('formatPdf')); + imageFormatSelect.appendChild(pdfOption); + + var svgOption = document.createElement('option'); + svgOption.setAttribute('value', 'svg'); + mxUtils.write(svgOption, mxResources.get('formatSvg')); + imageFormatSelect.appendChild(svgOption); + + if (ExportDialog.showXmlOption) + { + var xmlOption = document.createElement('option'); + xmlOption.setAttribute('value', 'xml'); + mxUtils.write(xmlOption, mxResources.get('formatXml')); + imageFormatSelect.appendChild(xmlOption); + } + + td = document.createElement('td'); + td.appendChild(imageFormatSelect); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('zoom') + ' (%):'); + + row.appendChild(td); + + var zoomInput = document.createElement('input'); + zoomInput.setAttribute('type', 'number'); + zoomInput.setAttribute('value', '100'); + zoomInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(zoomInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('width') + ':'); + + row.appendChild(td); + + var widthInput = document.createElement('input'); + widthInput.setAttribute('value', width); + widthInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(widthInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('height') + ':'); + + row.appendChild(td); + + var heightInput = document.createElement('input'); + heightInput.setAttribute('value', height); + heightInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(heightInput); + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('background') + ':'); + + row.appendChild(td); + + var transparentCheckbox = document.createElement('input'); + transparentCheckbox.setAttribute('type', 'checkbox'); + transparentCheckbox.checked = graph.background == null || graph.background == mxConstants.NONE; + + td = document.createElement('td'); + td.appendChild(transparentCheckbox); + mxUtils.write(td, mxResources.get('transparent')); + + row.appendChild(td); + + tbody.appendChild(row); + + row = document.createElement('tr'); + + td = document.createElement('td'); + td.style.fontSize = '10pt'; + mxUtils.write(td, mxResources.get('borderWidth') + ':'); + + row.appendChild(td); + + var borderInput = document.createElement('input'); + borderInput.setAttribute('type', 'number'); + borderInput.setAttribute('value', ExportDialog.lastBorderValue); + borderInput.style.width = '180px'; + + td = document.createElement('td'); + td.appendChild(borderInput); + row.appendChild(td); + + tbody.appendChild(row); + table.appendChild(tbody); + + // Handles changes in the export format + function formatChanged() + { + var name = nameInput.value; + var dot = name.lastIndexOf('.'); + + if (dot > 0) + { + nameInput.value = name.substring(0, dot + 1) + imageFormatSelect.value; + } + else + { + nameInput.value = name + '.' + imageFormatSelect.value; + } + + if (imageFormatSelect.value === 'xml') + { + zoomInput.setAttribute('disabled', 'true'); + widthInput.setAttribute('disabled', 'true'); + heightInput.setAttribute('disabled', 'true'); + borderInput.setAttribute('disabled', 'true'); + } + else + { + zoomInput.removeAttribute('disabled'); + widthInput.removeAttribute('disabled'); + heightInput.removeAttribute('disabled'); + borderInput.removeAttribute('disabled'); + } + + if (imageFormatSelect.value === 'png' || imageFormatSelect.value === 'svg') + { + transparentCheckbox.removeAttribute('disabled'); + } + else + { + transparentCheckbox.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(imageFormatSelect, 'change', formatChanged); + formatChanged(); + + function checkValues() + { + if (widthInput.value * heightInput.value > MAX_AREA || widthInput.value <= 0) + { + widthInput.style.backgroundColor = 'red'; + } + else + { + widthInput.style.backgroundColor = ''; + } + + if (widthInput.value * heightInput.value > MAX_AREA || heightInput.value <= 0) + { + heightInput.style.backgroundColor = 'red'; + } + else + { + heightInput.style.backgroundColor = ''; + } + }; + + mxEvent.addListener(zoomInput, 'change', function() + { + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + zoomInput.value = parseFloat((s * 100).toFixed(2)); + + if (width > 0) + { + widthInput.value = Math.floor(width * s); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(widthInput, 'change', function() + { + var s = parseInt(widthInput.value) / width; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + heightInput.value = Math.floor(height * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + mxEvent.addListener(heightInput, 'change', function() + { + var s = parseInt(heightInput.value) / height; + + if (s > 0) + { + zoomInput.value = parseFloat((s * 100).toFixed(2)); + widthInput.value = Math.floor(width * s); + } + else + { + zoomInput.value = '100'; + widthInput.value = width; + heightInput.value = height; + } + + checkValues(); + }); + + row = document.createElement('tr'); + td = document.createElement('td'); + td.setAttribute('align', 'right'); + td.style.paddingTop = '22px'; + td.colSpan = 2; + + var saveBtn = mxUtils.button(mxResources.get('export'), mxUtils.bind(this, function() + { + if (parseInt(zoomInput.value) <= 0) + { + mxUtils.alert(mxResources.get('drawingEmpty')); + } + else + { + var name = nameInput.value; + var format = imageFormatSelect.value; + var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; + var b = Math.max(0, parseInt(borderInput.value)); + var bg = graph.background; + + if ((format == 'svg' || format == 'png') && transparentCheckbox.checked) + { + bg = null; + } + else if (bg == null || bg == mxConstants.NONE) + { + bg = '#ffffff'; + } + + ExportDialog.lastBorderValue = b; + ExportDialog.exportFile(editorUi, name, format, bg, s, b); + } + })); + saveBtn.className = 'geBtn gePrimaryBtn'; + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + td.appendChild(cancelBtn); + td.appendChild(saveBtn); + } + else + { + td.appendChild(saveBtn); + td.appendChild(cancelBtn); + } + + row.appendChild(td); + tbody.appendChild(row); + table.appendChild(tbody); + this.container = table; +}; + +/** + * Remembers last value for border. + */ +ExportDialog.lastBorderValue = 0; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showGifOption = true; + +/** + * Global switches for the export dialog. + */ +ExportDialog.showXmlOption = true; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.exportFile = function(editorUi, name, format, bg, s, b) +{ + var graph = editorUi.editor.graph; + + if (format == 'xml') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(editorUi.editor.getGraphXml()), name, format); + } + else if (format == 'svg') + { + ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(graph.getSvg(bg, s, b)), name, format); + } + else + { + var bounds = graph.getGraphBounds(); + + // New image export + var xmlDoc = mxUtils.createXmlDocument(); + var root = xmlDoc.createElement('output'); + xmlDoc.appendChild(root); + + // Renders graph. Offset will be multiplied with state's scale when painting state. + var xmlCanvas = new mxXmlCanvas2D(root); + xmlCanvas.translate(Math.floor((b / s - bounds.x) / graph.view.scale), + Math.floor((b / s - bounds.y) / graph.view.scale)); + xmlCanvas.scale(s / graph.view.scale); + + var imgExport = new mxImageExport() + imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); + + // Puts request data together + var param = 'xml=' + encodeURIComponent(mxUtils.getXml(root)); + var w = Math.ceil(bounds.width * s / graph.view.scale + 2 * b); + var h = Math.ceil(bounds.height * s / graph.view.scale + 2 * b); + + // Requests image if request is valid + if (param.length <= MAX_REQUEST_SIZE && w * h < MAX_AREA) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(EXPORT_URL, 'format=' + format + + '&filename=' + encodeURIComponent(name) + + '&bg=' + ((bg != null) ? bg : 'none') + + '&w=' + w + '&h=' + h + '&' + param); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + } + } +}; + +/** + * Hook for getting the export format. Returns null for the default + * intermediate XML export format or a function that returns the + * parameter and value to be used in the request in the form + * key=value, where value should be URL encoded. + */ +ExportDialog.saveLocalFile = function(editorUi, data, filename, format) +{ + if (data.length < MAX_REQUEST_SIZE) + { + editorUi.hideDialog(); + var req = new mxXmlRequest(SAVE_URL, 'xml=' + encodeURIComponent(data) + '&filename=' + + encodeURIComponent(filename) + '&format=' + format); + req.simulate(document, '_blank'); + } + else + { + mxUtils.alert(mxResources.get('drawingTooLarge')); + mxUtils.popup(xml); + } +}; + +/** + * Constructs a new metadata dialog. + */ +var EditDataDialog = function(ui, cell) +{ + var div = document.createElement('div'); + var graph = ui.editor.graph; + + var value = graph.getModel().getValue(cell); + + // Converts the value to an XML node + if (!mxUtils.isNode(value)) + { + var doc = mxUtils.createXmlDocument(); + var obj = doc.createElement('object'); + obj.setAttribute('label', value || ''); + value = obj; + } + + // Creates the dialog contents + var form = new mxForm('properties'); + form.table.style.width = '100%'; + + var attrs = value.attributes; + var names = []; + var texts = []; + var count = 0; + + var id = EditDataDialog.getDisplayIdForCell(ui, cell); + + // FIXME: Fix remove button for quirks mode + var addRemoveButton = function(text, name) + { + var wrapper = document.createElement('div'); + wrapper.style.position = 'relative'; + wrapper.style.paddingRight = '20px'; + wrapper.style.boxSizing = 'border-box'; + wrapper.style.width = '100%'; + + var removeAttr = document.createElement('a'); + var img = mxUtils.createImage(Dialog.prototype.closeImage); + img.style.height = '9px'; + img.style.fontSize = '9px'; + img.style.marginBottom = (mxClient.IS_IE11) ? '-1px' : '5px'; + + removeAttr.className = 'geButton'; + removeAttr.setAttribute('title', mxResources.get('delete')); + removeAttr.style.position = 'absolute'; + removeAttr.style.top = '4px'; + removeAttr.style.right = '0px'; + removeAttr.style.margin = '0px'; + removeAttr.style.width = '9px'; + removeAttr.style.height = '9px'; + removeAttr.style.cursor = 'pointer'; + removeAttr.appendChild(img); + + var removeAttrFn = (function(name) + { + return function() + { + var count = 0; + + for (var j = 0; j < names.length; j++) + { + if (names[j] == name) + { + texts[j] = null; + form.table.deleteRow(count + ((id != null) ? 1 : 0)); + + break; + } + + if (texts[j] != null) + { + count++; + } + } + }; + })(name); + + mxEvent.addListener(removeAttr, 'click', removeAttrFn); + + var parent = text.parentNode; + wrapper.appendChild(text); + wrapper.appendChild(removeAttr); + parent.appendChild(wrapper); + }; + + var addTextArea = function(index, name, value) + { + names[index] = name; + texts[index] = form.addTextarea(names[count] + ':', value, 2); + texts[index].style.width = '100%'; + + addRemoveButton(texts[index], name); + }; + + var temp = []; + var isLayer = graph.getModel().getParent(cell) == graph.getModel().getRoot(); + + for (var i = 0; i < attrs.length; i++) + { + if ((isLayer || attrs[i].nodeName != 'label') && attrs[i].nodeName != 'placeholders') + { + temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); + } + } + + // Sorts by name + temp.sort(function(a, b) + { + if (a.name < b.name) + { + return -1; + } + else if (a.name > b.name) + { + return 1; + } + else + { + return 0; + } + }); + + if (id != null) + { + var text = document.createElement('input'); + text.style.width = '280px'; + text.style.textAlign = 'center'; + text.setAttribute('type', 'text'); + text.setAttribute('readOnly', 'true'); + text.setAttribute('value', id); + + form.addField(mxResources.get('id') + ':', text); + } + + for (var i = 0; i < temp.length; i++) + { + addTextArea(count, temp[i].name, temp[i].value); + count++; + } + + var top = document.createElement('div'); + top.style.cssText = 'position:absolute;left:30px;right:30px;overflow-y:auto;top:30px;bottom:80px;'; + top.appendChild(form.table); + + var newProp = document.createElement('div'); + newProp.style.whiteSpace = 'nowrap'; + newProp.style.marginTop = '6px'; + + var nameInput = document.createElement('input'); + nameInput.setAttribute('placeholder', mxResources.get('enterPropertyName')); + nameInput.setAttribute('type', 'text'); + nameInput.setAttribute('size', (mxClient.IS_IE || mxClient.IS_IE11) ? '18' : '22'); + nameInput.style.marginLeft = '2px'; + + newProp.appendChild(nameInput); + top.appendChild(newProp); + div.appendChild(top); + + var addBtn = mxUtils.button(mxResources.get('addProperty'), function() + { + var name = nameInput.value; + + // Avoid ':' in attribute names which seems to be valid in Chrome + if (name.length > 0 && name != 'label' && name != 'placeholders' && name.indexOf(':') < 0) + { + try + { + var idx = mxUtils.indexOf(names, name); + + if (idx >= 0 && texts[idx] != null) + { + texts[idx].focus(); + } + else + { + // Checks if the name is valid + var clone = value.cloneNode(false); + clone.setAttribute(name, ''); + + if (idx >= 0) + { + names.splice(idx, 1); + texts.splice(idx, 1); + } + + names.push(name); + var text = form.addTextarea(name + ':', '', 2); + text.style.width = '100%'; + texts.push(text); + addRemoveButton(text, name); + + text.focus(); + } + + nameInput.value = ''; + } + catch (e) + { + mxUtils.alert(e); + } + } + else + { + mxUtils.alert(mxResources.get('invalidName')); + } + }); + + this.init = function() + { + if (texts.length > 0) + { + texts[0].focus(); + } + else + { + nameInput.focus(); + } + }; + + addBtn.setAttribute('disabled', 'disabled'); + addBtn.style.marginLeft = '10px'; + addBtn.style.width = '144px'; + newProp.appendChild(addBtn); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + ui.hideDialog.apply(ui, arguments); + }); + + cancelBtn.className = 'geBtn'; + + var applyBtn = mxUtils.button(mxResources.get('apply'), function() + { + try + { + ui.hideDialog.apply(ui, arguments); + + // Clones and updates the value + value = value.cloneNode(true); + var removeLabel = false; + + for (var i = 0; i < names.length; i++) + { + if (texts[i] == null) + { + value.removeAttribute(names[i]); + } + else + { + value.setAttribute(names[i], texts[i].value); + removeLabel = removeLabel || (names[i] == 'placeholder' && + value.getAttribute('placeholders') == '1'); + } + } + + // Removes label if placeholder is assigned + if (removeLabel) + { + value.removeAttribute('label'); + } + + // Updates the value of the cell (undoable) + graph.getModel().setValue(cell, value); + } + catch (e) + { + mxUtils.alert(e); + } + }); + applyBtn.className = 'geBtn gePrimaryBtn'; + + function updateAddBtn() + { + if (nameInput.value.length > 0) + { + addBtn.removeAttribute('disabled'); + } + else + { + addBtn.setAttribute('disabled', 'disabled'); + } + }; + + mxEvent.addListener(nameInput, 'keyup', updateAddBtn); + + // Catches all changes that don't fire a keyup (such as paste via mouse) + mxEvent.addListener(nameInput, 'change', updateAddBtn); + + var buttons = document.createElement('div'); + buttons.style.cssText = 'position:absolute;left:30px;right:30px;text-align:right;bottom:30px;height:40px;' + + if (ui.editor.graph.getModel().isVertex(cell) || ui.editor.graph.getModel().isEdge(cell)) + { + var replace = document.createElement('span'); + replace.style.marginRight = '10px'; + var input = document.createElement('input'); + input.setAttribute('type', 'checkbox'); + input.style.marginRight = '6px'; + + if (value.getAttribute('placeholders') == '1') + { + input.setAttribute('checked', 'checked'); + input.defaultChecked = true; + } + + mxEvent.addListener(input, 'click', function() + { + if (value.getAttribute('placeholders') == '1') + { + value.removeAttribute('placeholders'); + } + else + { + value.setAttribute('placeholders', '1'); + } + }); + + replace.appendChild(input); + mxUtils.write(replace, mxResources.get('placeholders')); + + if (EditDataDialog.placeholderHelpLink != null) + { + var link = document.createElement('a'); + link.setAttribute('href', EditDataDialog.placeholderHelpLink); + link.setAttribute('title', mxResources.get('help')); + link.setAttribute('target', '_blank'); + link.style.marginLeft = '10px'; + link.style.cursor = 'help'; + + var icon = document.createElement('img'); + icon.setAttribute('border', '0'); + icon.setAttribute('valign', 'middle'); + icon.style.marginTop = (mxClient.IS_IE11) ? '0px' : '-4px'; + icon.setAttribute('src', Editor.helpImage); + link.appendChild(icon); + + replace.appendChild(link); + } + + buttons.appendChild(replace); + } + + if (ui.editor.cancelFirst) + { + buttons.appendChild(cancelBtn); + buttons.appendChild(applyBtn); + } + else + { + buttons.appendChild(applyBtn); + buttons.appendChild(cancelBtn); + } + + div.appendChild(buttons); + this.container = div; +}; + +/** + * Optional help link. + */ +EditDataDialog.getDisplayIdForCell = function(ui, cell) +{ + var id = null; + + if (ui.editor.graph.getModel().getParent(cell) != null) + { + id = cell.getId(); + } + + return id; +}; + +/** + * Optional help link. + */ +EditDataDialog.placeholderHelpLink = null; + +/** + * Constructs a new link dialog. + */ +var LinkDialog = function(editorUi, initialValue, btnLabel, fn) +{ + var div = document.createElement('div'); + mxUtils.write(div, mxResources.get('editLink') + ':'); + + var inner = document.createElement('div'); + inner.className = 'geTitle'; + inner.style.backgroundColor = 'transparent'; + inner.style.borderColor = 'transparent'; + inner.style.whiteSpace = 'nowrap'; + inner.style.textOverflow = 'clip'; + inner.style.cursor = 'default'; + + if (!mxClient.IS_VML) + { + inner.style.paddingRight = '20px'; + } + + var linkInput = document.createElement('input'); + linkInput.setAttribute('value', initialValue); + linkInput.setAttribute('placeholder', 'http://www.example.com/'); + linkInput.setAttribute('type', 'text'); + linkInput.style.marginTop = '6px'; + linkInput.style.width = '400px'; + linkInput.style.backgroundImage = 'url(\'' + Dialog.prototype.clearImage + '\')'; + linkInput.style.backgroundRepeat = 'no-repeat'; + linkInput.style.backgroundPosition = '100% 50%'; + linkInput.style.paddingRight = '14px'; + + var cross = document.createElement('div'); + cross.setAttribute('title', mxResources.get('reset')); + cross.style.position = 'relative'; + cross.style.left = '-16px'; + cross.style.width = '12px'; + cross.style.height = '14px'; + cross.style.cursor = 'pointer'; + + // Workaround for inline-block not supported in IE + cross.style.display = (mxClient.IS_VML) ? 'inline' : 'inline-block'; + cross.style.top = ((mxClient.IS_VML) ? 0 : 3) + 'px'; + + // Needed to block event transparency in IE + cross.style.background = 'url(' + IMAGE_PATH + '/transparent.gif)'; + + mxEvent.addListener(cross, 'click', function() + { + linkInput.value = ''; + linkInput.focus(); + }); + + inner.appendChild(linkInput); + inner.appendChild(cross); + div.appendChild(inner); + + this.init = function() + { + linkInput.focus(); + + if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) + { + linkInput.select(); + } + else + { + document.execCommand('selectAll', false, null); + } + }; + + var btns = document.createElement('div'); + btns.style.marginTop = '18px'; + btns.style.textAlign = 'right'; + + mxEvent.addListener(linkInput, 'keypress', function(e) + { + if (e.keyCode == 13) + { + editorUi.hideDialog(); + fn(linkInput.value); + } + }); + + var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() + { + editorUi.hideDialog(); + }); + cancelBtn.className = 'geBtn'; + + if (editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + var mainBtn = mxUtils.button(btnLabel, function() + { + editorUi.hideDialog(); + fn(linkInput.value); + }); + mainBtn.className = 'geBtn gePrimaryBtn'; + btns.appendChild(mainBtn); + + if (!editorUi.editor.cancelFirst) + { + btns.appendChild(cancelBtn); + } + + div.appendChild(btns); + + this.container = div; +}; + +/** + * + */ +var OutlineWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.position = 'absolute'; + div.style.width = '100%'; + div.style.height = '100%'; + div.style.border = '1px solid whiteSmoke'; + div.style.overflow = 'hidden'; + + this.window = new mxWindow(mxResources.get('outline'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 80, 80); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + var outline = editorUi.createOutline(this.window); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + outline.destroy(); + } + + this.window.addListener(mxEvent.RESIZE, mxUtils.bind(this, function() + { + outline.update(false); + outline.outline.sizeDidChange(); + })); + + this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function() + { + outline.suspended = false; + outline.outline.refresh(); + outline.update(); + })); + + this.window.addListener(mxEvent.HIDE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + this.window.addListener(mxEvent.NORMALIZE, mxUtils.bind(this, function() + { + outline.suspended = false; + outline.update(); + })); + + this.window.addListener(mxEvent.MINIMIZE, mxUtils.bind(this, function() + { + outline.suspended = true; + })); + + var outlineCreateGraph = outline.createGraph; + outline.createGraph = function(container) + { + var g = outlineCreateGraph.apply(this, arguments); + g.gridEnabled = false; + g.pageScale = graph.pageScale; + g.pageFormat = graph.pageFormat; + g.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; + g.pageVisible = graph.pageVisible; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + return g; + }; + + function update() + { + outline.outline.pageScale = graph.pageScale; + outline.outline.pageFormat = graph.pageFormat; + outline.outline.pageVisible = graph.pageVisible; + outline.outline.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background;; + + var current = mxUtils.getCurrentStyle(graph.container); + div.style.backgroundColor = current.backgroundColor; + + if (graph.view.backgroundPageShape != null && outline.outline.view.backgroundPageShape != null) + { + outline.outline.view.backgroundPageShape.fill = graph.view.backgroundPageShape.fill; + } + + outline.outline.refresh(); + }; + + outline.init(div); + + editorUi.editor.addListener('resetGraphView', update); + editorUi.addListener('pageFormatChanged', update); + editorUi.addListener('backgroundColorChanged', update); + editorUi.addListener('backgroundImageChanged', update); + editorUi.addListener('pageViewChanged', function() + { + update(); + outline.update(true); + }); + + if (outline.outline.dialect == mxConstants.DIALECT_SVG) + { + var zoomInAction = editorUi.actions.get('zoomIn'); + var zoomOutAction = editorUi.actions.get('zoomOut'); + + mxEvent.addMouseWheelListener(function(evt, up) + { + var outlineWheel = false; + var source = mxEvent.getSource(evt); + + while (source != null) + { + if (source == outline.outline.view.canvas.ownerSVGElement) + { + outlineWheel = true; + break; + } + + source = source.parentNode; + } + + if (outlineWheel) + { + if (up) + { + zoomInAction.funct(); + } + else + { + zoomOutAction.funct(); + } + + mxEvent.consume(evt); + } + }); + } +}; + +/** + * + */ +var LayersWindow = function(editorUi, x, y, w, h) +{ + var graph = editorUi.editor.graph; + + var div = document.createElement('div'); + div.style.userSelect = 'none'; + div.style.background = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + div.style.border = '1px solid whiteSmoke'; + div.style.height = '100%'; + div.style.marginBottom = '10px'; + div.style.overflow = 'auto'; + + var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px'; + + var listDiv = document.createElement('div') + listDiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? '#dcdcdc' : '#e5e5e5'; + listDiv.style.position = 'absolute'; + listDiv.style.overflow = 'auto'; + listDiv.style.left = '0px'; + listDiv.style.right = '0px'; + listDiv.style.top = '0px'; + listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px'; + div.appendChild(listDiv); + + var dragSource = null; + var dropIndex = null; + + mxEvent.addListener(div, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = 0; + evt.stopPropagation(); + evt.preventDefault(); + }); + + // Workaround for "no element found" error in FF + mxEvent.addListener(div, 'drop', function(evt) + { + evt.stopPropagation(); + evt.preventDefault(); + }); + + var layerCount = null; + var selectionLayer = null; + + var ldiv = document.createElement('div'); + + ldiv.className = 'geToolbarContainer'; + ldiv.style.position = 'absolute'; + ldiv.style.bottom = '0px'; + ldiv.style.left = '0px'; + ldiv.style.right = '0px'; + ldiv.style.height = tbarHeight; + ldiv.style.overflow = 'hidden'; + ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px'; + ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; + ldiv.style.borderWidth = '1px 0px 0px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.display = 'block'; + ldiv.style.whiteSpace = 'nowrap'; + + if (mxClient.IS_QUIRKS) + { + ldiv.style.filter = 'none'; + } + + var link = document.createElement('a'); + link.className = 'geButton'; + + if (mxClient.IS_QUIRKS) + { + link.style.filter = 'none'; + } + + var removeLink = link.cloneNode(); + removeLink.innerHTML = '
'; + + mxEvent.addListener(removeLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + try + { + var index = graph.model.root.getIndex(selectionLayer); + graph.removeCells([selectionLayer], false); + + // Creates default layer if no layer exists + if (graph.model.getChildCount(graph.model.root) == 0) + { + graph.model.add(graph.model.root, new mxCell()); + graph.setDefaultParent(null); + } + else if (index > 0 && index <= graph.model.getChildCount(graph.model.root)) + { + graph.setDefaultParent(graph.model.getChildAt(graph.model.root, index - 1)); + } + else + { + graph.setDefaultParent(null); + } + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + removeLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(removeLink); + + var insertLink = link.cloneNode(); + insertLink.innerHTML = '
'; + + mxEvent.addListener(insertLink, 'click', function(evt) + { + if (graph.isEnabled() && !graph.isSelectionEmpty()) + { + graph.moveCells(graph.getSelectionCells(), 0, 0, false, selectionLayer); + } + }); + + ldiv.appendChild(insertLink); + + var dataLink = link.cloneNode(); + dataLink.innerHTML = '
'; + dataLink.setAttribute('title', mxResources.get('rename')); + + mxEvent.addListener(dataLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + editorUi.showDataDialog(selectionLayer); + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + dataLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(dataLink); + + function renameLayer(layer) + { + if (graph.isEnabled() && layer != null) + { + var label = graph.convertValueToString(layer); + var dlg = new FilenameDialog(editorUi, label || mxResources.get('background'), mxResources.get('rename'), mxUtils.bind(this, function(newValue) + { + if (newValue != null) + { + graph.cellLabelChanged(layer, newValue); + } + }), mxResources.get('enterName')); + editorUi.showDialog(dlg.container, 300, 100, true, true); + dlg.init(); + } + }; + + var duplicateLink = link.cloneNode(); + duplicateLink.innerHTML = '
'; + + mxEvent.addListener(duplicateLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + var newCell = null; + graph.model.beginUpdate(); + try + { + newCell = graph.cloneCells([selectionLayer])[0]; + graph.cellLabelChanged(newCell, mxResources.get('untitledLayer')); + newCell.setVisible(true); + newCell = graph.addCell(newCell, graph.model.root); + graph.setDefaultParent(newCell); + } + finally + { + graph.model.endUpdate(); + } + + if (newCell != null && !graph.isCellLocked(newCell)) + { + graph.selectAll(newCell); + } + } + }); + + if (!graph.isEnabled()) + { + duplicateLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(duplicateLink); + + var addLink = link.cloneNode(); + addLink.innerHTML = '
'; + addLink.setAttribute('title', mxResources.get('addLayer')); + + mxEvent.addListener(addLink, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.model.beginUpdate(); + + try + { + var cell = graph.addCell(new mxCell(mxResources.get('untitledLayer')), graph.model.root); + graph.setDefaultParent(cell); + } + finally + { + graph.model.endUpdate(); + } + } + + mxEvent.consume(evt); + }); + + if (!graph.isEnabled()) + { + addLink.className = 'geButton mxDisabled'; + } + + ldiv.appendChild(addLink); + + div.appendChild(ldiv); + + function refresh() + { + layerCount = graph.model.getChildCount(graph.model.root) + listDiv.innerHTML = ''; + + function addLayer(index, label, child, defaultParent) + { + var ldiv = document.createElement('div'); + ldiv.className = 'geToolbarContainer'; + + ldiv.style.overflow = 'hidden'; + ldiv.style.position = 'relative'; + ldiv.style.padding = '4px'; + ldiv.style.height = '22px'; + ldiv.style.display = 'block'; + ldiv.style.backgroundColor = 'whiteSmoke'; + ldiv.style.borderWidth = '0px 0px 1px 0px'; + ldiv.style.borderColor = '#c3c3c3'; + ldiv.style.borderStyle = 'solid'; + ldiv.style.whiteSpace = 'nowrap'; + ldiv.setAttribute('title', label); + + var left = document.createElement('div'); + left.style.display = 'inline-block'; + left.style.width = '100%'; + left.style.textOverflow = 'ellipsis'; + left.style.overflow = 'hidden'; + + mxEvent.addListener(ldiv, 'dragover', function(evt) + { + evt.dataTransfer.dropEffect = 'move'; + dropIndex = index; + evt.stopPropagation(); + evt.preventDefault(); + }); + + mxEvent.addListener(ldiv, 'dragstart', function(evt) + { + dragSource = ldiv; + + // Workaround for no DnD on DIV in FF + if (mxClient.IS_FF) + { + // LATER: Check what triggers a parse as XML on this in FF after drop + evt.dataTransfer.setData('Text', ''); + } + }); + + mxEvent.addListener(ldiv, 'dragend', function(evt) + { + if (dragSource != null && dropIndex != null) + { + graph.addCell(child, graph.model.root, dropIndex); + } + + dragSource = null; + dropIndex = null; + evt.stopPropagation(); + evt.preventDefault(); + }); + + var btn = document.createElement('img'); + btn.setAttribute('draggable', 'false'); + btn.setAttribute('align', 'top'); + btn.setAttribute('border', '0'); + btn.style.padding = '4px'; + btn.setAttribute('title', mxResources.get('lockUnlock')); + + var state = graph.view.getState(child); + var style = (state != null) ? state.style : graph.getCellStyle(child); + + if (mxUtils.getValue(style, 'locked', '0') == '1') + { + btn.setAttribute('src', Dialog.prototype.lockedImage); + } + else + { + btn.setAttribute('src', Dialog.prototype.unlockedImage); + } + + if (graph.isEnabled()) + { + btn.style.cursor = 'pointer'; + } + + mxEvent.addListener(btn, 'click', function(evt) + { + if (graph.isEnabled()) + { + var value = null; + + graph.getModel().beginUpdate(); + try + { + value = (mxUtils.getValue(style, 'locked', '0') == '1') ? null : '1'; + graph.setCellStyles('locked', value, [child]); + } + finally + { + graph.getModel().endUpdate(); + } + + if (value == '1') + { + graph.removeSelectionCells(graph.getModel().getDescendants(child)); + } + + mxEvent.consume(evt); + } + }); + + left.appendChild(btn); + + var inp = document.createElement('input'); + inp.setAttribute('type', 'checkbox'); + inp.setAttribute('title', mxResources.get('hideIt', [child.value || mxResources.get('background')])); + inp.style.marginLeft = '4px'; + inp.style.marginRight = '6px'; + inp.style.marginTop = '4px'; + left.appendChild(inp); + + if (graph.model.isVisible(child)) + { + inp.setAttribute('checked', 'checked'); + inp.defaultChecked = true; + } + + mxEvent.addListener(inp, 'click', function(evt) + { + graph.model.setVisible(child, !graph.model.isVisible(child)); + mxEvent.consume(evt); + }); + + mxUtils.write(left, label); + ldiv.appendChild(left); + + if (graph.isEnabled()) + { + // Fallback if no drag and drop is available + if (mxClient.IS_TOUCH || mxClient.IS_POINTER || mxClient.IS_VML || + (mxClient.IS_IE && document.documentMode < 10)) + { + var right = document.createElement('div'); + right.style.display = 'block'; + right.style.textAlign = 'right'; + right.style.whiteSpace = 'nowrap'; + right.style.position = 'absolute'; + right.style.right = '6px'; + right.style.top = '6px'; + + // Poor man's change layer order + if (index > 0) + { + var img2 = document.createElement('a'); + + img2.setAttribute('title', mxResources.get('toBack')); + + img2.className = 'geButton'; + img2.style.cssFloat = 'none'; + img2.innerHTML = '▼'; + img2.style.width = '14px'; + img2.style.height = '14px'; + img2.style.fontSize = '14px'; + img2.style.margin = '0px'; + img2.style.marginTop = '-1px'; + right.appendChild(img2); + + mxEvent.addListener(img2, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index - 1); + } + + mxEvent.consume(evt); + }); + } + + if (index >= 0 && index < layerCount - 1) + { + var img1 = document.createElement('a'); + + img1.setAttribute('title', mxResources.get('toFront')); + + img1.className = 'geButton'; + img1.style.cssFloat = 'none'; + img1.innerHTML = '▲'; + img1.style.width = '14px'; + img1.style.height = '14px'; + img1.style.fontSize = '14px'; + img1.style.margin = '0px'; + img1.style.marginTop = '-1px'; + right.appendChild(img1); + + mxEvent.addListener(img1, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.addCell(child, graph.model.root, index + 1); + } + + mxEvent.consume(evt); + }); + } + + ldiv.appendChild(right); + } + + if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10)) + { + ldiv.setAttribute('draggable', 'true'); + ldiv.style.cursor = 'move'; + } + } + + mxEvent.addListener(ldiv, 'dblclick', function(evt) + { + var nodeName = mxEvent.getSource(evt).nodeName; + + if (nodeName != 'INPUT' && nodeName != 'IMG') + { + renameLayer(child); + mxEvent.consume(evt); + } + }); + + if (graph.getDefaultParent() == child) + { + ldiv.style.background = '#e6eff8'; + ldiv.style.fontWeight = (graph.isEnabled()) ? 'bold' : ''; + selectionLayer = child; + } + else + { + mxEvent.addListener(ldiv, 'click', function(evt) + { + if (graph.isEnabled()) + { + graph.setDefaultParent(defaultParent); + graph.view.setCurrentRoot(null); + refresh(); + } + }); + } + + listDiv.appendChild(ldiv); + }; + + // Cannot be moved or deleted + for (var i = layerCount - 1; i >= 0; i--) + { + (mxUtils.bind(this, function(child) + { + addLayer(i, graph.convertValueToString(child) || + mxResources.get('background'), child, child); + }))(graph.model.getChildAt(graph.model.root, i)); + } + + var label = graph.convertValueToString(selectionLayer) || mxResources.get('background'); + removeLink.setAttribute('title', mxResources.get('removeIt', [label])); + insertLink.setAttribute('title', mxResources.get('moveSelectionTo', [label])); + duplicateLink.setAttribute('title', mxResources.get('duplicateIt', [label])); + dataLink.setAttribute('title', mxResources.get('editData')); + + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + }; + + refresh(); + graph.model.addListener(mxEvent.CHANGE, function() + { + refresh(); + }); + + graph.selectionModel.addListener(mxEvent.CHANGE, function() + { + if (graph.isSelectionEmpty()) + { + insertLink.className = 'geButton mxDisabled'; + } + else + { + insertLink.className = 'geButton'; + } + }); + + this.window = new mxWindow(mxResources.get('layers'), div, x, y, w, h, true, true); + this.window.minimumSize = new mxRectangle(0, 0, 120, 120); + this.window.destroyOnClose = false; + this.window.setMaximizable(false); + this.window.setResizable(true); + this.window.setClosable(true); + this.window.setVisible(true); + + // Make refresh available via instance + this.refreshLayers = refresh; + + this.window.setLocation = function(x, y) + { + var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; + var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; + + x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); + y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); + + if (this.getX() != x || this.getY() != y) + { + mxWindow.prototype.setLocation.apply(this, arguments); + } + }; + + var resizeListener = mxUtils.bind(this, function() + { + var x = this.window.getX(); + var y = this.window.getY(); + + this.window.setLocation(x, y); + }); + + mxEvent.addListener(window, 'resize', resizeListener); + + this.destroy = function() + { + mxEvent.removeListener(window, 'resize', resizeListener); + this.window.destroy(); + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Format.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Format.js new file mode 100644 index 00000000..981272f1 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/Format.js @@ -0,0 +1,5511 @@ +/** + * Copyright (c) 2006-2012, JGraph Ltd + */ +Format = function(editorUi, container) +{ + this.editorUi = editorUi; + this.container = container; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.labelIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.currentIndex = 0; + +/** + * Returns information about the current selection. + */ +Format.prototype.showCloseButton = true; + +/** + * Background color for inactive tabs. + */ +Format.prototype.inactiveTabBackgroundColor = '#d7d7d7'; + +/** + * Background color for inactive tabs. + */ +Format.prototype.roundableShapes = ['label', 'rectangle', 'internalStorage', 'corner', + 'parallelogram', 'swimlane', 'triangle', 'trapezoid', + 'ext', 'step', 'tee', 'process', 'link', + 'rhombus', 'offPageConnector', 'loopLimit', 'hexagon', + 'manualInput', 'curlyBracket', 'singleArrow', 'callout', + 'doubleArrow', 'flexArrow', 'card', 'umlLifeline']; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + this.update = mxUtils.bind(this, function(sender, evt) + { + this.clearSelectionState(); + this.refresh(); + }); + + graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.EDITING_STARTED, this.update); + graph.addListener(mxEvent.EDITING_STOPPED, this.update); + graph.getModel().addListener(mxEvent.CHANGE, this.update); + graph.addListener(mxEvent.ROOT, mxUtils.bind(this, function() + { + this.refresh(); + })); + + editor.addListener('autosaveChanged', mxUtils.bind(this, function() + { + this.refresh(); + })); + + this.refresh(); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.clearSelectionState = function() +{ + this.selectionState = null; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.getSelectionState = function() +{ + if (this.selectionState == null) + { + this.selectionState = this.createSelectionState(); + } + + return this.selectionState; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.createSelectionState = function() +{ + var cells = this.editorUi.editor.graph.getSelectionCells(); + var result = this.initSelectionState(); + + for (var i = 0; i < cells.length; i++) + { + this.updateSelectionStateForCell(result, cells[i], cells); + } + + return result; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.initSelectionState = function() +{ + return {vertices: [], edges: [], x: null, y: null, width: null, height: null, style: {}, + containsImage: false, containsLabel: false, fill: true, glass: true, rounded: true, + comic: true, autoSize: false, image: true, shadow: true, lineJumps: true}; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.updateSelectionStateForCell = function(result, cell, cells) +{ + var graph = this.editorUi.editor.graph; + + if (graph.getModel().isVertex(cell)) + { + result.vertices.push(cell); + var geo = graph.getCellGeometry(cell); + + if (geo != null) + { + if (geo.width > 0) + { + if (result.width == null) + { + result.width = geo.width; + } + else if (result.width != geo.width) + { + result.width = ''; + } + } + else + { + result.containsLabel = true; + } + + if (geo.height > 0) + { + if (result.height == null) + { + result.height = geo.height; + } + else if (result.height != geo.height) + { + result.height = ''; + } + } + else + { + result.containsLabel = true; + } + + if (!geo.relative || geo.offset != null) + { + var x = (geo.relative) ? geo.offset.x : geo.x; + var y = (geo.relative) ? geo.offset.y : geo.y; + + if (result.x == null) + { + result.x = x; + } + else if (result.x != x) + { + result.x = ''; + } + + if (result.y == null) + { + result.y = y; + } + else if (result.y != y) + { + result.y = ''; + } + } + } + } + else if (graph.getModel().isEdge(cell)) + { + result.edges.push(cell); + } + + var state = graph.view.getState(cell); + + if (state != null) + { + result.autoSize = result.autoSize || this.isAutoSizeState(state); + result.glass = result.glass && this.isGlassState(state); + result.rounded = result.rounded && this.isRoundedState(state); + result.lineJumps = result.lineJumps && this.isLineJumpState(state); + result.comic = result.comic && this.isComicState(state); + result.image = result.image && this.isImageState(state); + result.shadow = result.shadow && this.isShadowState(state); + result.fill = result.fill && this.isFillState(state); + + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + result.containsImage = result.containsImage || shape == 'image'; + + for (var key in state.style) + { + var value = state.style[key]; + + if (value != null) + { + if (result.style[key] == null) + { + result.style[key] = value; + } + else if (result.style[key] != value) + { + result.style[key] = ''; + } + } + } + } +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isFillState = function(state) +{ + return state.view.graph.model.isVertex(state.cell) || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'arrow' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'filledEdge' || + mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'flexArrow'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isGlassState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'rectangle' || shape == 'internalStorage' || + shape == 'ext' || shape == 'umlLifeline' || shape == 'swimlane' || + shape == 'process'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isRoundedState = function(state) +{ + return (state.shape != null) ? state.shape.isRoundable() : + mxUtils.indexOf(this.roundableShapes, mxUtils.getValue(state.style, + mxConstants.STYLE_SHAPE, null)) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isLineJumpState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + var curved = mxUtils.getValue(state.style, mxConstants.STYLE_CURVED, false); + + return !curved && (shape == 'connector' || shape == 'filledEdge'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isComicState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return mxUtils.indexOf(['label', 'rectangle', 'internalStorage', 'corner', 'parallelogram', 'note', 'collate', + 'swimlane', 'triangle', 'trapezoid', 'ext', 'step', 'tee', 'process', 'link', 'rhombus', + 'offPageConnector', 'loopLimit', 'hexagon', 'manualInput', 'singleArrow', 'doubleArrow', + 'flexArrow', 'filledEdge', 'card', 'umlLifeline', 'connector', 'folder', 'component', 'sortShape', + 'cross', 'umlFrame', 'cube', 'isoCube', 'isoRectangle', 'partialRectangle'], shape) >= 0; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isAutoSizeState = function(state) +{ + return mxUtils.getValue(state.style, mxConstants.STYLE_AUTOSIZE, null) == '1'; +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isImageState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape == 'label' || shape == 'image'); +}; + +/** + * Returns information about the current selection. + */ +Format.prototype.isShadowState = function(state) +{ + var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + return (shape != 'image'); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.clear = function() +{ + this.container.innerHTML = ''; + + // Destroy existing panels + if (this.panels != null) + { + for (var i = 0; i < this.panels.length; i++) + { + this.panels[i].destroy(); + } + } + + this.panels = []; +}; + + +Format.prototype.getTabs =function(){ + return +} + +//begin add by wsp================================================================== +/** + * 获取格式化面板包含的格式化 tab 数组 + */ +Format.prototype.getDiagramFormaters =function(){ + var result =[]; + var index =0; + var ui = this.editorUi; + var graph = ui.editor.graph; + if (graph.isSelectionEmpty()){ + result[index++] ='diagram'; + }else if(graph.isEditing()){ + result[index++] ='text'; + }else{ + //是否是正在编辑连线上的文本 + var containsLabel = this.getSelectionState().containsLabel; + if(containsLabel){ + result[index++] ='text'; + result[index++] ='arrange'; + }else{ + result[index++] ='style'; + result[index++] ='text'; + result[index++] ='arrange'; + } + } + return result; +} + +/** + * 创建 tab 容器 Div + */ +Format.prototype.createTabContainerlDiv =function(){ + var div = document.createElement('div'); + div.style.whiteSpace = 'nowrap'; + div.style.color = 'rgb(112, 112, 112)'; + div.style.textAlign = 'left'; + div.style.cursor = 'default'; + this.container.appendChild(div); + return div; +} + +/** + * 创建 tab 项 Div + */ +Format.prototype.createTabItemLabelDiv =function(){ + var label = document.createElement('div'); + label.style.border = '1px solid #c0c0c0'; + label.style.borderWidth = '0px 0px 1px 0px'; + label.style.textAlign = 'center'; + label.style.fontWeight = 'bold'; + label.style.overflow = 'hidden'; + label.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + label.style.paddingTop = '8px'; + label.style.height = (mxClient.IS_QUIRKS) ? '34px' : '25px'; + return label; +} + +/** + * 创建 tab panel Div + */ +Format.prototype.createTabPanelDiv =function(){ + var div = document.createElement('div'); + div.style.whiteSpace = 'nowrap'; + div.style.color = 'rgb(112, 112, 112)'; + div.style.textAlign = 'left'; + div.style.cursor = 'default'; + this.container.appendChild(div); + return div; +} +//end add by wsp================================================================== + +/** + * Adds the label menu items to the given menu and parent. + */ +Format.prototype.refresh = function() +{ + if (this.container.style.width == '0px'){ + return; + } + + this.clear(); + + var ui = this.editorUi; + var graph = ui.editor.graph; + + var tabContainerlDiv =this.createTabContainerlDiv(); + this.container.appendChild(tabContainerlDiv); + + var tabs =this.getDiagramFormaters(); + if(tabs){ + var containsLabel = this.getSelectionState().containsLabel; + var currentLabel = null; + var currentPanel = null; + + var addClickHandler = mxUtils.bind(this, function(elt, panel, index) + { + var clickHandler = mxUtils.bind(this, function(evt) + { + if (currentLabel != elt) + { + if (containsLabel) + { + this.labelIndex = index; + } + else + { + this.currentIndex = index; + } + + if (currentLabel != null) + { + currentLabel.style.backgroundColor = this.inactiveTabBackgroundColor; + currentLabel.style.borderBottomWidth = '1px'; + } + + currentLabel = elt; + currentLabel.style.backgroundColor = ''; + currentLabel.style.borderBottomWidth = '0px'; + + if (currentPanel != panel) + { + if (currentPanel != null) + { + currentPanel.style.display = 'none'; + } + + currentPanel = panel; + currentPanel.style.display = ''; + } + } + }); + + mxEvent.addListener(elt, 'click', clickHandler); + + if (index == ((containsLabel) ? this.labelIndex : this.currentIndex)) + { + // Invokes handler directly as a workaround for no click on DIV in KHTML. + clickHandler(); + } + }); + + var tabWidth =100/tabs.length + '%'; + var isMultiTab =tabs.length>1; + for(var i=0;i0){ + label.style.borderLeftWidth = '1px'; + } + }else{ + label.style.borderLeftWidth = '0px'; + } + + var panel =null; + + if(tab=='configure'){ + mxUtils.write(label, mxResources.get(tab)); + panel =new ConfigureFormatPanel(this, ui, panelDiv); + }else if(tab=='diagram'){ + mxUtils.write(label, mxResources.get(tab)); + panel =new DiagramFormatPanel(this, ui, panelDiv); + }else if(tab=='text'){ + mxUtils.write(label, mxResources.get(tab)); + panel =new TextFormatPanel(this, ui, panelDiv); + }else if(tab=='style'){ + mxUtils.write(label, mxResources.get(tab)); + panel =new StyleFormatPanel(this, ui, panelDiv); + }else if(tab=='arrange'){ + mxUtils.write(label, mxResources.get(tab)); + panel =new ArrangePanel(this, ui, panelDiv); + } + + tabContainerlDiv.appendChild(label); + this.panels.push(panel); + this.container.appendChild(panelDiv); + + if(tabs.length>1){ + addClickHandler(label, panelDiv, i); + } + } + } +}; + +/** + * Base class for format panels. + */ +BaseFormatPanel = function(format, editorUi, container) +{ + this.format = format; + this.editorUi = editorUi; + this.container = container; + this.listeners = []; +}; + +/** + * + */ +BaseFormatPanel.prototype.buttonBackgroundColor = 'white'; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.getSelectionState = function() +{ + var graph = this.editorUi.editor.graph; + var cells = graph.getSelectionCells(); + var shape = null; + + for (var i = 0; i < cells.length; i++) + { + var state = graph.view.getState(cells[i]); + + if (state != null) + { + var tmp = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); + + if (tmp != null) + { + if (shape == null) + { + shape = tmp; + } + else if (shape != tmp) + { + return null; + } + } + + } + } + + return shape; +}; + +/** + * Install input handler. + */ +BaseFormatPanel.prototype.installInputHandler = function(input, key, defaultValue, min, max, unit, textEditFallback, isFloat) +{ + unit = (unit != null) ? unit : ''; + isFloat = (isFloat != null) ? isFloat : false; + + var ui = this.editorUi; + var graph = ui.editor.graph; + + min = (min != null) ? min : 1; + max = (max != null) ? max : 999; + + var selState = null; + var updating = false; + + var update = mxUtils.bind(this, function(evt) + { + var value = (isFloat) ? parseFloat(input.value) : parseInt(input.value); + + // Special case: angle mod 360 + if (!isNaN(value) && key == mxConstants.STYLE_ROTATION) + { + // Workaround for decimal rounding errors in floats is to + // use integer and round all numbers to two decimal point + value = mxUtils.mod(Math.round(value * 100), 36000) / 100; + } + + value = Math.min(max, Math.max(min, (isNaN(value)) ? defaultValue : value)); + + if (graph.cellEditor.isContentEditing() && textEditFallback) + { + if (!updating) + { + updating = true; + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + textEditFallback(value); + input.value = value + unit; + + // Restore focus and selection in input + updating = false; + } + } + else if (value != mxUtils.getValue(this.format.getSelectionState().style, key, defaultValue)) + { + if (graph.isEditing()) + { + graph.stopEditing(true); + } + + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles(key, value, graph.getSelectionCells()); + + // Handles special case for fontSize where HTML labels are parsed and updated + if (key == mxConstants.STYLE_FONTSIZE) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.fontSize = value + 'px'; + elt.removeAttribute('size'); + }); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + + input.value = value + unit; + mxEvent.consume(evt); + }); + + if (textEditFallback && graph.cellEditor.isContentEditing()) + { + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + } + + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'blur', update); + + return update; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createPanel = function() +{ + var div = document.createElement('div'); + div.style.padding = '12px 0px 12px 18px'; + div.style.borderBottom = '1px solid #c0c0c0'; + + return div; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createTitle = function(title) +{ + var div = document.createElement('div'); + div.style.padding = '0px 0px 6px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.fontWeight = 'bold'; + mxUtils.write(div, title); + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createStepper = function(input, update, step, height, disableFocus, defaultValue) +{ + step = (step != null) ? step : 1; + height = (height != null) ? height : 8; + + if (mxClient.IS_QUIRKS) + { + height = height - 2; + } + else if (mxClient.IS_MT || document.documentMode >= 8) + { + height = height + 1; + } + + var stepper = document.createElement('div'); + mxUtils.setPrefixedStyle(stepper.style, 'borderRadius', '3px'); + stepper.style.border = '1px solid rgb(192, 192, 192)'; + stepper.style.position = 'absolute'; + + var up = document.createElement('div'); + up.style.borderBottom = '1px solid rgb(192, 192, 192)'; + up.style.position = 'relative'; + up.style.height = height + 'px'; + up.style.width = '10px'; + up.className = 'geBtnUp'; + stepper.appendChild(up); + + var down = up.cloneNode(false); + down.style.border = 'none'; + down.style.height = height + 'px'; + down.className = 'geBtnDown'; + stepper.appendChild(down); + + mxEvent.addListener(down, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '2'; + } + + var val = parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val - step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + mxEvent.addListener(up, 'click', function(evt) + { + if (input.value == '') + { + input.value = defaultValue || '0'; + } + + var val = parseInt(input.value); + + if (!isNaN(val)) + { + input.value = val + step; + + if (update != null) + { + update(evt); + } + } + + mxEvent.consume(evt); + }); + + // Disables transfer of focus to DIV but also :active CSS + // so it's only used for fontSize where the focus should + // stay on the selected text, but not for any other input. + if (disableFocus) + { + var currentSelection = null; + + mxEvent.addGestureListeners(stepper, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + currentSelection = document.selection.createRange(); + } + + mxEvent.consume(evt); + }, + null, + function(evt) + { + // Workaround for lost current selection in page because of focus in IE + if (currentSelection != null) + { + try + { + currentSelection.select(); + } + catch (e) + { + // ignore + } + + currentSelection = null; + mxEvent.consume(evt); + } + } + ); + } + + return stepper; +}; + +/** + * Adds the given option. + */ +BaseFormatPanel.prototype.createOption = function(label, isCheckedFn, setCheckedFn, listener) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + div.appendChild(cb); + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var applying = false; + var value = isCheckedFn(); + + var apply = function(newValue) + { + if (!applying) + { + applying = true; + + if (newValue) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + if (value != newValue) + { + value = newValue; + + // Checks if the color value needs to be updated in the model + if (isCheckedFn() != value) + { + setCheckedFn(value); + } + } + + applying = false; + } + }; + + mxEvent.addListener(div, 'click', function(evt) + { + if (cb.getAttribute('disabled') != 'disabled') + { + // Toggles checkbox state for click on label + var source = mxEvent.getSource(evt); + + if (source == div || source == span) + { + cb.checked = !cb.checked; + } + + apply(cb.checked); + } + }); + + apply(value); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * The string 'null' means use null in values. + */ +BaseFormatPanel.prototype.createCellOption = function(label, key, defaultValue, enabledValue, disabledValue, fn, action, stopEditing) +{ + enabledValue = (enabledValue != null) ? ((enabledValue == 'null') ? null : enabledValue) : '1'; + disabledValue = (disabledValue != null) ? ((disabledValue == 'null') ? null : disabledValue) : '0'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, key, defaultValue) != disabledValue; + } + + return null; + }, function(checked) + { + if (stopEditing) + { + graph.stopEditing(); + } + + if (action != null) + { + action.funct(); + } + else + { + graph.getModel().beginUpdate(); + try + { + var value = (checked) ? enabledValue : disabledValue; + graph.setCellStyles(key, value, graph.getSelectionCells()); + + if (fn != null) + { + fn(graph.getSelectionCells(), value); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + } + }, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, key, defaultValue) != disabledValue); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }); +}; + +/** + * Adds the given color option. + */ +BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setColorFn, defaultColor, listener, callbackFn, hideCheckbox) +{ + var div = document.createElement('div'); + div.style.padding = '6px 0px 1px 0px'; + div.style.whiteSpace = 'nowrap'; + div.style.overflow = 'hidden'; + div.style.width = '200px'; + div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; + + var cb = document.createElement('input'); + cb.setAttribute('type', 'checkbox'); + cb.style.margin = '0px 6px 0px 0px'; + + if (!hideCheckbox) + { + div.appendChild(cb); + } + + var span = document.createElement('span'); + mxUtils.write(span, label); + div.appendChild(span); + + var applying = false; + var value = getColorFn(); + + var btn = null; + + var apply = function(color, disableUpdate, forceUpdate) + { + if (!applying) + { + applying = true; + btn.innerHTML = '
'; + + // Fine-tuning in Firefox, quirks mode and IE8 standards + if (mxClient.IS_QUIRKS || document.documentMode == 8) + { + btn.firstChild.style.margin = '0px'; + } + + if (color != null && color != mxConstants.NONE) + { + cb.setAttribute('checked', 'checked'); + cb.defaultChecked = true; + cb.checked = true; + } + else + { + cb.removeAttribute('checked'); + cb.defaultChecked = false; + cb.checked = false; + } + + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + + if (callbackFn != null) + { + callbackFn(color); + } + + if (!disableUpdate) + { + value = color; + + // Checks if the color value needs to be updated in the model + if (forceUpdate || hideCheckbox || getColorFn() != value) + { + setColorFn(value); + } + } + + applying = false; + } + }; + + btn = mxUtils.button('', mxUtils.bind(this, function(evt) + { + this.editorUi.pickColor(value, function(color) + { + apply(color, null, true); + }); + mxEvent.consume(evt); + })); + + btn.style.position = 'absolute'; + btn.style.marginTop = '-4px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '0px' : '20px'; + btn.style.height = '22px'; + btn.className = 'geColorBtn'; + btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; + div.appendChild(btn); + + mxEvent.addListener(div, 'click', function(evt) + { + var source = mxEvent.getSource(evt); + + if (source == cb || source.nodeName != 'INPUT') + { + // Toggles checkbox state for click on label + if (source != cb) + { + cb.checked = !cb.checked; + } + + // Overrides default value with current value to make it easier + // to restore previous value if the checkbox is clicked twice + if (!cb.checked && value != null && value != mxConstants.NONE && + defaultColor != mxConstants.NONE) + { + defaultColor = value; + } + + apply((cb.checked) ? defaultColor : mxConstants.NONE); + } + }); + + apply(value, true); + + if (listener != null) + { + listener.install(apply); + this.listeners.push(listener); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.createCellColorOption = function(label, colorKey, defaultColor, callbackFn, setStyleFn) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + return this.createColorOption(label, function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + return mxUtils.getValue(state.style, colorKey, null); + } + + return null; + }, function(color) + { + graph.getModel().beginUpdate(); + try + { + if (setStyleFn != null) + { + setStyleFn(color); + } + + graph.setCellStyles(colorKey, color, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [colorKey], + 'values', [color], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + }, defaultColor || mxConstants.NONE, + { + install: function(apply) + { + this.listener = function() + { + // Seems to be null sometimes, not sure why... + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null) + { + apply(mxUtils.getValue(state.style, colorKey, null)); + } + }; + + graph.getModel().addListener(mxEvent.CHANGE, this.listener); + }, + destroy: function() + { + graph.getModel().removeListener(this.listener); + } + }, callbackFn); +}; + +/** + * + */ +BaseFormatPanel.prototype.addArrow = function(elt, height) +{ + height = (height != null) ? height : 10; + + var arrow = document.createElement('div'); + arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + arrow.style.padding = '6px'; + arrow.style.paddingRight = '4px'; + + var m = (10 - height); + + if (m == 2) + { + arrow.style.paddingTop = 6 + 'px'; + } + else if (m > 0) + { + arrow.style.paddingTop = (6 - m) + 'px'; + } + else + { + arrow.style.marginTop = '-2px'; + } + + arrow.style.height = height + 'px'; + arrow.style.borderLeft = '1px solid #a0a0a0'; + arrow.innerHTML = ''; + mxUtils.setOpacity(arrow, 70); + + var symbol = elt.getElementsByTagName('div')[0]; + + if (symbol != null) + { + symbol.style.paddingRight = '6px'; + symbol.style.marginLeft = '4px'; + symbol.style.marginTop = '-1px'; + symbol.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; + mxUtils.setOpacity(symbol, 60); + } + + mxUtils.setOpacity(elt, 100); + elt.style.border = '1px solid #a0a0a0'; + elt.style.backgroundColor = this.buttonBackgroundColor; + elt.style.backgroundImage = 'none'; + elt.style.width = 'auto'; + elt.className += ' geColorBtn'; + mxUtils.setPrefixedStyle(elt.style, 'borderRadius', '3px'); + + elt.appendChild(arrow); + + return symbol; +}; + +/** + * + */ +BaseFormatPanel.prototype.addUnitInput = function(container, unit, right, width, update, step, marginTop, disableFocus) +{ + marginTop = (marginTop != null) ? marginTop : 0; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.marginTop = '-2px'; + input.style.right = (right + 12) + 'px'; + input.style.width = width + 'px'; + container.appendChild(input); + + var stepper = this.createStepper(input, update, step, null, disableFocus); + stepper.style.marginTop = (marginTop - 2) + 'px'; + stepper.style.right = right + 'px'; + container.appendChild(stepper); + + return input; +}; + +/** + * + */ +BaseFormatPanel.prototype.createRelativeOption = function(label, key, width, handler, init) +{ + width = (width != null) ? width : 44; + + var graph = this.editorUi.editor.graph; + var div = this.createPanel(); + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + mxUtils.write(div, label); + div.style.fontWeight = 'bold'; + + var update = mxUtils.bind(this, function(evt) + { + if (handler != null) + { + handler(input); + } + else + { + var value = parseInt(input.value); + value = Math.min(100, Math.max(0, (isNaN(value)) ? 100 : value)); + var state = graph.view.getState(graph.getSelectionCell()); + + if (state != null && value != mxUtils.getValue(state.style, key, 100)) + { + // Removes entry in style (assumes 100 is default for relative values) + if (value == 100) + { + value = null; + } + + graph.setCellStyles(key, value, graph.getSelectionCells()); + this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = ((value != null) ? value : '100') + ' %'; + } + + mxEvent.consume(evt); + }); + + var input = this.addUnitInput(div, '%', 20, width, update, 10, -15, handler != null); + + if (key != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || input != document.activeElement) + { + var ss = this.format.getSelectionState(); + var tmp = parseInt(mxUtils.getValue(ss.style, key, 100)); + input.value = (isNaN(tmp)) ? '' : tmp + ' %'; + } + }); + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + listener(null, null, true); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + if (init != null) + { + init(input); + } + + return div; +}; + +/** + * + */ +BaseFormatPanel.prototype.addLabel = function(div, title, right, width) +{ + width = (width != null) ? width : 61; + + var label = document.createElement('div'); + mxUtils.write(label, title); + label.style.position = 'absolute'; + label.style.right = right + 'px'; + label.style.width = width + 'px'; + label.style.marginTop = '6px'; + label.style.textAlign = 'center'; + div.appendChild(label); +}; + +/** + * + */ +BaseFormatPanel.prototype.addKeyHandler = function(input, listener) +{ + mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e) + { + if (e.keyCode == 13) + { + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + if (listener != null) + { + listener(null, null, true); + } + + this.editorUi.editor.graph.container.focus(); + mxEvent.consume(e); + } + })); +}; + +/** + * + */ +BaseFormatPanel.prototype.styleButtons = function(elts) +{ + for (var i = 0; i < elts.length; i++) + { + mxUtils.setPrefixedStyle(elts[i].style, 'borderRadius', '3px'); + mxUtils.setOpacity(elts[i], 100); + elts[i].style.border = '1px solid #a0a0a0'; + elts[i].style.padding = '4px'; + elts[i].style.paddingTop = '3px'; + elts[i].style.paddingRight = '1px'; + elts[i].style.margin = '1px'; + elts[i].style.width = '24px'; + elts[i].style.height = '20px'; + elts[i].className += ' geColorBtn'; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +BaseFormatPanel.prototype.destroy = function() +{ + if (this.listeners != null) + { + for (var i = 0; i < this.listeners.length; i++) + { + this.listeners[i].destroy(); + } + + this.listeners = null; + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(ArrangePanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +ArrangePanel.prototype.init = function() +{ + var graph = this.editorUi.editor.graph; + var ss = this.format.getSelectionState(); + + this.container.appendChild(this.addLayerOps(this.createPanel())); + // Special case that adds two panels + this.addGeometry(this.container); + this.addEdgeGeometry(this.container); + + if (!ss.containsLabel || ss.edges.length == 0) + { + this.container.appendChild(this.addAngle(this.createPanel())); + } + + if (!ss.containsLabel && ss.edges.length == 0) + { + this.container.appendChild(this.addFlip(this.createPanel())); + } + + if (ss.vertices.length > 1) + { + this.container.appendChild(this.addAlign(this.createPanel())); + this.container.appendChild(this.addDistribute(this.createPanel())); + } + + this.container.appendChild(this.addGroupOps(this.createPanel())); +}; + +/** + * + */ +ArrangePanel.prototype.addLayerOps = function(div) +{ + var ui = this.editorUi; + + var btn = mxUtils.button(mxResources.get('toFront'), function(evt) + { + ui.actions.get('toFront').funct(); + }) + + btn.setAttribute('title', mxResources.get('toFront') + ' (' + this.editorUi.actions.get('toFront').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('toBack'), function(evt) + { + ui.actions.get('toBack').funct(); + }) + + btn.setAttribute('title', mxResources.get('toBack') + ' (' + this.editorUi.actions.get('toBack').shortcut + ')'); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addGroupOps = function(div) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var cell = graph.getSelectionCell(); + var ss = this.format.getSelectionState(); + var count = 0; + var btn = null; + + div.style.paddingTop = '8px'; + div.style.paddingBottom = '6px'; + + if (graph.getSelectionCount() > 1) + { + btn = mxUtils.button(mxResources.get('group'), function(evt) + { + ui.actions.get('group').funct(); + }) + + btn.setAttribute('title', mxResources.get('group') + ' (' + this.editorUi.actions.get('group').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && + graph.getModel().getChildCount(cell) > 0) + { + btn = mxUtils.button(mxResources.get('ungroup'), function(evt) + { + ui.actions.get('ungroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('ungroup') + ' (' + + this.editorUi.actions.get('ungroup').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (ss.vertices.length > 0) + { + if (count > 0) + { + mxUtils.br(div); + count = 0; + } + + var btn = mxUtils.button(mxResources.get('copySize'), function(evt) + { + ui.actions.get('copySize').funct(); + }); + + btn.setAttribute('title', mxResources.get('copySize') + ' (' + + this.editorUi.actions.get('copySize').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + count++; + + if (ui.copiedSize != null) + { + var btn2 = mxUtils.button(mxResources.get('pasteSize'), function(evt) + { + ui.actions.get('pasteSize').funct(); + }); + + btn2.setAttribute('title', mxResources.get('pasteSize') + ' (' + + this.editorUi.actions.get('pasteSize').shortcut + ')'); + + div.appendChild(btn2); + count++; + + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + btn2.style.width = '100px'; + btn2.style.marginBottom = '2px'; + } + } + + if (graph.getSelectionCount() == 1 && graph.getModel().isVertex(cell) && + graph.getModel().isVertex(graph.getModel().getParent(cell))) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('removeFromGroup'), function(evt) + { + ui.actions.get('removeFromGroup').funct(); + }) + + btn.setAttribute('title', mxResources.get('removeFromGroup')); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + else if (graph.getSelectionCount() > 0) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('clearWaypoints'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearWaypoints').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearWaypoints') + ' (' + this.editorUi.actions.get('clearWaypoints').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + count++; + } + + if (graph.getSelectionCount() == 1) + { + if (count > 0) + { + mxUtils.br(div); + } + + btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '100px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + + btn = mxUtils.button(mxResources.get('editLink'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editLink').funct(); + })); + + btn.setAttribute('title', mxResources.get('editLink')); + btn.style.width = '100px'; + btn.style.marginLeft = '2px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + count++; + } + + if (count == 0) + { + div.style.display = 'none'; + } + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAlign = function(div) +{ + var graph = this.editorUi.editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + div.appendChild(this.createTitle(mxResources.get('align'))); + + var stylePanel = document.createElement('div'); + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '0px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + div.style.height = '60px'; + } + + var left = this.editorUi.toolbar.addButton('geSprite-alignleft', mxResources.get('left'), + function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, stylePanel); + var center = this.editorUi.toolbar.addButton('geSprite-aligncenter', mxResources.get('center'), + function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, stylePanel); + var right = this.editorUi.toolbar.addButton('geSprite-alignright', mxResources.get('right'), + function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, stylePanel); + + var top = this.editorUi.toolbar.addButton('geSprite-aligntop', mxResources.get('top'), + function() { graph.alignCells(mxConstants.ALIGN_TOP); }, stylePanel); + var middle = this.editorUi.toolbar.addButton('geSprite-alignmiddle', mxResources.get('middle'), + function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, stylePanel); + var bottom = this.editorUi.toolbar.addButton('geSprite-alignbottom', mxResources.get('bottom'), + function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, stylePanel); + + this.styleButtons([left, center, right, top, middle, bottom]); + right.style.marginRight = '6px'; + div.appendChild(stylePanel); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addFlip = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '10px'; + + var span = document.createElement('div'); + span.style.marginTop = '2px'; + span.style.marginBottom = '8px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('flip')); + div.appendChild(span); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addDistribute = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + div.style.paddingTop = '6px'; + div.style.paddingBottom = '12px'; + + div.appendChild(this.createTitle(mxResources.get('distribute'))); + + var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) + { + graph.distributeCells(true); + }) + + btn.setAttribute('title', mxResources.get('horizontal')); + btn.style.width = '100px'; + btn.style.marginRight = '2px'; + div.appendChild(btn); + + var btn = mxUtils.button(mxResources.get('vertical'), function(evt) + { + graph.distributeCells(false); + }) + + btn.setAttribute('title', mxResources.get('vertical')); + btn.style.width = '100px'; + div.appendChild(btn); + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addAngle = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + + var input = null; + var update = null; + var btn = null; + + if (ss.edges.length == 0) + { + mxUtils.write(span, mxResources.get('angle')); + div.appendChild(span); + + input = this.addUnitInput(div, '°', 20, 44, function() + { + update.apply(this, arguments); + }); + + mxUtils.br(div); + div.style.paddingTop = '10px'; + } + else + { + div.style.paddingTop = '8px'; + } + + if (!ss.containsLabel) + { + var label = mxResources.get('reverse'); + + if (ss.vertices.length > 0 && ss.edges.length > 0) + { + label = mxResources.get('turn') + ' / ' + label; + } + else if (ss.vertices.length > 0) + { + label = mxResources.get('turn'); + } + + btn = mxUtils.button(label, function(evt) + { + ui.actions.get('turn').funct(); + }) + + btn.setAttribute('title', label + ' (' + this.editorUi.actions.get('turn').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + if (input != null) + { + btn.style.marginTop = '8px'; + } + } + + if (input != null) + { + var listener = mxUtils.bind(this, function(sender, evt, force) + { + if (force || document.activeElement != input) + { + ss = this.format.getSelectionState(); + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0)); + input.value = (isNaN(tmp)) ? '' : tmp + '°'; + } + }); + + update = this.installInputHandler(input, mxConstants.STYLE_ROTATION, 0, 0, 360, '°', null, true); + this.addKeyHandler(input, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + + return div; +}; + +/** + * + */ +ArrangePanel.prototype.addGeometry = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + div.style.paddingBottom = '8px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '50px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('size')); + div.appendChild(span); + + var widthUpdate, heightUpdate, leftUpdate, topUpdate; + var width = this.addUnitInput(div, 'pt', 84, 44, function() + { + widthUpdate.apply(this, arguments); + }); + var height = this.addUnitInput(div, 'pt', 20, 44, function() + { + heightUpdate.apply(this, arguments); + }); + + var autosizeBtn = document.createElement('div'); + autosizeBtn.className = 'geSprite geSprite-fit'; + autosizeBtn.setAttribute('title', mxResources.get('autosize') + ' (' + this.editorUi.actions.get('autosize').shortcut + ')'); + autosizeBtn.style.position = 'relative'; + autosizeBtn.style.cursor = 'pointer'; + autosizeBtn.style.marginTop = '-3px'; + autosizeBtn.style.border = '0px'; + autosizeBtn.style.left = '52px'; + mxUtils.setOpacity(autosizeBtn, 50); + + mxEvent.addListener(autosizeBtn, 'mouseenter', function() + { + mxUtils.setOpacity(autosizeBtn, 100); + }); + + mxEvent.addListener(autosizeBtn, 'mouseleave', function() + { + mxUtils.setOpacity(autosizeBtn, 50); + }); + + mxEvent.addListener(autosizeBtn, 'click', function() + { + ui.actions.get('autosize').funct(); + }); + + div.appendChild(autosizeBtn); + this.addLabel(div, mxResources.get('width'), 84); + this.addLabel(div, mxResources.get('height'), 20); + mxUtils.br(div); + + var wrapper = document.createElement('div'); + wrapper.style.paddingTop = '8px'; + wrapper.style.paddingRight = '20px'; + wrapper.style.whiteSpace = 'nowrap'; + wrapper.style.textAlign = 'right'; + var opt = this.createCellOption(mxResources.get('constrainProportions'), + mxConstants.STYLE_ASPECT, null, 'fixed', 'null'); + opt.style.width = '100%'; + wrapper.appendChild(opt); + div.appendChild(wrapper); + + var constrainCheckbox = opt.getElementsByTagName('input')[0]; + this.addKeyHandler(width, listener); + this.addKeyHandler(height, listener); + + widthUpdate = this.addGeometryHandler(width, function(geo, value) + { + if (geo.width > 0) + { + var value = Math.max(1, value); + + if (constrainCheckbox.checked) + { + geo.height = Math.round((geo.height * value * 100) / geo.width) / 100; + } + + geo.width = value; + } + }); + heightUpdate = this.addGeometryHandler(height, function(geo, value) + { + if (geo.height > 0) + { + var value = Math.max(1, value); + + if (constrainCheckbox.checked) + { + geo.width = Math.round((geo.width * value * 100) / geo.height) / 100; + } + + geo.height = value; + } + }); + + container.appendChild(div); + + var div2 = this.createPanel(); + div2.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('position')); + div2.appendChild(span); + + var left = this.addUnitInput(div2, 'pt', 84, 44, function() + { + leftUpdate.apply(this, arguments); + }); + var top = this.addUnitInput(div2, 'pt', 20, 44, function() + { + topUpdate.apply(this, arguments); + }); + + mxUtils.br(div2); + this.addLabel(div2, mxResources.get('left'), 84); + this.addLabel(div2, mxResources.get('top'), 20); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + + if (!rect.containsLabel && rect.vertices.length == graph.getSelectionCount() && + rect.width != null && rect.height != null) + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + width.value = rect.width + ((rect.width == '') ? '' : ' pt'); + } + + if (force || document.activeElement != height) + { + height.value = rect.height + ((rect.height == '') ? '' : ' pt'); + } + } + else + { + div.style.display = 'none'; + } + + if (rect.vertices.length == graph.getSelectionCount() && + rect.x != null && rect.y != null) + { + div2.style.display = ''; + + if (force || document.activeElement != left) + { + left.value = rect.x + ((rect.x == '') ? '' : ' pt'); + } + + if (force || document.activeElement != top) + { + top.value = rect.y + ((rect.y == '') ? '' : ' pt'); + } + } + else + { + div2.style.display = 'none'; + } + }); + + this.addKeyHandler(left, listener); + this.addKeyHandler(top, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + leftUpdate = this.addGeometryHandler(left, function(geo, value) + { + if (geo.relative) + { + geo.offset.x = value; + } + else + { + geo.x = value; + } + }); + topUpdate = this.addGeometryHandler(top, function(geo, value) + { + if (geo.relative) + { + geo.offset.y = value; + } + else + { + geo.y = value; + } + }); + + container.appendChild(div2); +}; + +/** + * + */ +ArrangePanel.prototype.addGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isVertex(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' pt'; + } + else if (isNaN(value)) + { + input.value = initialValue + ' pt'; + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +ArrangePanel.prototype.addEdgeGeometryHandler = function(input, fn) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var initialValue = null; + + function update(evt) + { + if (input.value != '') + { + var value = parseFloat(input.value); + + if (isNaN(value)) + { + input.value = initialValue + ' pt'; + } + else if (value != initialValue) + { + graph.getModel().beginUpdate(); + try + { + var cells = graph.getSelectionCells(); + + for (var i = 0; i < cells.length; i++) + { + if (graph.getModel().isEdge(cells[i])) + { + var geo = graph.getCellGeometry(cells[i]); + + if (geo != null) + { + geo = geo.clone(); + fn(geo, value); + + graph.getModel().setGeometry(cells[i], geo); + } + } + } + } + finally + { + graph.getModel().endUpdate(); + } + + initialValue = value; + input.value = value + ' pt'; + } + } + + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + mxEvent.addListener(input, 'focus', function() + { + initialValue = input.value; + }); + + return update; +}; + +/** + * + */ +ArrangePanel.prototype.addEdgeGeometry = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var rect = this.format.getSelectionState(); + + var div = this.createPanel(); + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('width')); + div.appendChild(span); + + var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate; + var width = this.addUnitInput(div, 'pt', 20, 44, function() + { + widthUpdate.apply(this, arguments); + }); + + mxUtils.br(div); + this.addKeyHandler(width, listener); + + function widthUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(width.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(rect.style, 'width', mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth)) + { + graph.setCellStyles('width', value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['width'], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + width.value = value + ' pt'; + mxEvent.consume(evt); + }; + + mxEvent.addListener(width, 'blur', widthUpdate); + mxEvent.addListener(width, 'change', widthUpdate); + + container.appendChild(div); + + var divs = this.createPanel(); + divs.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'Start'); + divs.appendChild(span); + + var xs = this.addUnitInput(divs, 'pt', 84, 44, function() + { + xsUpdate.apply(this, arguments); + }); + var ys = this.addUnitInput(divs, 'pt', 20, 44, function() + { + ysUpdate.apply(this, arguments); + }); + + mxUtils.br(divs); + this.addLabel(divs, mxResources.get('left'), 84); + this.addLabel(divs, mxResources.get('top'), 20); + container.appendChild(divs); + this.addKeyHandler(xs, listener); + this.addKeyHandler(ys, listener); + + var divt = this.createPanel(); + divt.style.paddingBottom = '30px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, 'End'); + divt.appendChild(span); + + var xt = this.addUnitInput(divt, 'pt', 84, 44, function() + { + xtUpdate.apply(this, arguments); + }); + var yt = this.addUnitInput(divt, 'pt', 20, 44, function() + { + ytUpdate.apply(this, arguments); + }); + + mxUtils.br(divt); + this.addLabel(divt, mxResources.get('left'), 84); + this.addLabel(divt, mxResources.get('top'), 20); + container.appendChild(divt); + this.addKeyHandler(xt, listener); + this.addKeyHandler(yt, listener); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + rect = this.format.getSelectionState(); + var cell = graph.getSelectionCell(); + + if (rect.style.shape == 'link' || rect.style.shape == 'flexArrow') + { + div.style.display = ''; + + if (force || document.activeElement != width) + { + var value = mxUtils.getValue(rect.style, 'width', + mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth); + width.value = value + ' pt'; + } + } + else + { + div.style.display = 'none'; + } + + if (graph.getSelectionCount() == 1 && graph.model.isEdge(cell)) + { + var geo = graph.model.getGeometry(cell); + + if (geo.sourcePoint != null && graph.model.getTerminal(cell, true) == null) + { + xs.value = geo.sourcePoint.x; + ys.value = geo.sourcePoint.y; + } + else + { + divs.style.display = 'none'; + } + + if (geo.targetPoint != null && graph.model.getTerminal(cell, false) == null) + { + xt.value = geo.targetPoint.x; + yt.value = geo.targetPoint.y; + } + else + { + divt.style.display = 'none'; + } + } + else + { + divs.style.display = 'none'; + divt.style.display = 'none'; + } + }); + + xsUpdate = this.addEdgeGeometryHandler(xs, function(geo, value) + { + geo.sourcePoint.x = value; + }); + + ysUpdate = this.addEdgeGeometryHandler(ys, function(geo, value) + { + geo.sourcePoint.y = value; + }); + + xtUpdate = this.addEdgeGeometryHandler(xt, function(geo, value) + { + geo.targetPoint.x = value; + }); + + ytUpdate = this.addEdgeGeometryHandler(yt, function(geo, value) + { + geo.targetPoint.y = value; + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(TextFormatPanel, BaseFormatPanel); + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.init = function() +{ + this.container.style.borderBottom = 'none'; + this.addFont(this.container); +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +TextFormatPanel.prototype.addFont = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var title = this.createTitle(mxResources.get('font')); + title.style.paddingLeft = '18px'; + title.style.paddingTop = '10px'; + title.style.paddingBottom = '6px'; + container.appendChild(title); + + var stylePanel = this.createPanel(); + stylePanel.style.paddingTop = '2px'; + stylePanel.style.paddingBottom = '2px'; + stylePanel.style.position = 'relative'; + stylePanel.style.marginLeft = '-2px'; + stylePanel.style.borderWidth = '0px'; + stylePanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS) + { + stylePanel.style.display = 'block'; + } + + if (graph.cellEditor.isContentEditing()) + { + var cssPanel = stylePanel.cloneNode(); + + var cssMenu = this.editorUi.toolbar.addMenu(mxResources.get('style'), + mxResources.get('style'), true, 'formatBlock', cssPanel); + cssMenu.style.color = 'rgb(112, 112, 112)'; + cssMenu.style.whiteSpace = 'nowrap'; + cssMenu.style.overflow = 'hidden'; + cssMenu.style.margin = '0px'; + this.addArrow(cssMenu); + cssMenu.style.width = '192px'; + cssMenu.style.height = '15px'; + + var arrow = cssMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + container.appendChild(cssPanel); + + // Workaround for offset in FF + if (mxClient.IS_FF) + { + cssMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; + } + } + + container.appendChild(stylePanel); + + var colorPanel = this.createPanel(); + colorPanel.style.marginTop = '8px'; + colorPanel.style.borderTop = '1px solid #c0c0c0'; + colorPanel.style.paddingTop = '6px'; + colorPanel.style.paddingBottom = '6px'; + + var fontMenu = this.editorUi.toolbar.addMenu('Helvetica', mxResources.get('fontFamily'), true, 'fontFamily', stylePanel); + fontMenu.style.color = 'rgb(112, 112, 112)'; + fontMenu.style.whiteSpace = 'nowrap'; + fontMenu.style.overflow = 'hidden'; + fontMenu.style.margin = '0px'; + + this.addArrow(fontMenu); + fontMenu.style.width = '192px'; + fontMenu.style.height = '15px'; + + // Workaround for offset in FF + if (mxClient.IS_FF) + { + fontMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; + } + + var stylePanel2 = stylePanel.cloneNode(false); + stylePanel2.style.marginLeft = '-3px'; + var fontStyleItems = this.editorUi.toolbar.addItems(['bold', 'italic', 'underline'], stylePanel2, true); + fontStyleItems[0].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); + fontStyleItems[1].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); + fontStyleItems[2].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); + + var verticalItem = this.editorUi.toolbar.addItems(['vertical'], stylePanel2, true)[0]; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel2); + + this.styleButtons(fontStyleItems); + this.styleButtons([verticalItem]); + + var stylePanel3 = stylePanel.cloneNode(false); + stylePanel3.style.marginLeft = '-3px'; + stylePanel3.style.paddingBottom = '0px'; + + // Helper function to return a wrapper function does not pass any arguments + var callFn = function(fn) + { + return function() + { + return fn(); + }; + }; + + var left = this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifyleft', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT])), stylePanel3); + var center = this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifycenter', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER])), stylePanel3); + var right = this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + (graph.cellEditor.isContentEditing()) ? + function() + { + document.execCommand('justifyright', false, null); + } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT])), stylePanel3); + + this.styleButtons([left, center, right]); + + if (graph.cellEditor.isContentEditing()) + { + var clear = this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('removeFormat'), + function() + { + document.execCommand('removeformat', false, null); + }, stylePanel2); + this.styleButtons([clear]); + } + + var top = this.editorUi.toolbar.addButton('geSprite-top', mxResources.get('top'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP])), stylePanel3); + var middle = this.editorUi.toolbar.addButton('geSprite-middle', mxResources.get('middle'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE])), stylePanel3); + var bottom = this.editorUi.toolbar.addButton('geSprite-bottom', mxResources.get('bottom'), + callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM])), stylePanel3); + + this.styleButtons([top, middle, bottom]); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(stylePanel3); + + // Hack for updating UI state below based on current text selection + // currentTable is the current selected DOM table updated below + var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow; + + if (graph.cellEditor.isContentEditing()) + { + top.style.display = 'none'; + middle.style.display = 'none'; + bottom.style.display = 'none'; + verticalItem.style.display = 'none'; + + full = this.editorUi.toolbar.addButton('geSprite-justifyfull', null, + function() + { + document.execCommand('justifyfull', false, null); + }, stylePanel3); + this.styleButtons([full, + sub = this.editorUi.toolbar.addButton('geSprite-subscript', + mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)', + function() + { + document.execCommand('subscript', false, null); + }, stylePanel3), sup = this.editorUi.toolbar.addButton('geSprite-superscript', + mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)', + function() + { + document.execCommand('superscript', false, null); + }, stylePanel3)]); + full.style.marginRight = '9px'; + + var tmp = stylePanel3.cloneNode(false); + tmp.style.paddingTop = '4px'; + var btns = [this.editorUi.toolbar.addButton('geSprite-orderedlist', mxResources.get('numberedList'), + function() + { + document.execCommand('insertorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-unorderedlist', mxResources.get('bulletedList'), + function() + { + document.execCommand('insertunorderedlist', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-outdent', mxResources.get('decreaseIndent'), + function() + { + document.execCommand('outdent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-indent', mxResources.get('increaseIndent'), + function() + { + document.execCommand('indent', false, null); + }, tmp), + this.editorUi.toolbar.addButton('geSprite-code', mxResources.get('html'), + function() + { + graph.cellEditor.toggleViewMode(); + }, tmp)]; + this.styleButtons(btns); + btns[btns.length - 1].style.marginLeft = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + tmp.style.height = '40'; + } + + container.appendChild(tmp); + } + else + { + fontStyleItems[2].style.marginRight = '9px'; + right.style.marginRight = '9px'; + } + + // Label position + var stylePanel4 = stylePanel.cloneNode(false); + stylePanel4.style.marginLeft = '0px'; + stylePanel4.style.paddingTop = '8px'; + stylePanel4.style.paddingBottom = '4px'; + stylePanel4.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel4, mxResources.get('position')); + + // Adds label position options + var positionSelect = document.createElement('select'); + positionSelect.style.position = 'absolute'; + positionSelect.style.right = '20px'; + positionSelect.style.width = '97px'; + positionSelect.style.marginTop = '-2px'; + + var directions = ['topLeft', 'top', 'topRight', 'left', 'center', 'right', 'bottomLeft', 'bottom', 'bottomRight']; + var lset = {'topLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM], + 'top': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM], + 'topRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM], + 'left': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE], + 'center': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE], + 'right': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE], + 'bottomLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP], + 'bottom': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP], + 'bottomRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP]}; + + for (var i = 0; i < directions.length; i++) + { + var positionOption = document.createElement('option'); + positionOption.setAttribute('value', directions[i]); + mxUtils.write(positionOption, mxResources.get(directions[i])); + positionSelect.appendChild(positionOption); + } + + stylePanel4.appendChild(positionSelect); + + // Writing direction + var stylePanel5 = stylePanel.cloneNode(false); + stylePanel5.style.marginLeft = '0px'; + stylePanel5.style.paddingTop = '4px'; + stylePanel5.style.paddingBottom = '4px'; + stylePanel5.style.fontWeight = 'normal'; + + mxUtils.write(stylePanel5, mxResources.get('writingDirection')); + + // Adds writing direction options + // LATER: Handle reselect of same option in all selects (change event + // is not fired for same option so have opened state on click) and + // handle multiple different styles for current selection + var dirSelect = document.createElement('select'); + dirSelect.style.position = 'absolute'; + dirSelect.style.right = '20px'; + dirSelect.style.width = '97px'; + dirSelect.style.marginTop = '-2px'; + + // NOTE: For automatic we use the value null since automatic + // requires the text to be non formatted and non-wrapped + var dirs = ['automatic', 'leftToRight', 'rightToLeft']; + var dirSet = {'automatic': null, + 'leftToRight': mxConstants.TEXT_DIRECTION_LTR, + 'rightToLeft': mxConstants.TEXT_DIRECTION_RTL}; + + for (var i = 0; i < dirs.length; i++) + { + var dirOption = document.createElement('option'); + dirOption.setAttribute('value', dirs[i]); + mxUtils.write(dirOption, mxResources.get(dirs[i])); + dirSelect.appendChild(dirOption); + } + + stylePanel5.appendChild(dirSelect); + + if (!graph.isEditing()) + { + container.appendChild(stylePanel4); + + mxEvent.addListener(positionSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var vals = lset[positionSelect.value]; + + if (vals != null) + { + graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, vals[0], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, vals[1], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], graph.getSelectionCells()); + graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, vals[3], graph.getSelectionCells()); + } + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // LATER: Update dir in text editor while editing and update style with label + // NOTE: The tricky part is handling and passing on the auto value + container.appendChild(stylePanel5); + + mxEvent.addListener(dirSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_TEXT_DIRECTION, dirSet[dirSelect.value], graph.getSelectionCells()); + mxEvent.consume(evt); + }); + } + + // Font size + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + } + + input.style.width = '46px'; + input.style.height = (mxClient.IS_QUIRKS) ? '21px' : '17px'; + stylePanel2.appendChild(input); + + // Workaround for font size 4 if no text is selected is update font size below + // after first character was entered (as the font element is lazy created) + var pendingFontSize = null; + + var inputUpdate = this.installInputHandler(input, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize, 1, 999, ' pt', + function(fontSize) + { + // IE does not support containsNode + // KNOWN: Fixes font size issues but bypasses undo + if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + var container = (selection.rangeCount > 0) ? selection.getRangeAt(0).commonAncestorContainer : + graph.cellEditor.textarea; + + function updateSize(elt, ignoreContains) + { + if (elt != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(elt) && + (ignoreContains || selection.containsNode(elt, true))) + { + if (elt.nodeName == 'FONT') + { + elt.removeAttribute('size'); + elt.style.fontSize = fontSize + 'px'; + } + else + { + var css = mxUtils.getCurrentStyle(elt); + + if (css.fontSize != fontSize + 'px') + { + if (mxUtils.getCurrentStyle(elt.parentNode).fontSize != fontSize + 'px') + { + elt.style.fontSize = fontSize + 'px'; + } + else + { + elt.style.fontSize = ''; + } + } + } + } + }; + + // Wraps text node or mixed selection with leading text in a font element + if (container == graph.cellEditor.textarea || + container.nodeType != mxConstants.NODETYPE_ELEMENT) + { + document.execCommand('fontSize', false, '1'); + } + + if (container != graph.cellEditor.textarea) + { + container = container.parentNode; + } + + if (container.nodeType == mxConstants.NODETYPE_ELEMENT) + { + var elts = container.getElementsByTagName('*'); + updateSize(container); + + for (var i = 0; i < elts.length; i++) + { + updateSize(elts[i]); + } + } + + input.value = fontSize + ' pt'; + } + else if (window.getSelection || document.selection) + { + // Checks selection + var par = null; + + if (document.selection) + { + par = document.selection.createRange().parentElement(); + } + else + { + var selection = window.getSelection(); + + if (selection.rangeCount > 0) + { + par = selection.getRangeAt(0).commonAncestorContainer; + } + } + + // Node.contains does not work for text nodes in IE11 + function isOrContains(container, node) + { + while (node != null) + { + if (node === container) + { + return true; + } + + node = node.parentNode; + } + + return false; + }; + + if (par != null && isOrContains(graph.cellEditor.textarea, par)) + { + pendingFontSize = fontSize; + + // Workaround for can't set font size in px is to change font size afterwards + document.execCommand('fontSize', false, '4'); + var elts = graph.cellEditor.textarea.getElementsByTagName('font'); + + for (var i = 0; i < elts.length; i++) + { + if (elts[i].getAttribute('size') == '4') + { + elts[i].removeAttribute('size'); + elts[i].style.fontSize = pendingFontSize + 'px'; + + // Overrides fontSize in input with the one just assigned as a workaround + // for potential fontSize values of parent elements that don't match + window.setTimeout(function() + { + input.value = pendingFontSize + ' pt'; + pendingFontSize = null; + }, 0); + + break; + } + } + } + } + }, true); + + var stepper = this.createStepper(input, inputUpdate, 1, 10, true, Menus.prototype.defaultFontSize); + stepper.style.display = input.style.display; + stepper.style.marginTop = '4px'; + + if (!mxClient.IS_QUIRKS) + { + stepper.style.right = '20px'; + } + + stylePanel2.appendChild(stepper); + + var arrow = fontMenu.getElementsByTagName('div')[0]; + arrow.style.cssFloat = 'right'; + + var bgColorApply = null; + var currentBgColor = '#ffffff'; + + var fontColorApply = null; + var currentFontColor = '#000000'; + + var bgPanel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('backgroundColor'), function() + { + return currentBgColor; + }, function(color) + { + document.execCommand('backcolor', false, (color != mxConstants.NONE) ? color : 'transparent'); + }, '#ffffff', + { + install: function(apply) { bgColorApply = apply; }, + destroy: function() { bgColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('backgroundColor'), mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, '#ffffff', null, function(color) + { + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.style.backgroundColor = null; + }); + }); + bgPanel.style.fontWeight = 'bold'; + + var borderPanel = this.createCellColorOption(mxResources.get('borderColor'), mxConstants.STYLE_LABEL_BORDERCOLOR, '#000000'); + borderPanel.style.fontWeight = 'bold'; + + var panel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('fontColor'), function() + { + return currentFontColor; + }, function(color) + { + document.execCommand('forecolor', false, (color != mxConstants.NONE) ? color : 'transparent'); + }, '#000000', + { + install: function(apply) { fontColorApply = apply; }, + destroy: function() { fontColorApply = null; } + }, null, true) : this.createCellColorOption(mxResources.get('fontColor'), mxConstants.STYLE_FONTCOLOR, '#000000', function(color) + { + if (color == null || color == mxConstants.NONE) + { + bgPanel.style.display = 'none'; + } + else + { + bgPanel.style.display = ''; + } + + borderPanel.style.display = bgPanel.style.display; + }, function(color) + { + if (color == null || color == mxConstants.NONE) + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, '1', graph.getSelectionCells()); + } + else + { + graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, graph.getSelectionCells()); + } + + graph.updateLabelElements(graph.getSelectionCells(), function(elt) + { + elt.removeAttribute('color'); + elt.style.color = null; + }); + }); + panel.style.fontWeight = 'bold'; + + colorPanel.appendChild(panel); + colorPanel.appendChild(bgPanel); + + if (!graph.cellEditor.isContentEditing()) + { + colorPanel.appendChild(borderPanel); + } + + container.appendChild(colorPanel); + + var extraPanel = this.createPanel(); + extraPanel.style.paddingTop = '2px'; + extraPanel.style.paddingBottom = '4px'; + + // LATER: Fix toggle using '' instead of 'null' + var wwOpt = this.createCellOption(mxResources.get('wordWrap'), mxConstants.STYLE_WHITE_SPACE, null, 'wrap', 'null', null, null, true); + wwOpt.style.fontWeight = 'bold'; + + // Word wrap in edge labels only supported via labelWidth style + if (!ss.containsLabel && !ss.autoSize && ss.edges.length == 0) + { + extraPanel.appendChild(wwOpt); + } + + // Delegates switch of style to formattedText action as it also convertes newlines + var htmlOpt = this.createCellOption(mxResources.get('formattedText'), 'html', '0', + null, null, null, ui.actions.get('formattedText')); + htmlOpt.style.fontWeight = 'bold'; + extraPanel.appendChild(htmlOpt); + + var spacingPanel = this.createPanel(); + spacingPanel.style.paddingTop = '10px'; + spacingPanel.style.paddingBottom = '28px'; + spacingPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.width = '70px'; + span.style.marginTop = '0px'; + span.style.fontWeight = 'bold'; + mxUtils.write(span, mxResources.get('spacing')); + spacingPanel.appendChild(span); + + var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate; + var topSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + topUpdate.apply(this, arguments); + }); + var globalSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + globalUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('top'), 91); + this.addLabel(spacingPanel, mxResources.get('global'), 20); + mxUtils.br(spacingPanel); + mxUtils.br(spacingPanel); + + var leftSpacing = this.addUnitInput(spacingPanel, 'pt', 162, 44, function() + { + leftUpdate.apply(this, arguments); + }); + var bottomSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() + { + bottomUpdate.apply(this, arguments); + }); + var rightSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() + { + rightUpdate.apply(this, arguments); + }); + + mxUtils.br(spacingPanel); + this.addLabel(spacingPanel, mxResources.get('left'), 162); + this.addLabel(spacingPanel, mxResources.get('bottom'), 91); + this.addLabel(spacingPanel, mxResources.get('right'), 20); + + if (!graph.cellEditor.isContentEditing()) + { + container.appendChild(extraPanel); + container.appendChild(this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_TEXT_OPACITY)); + container.appendChild(spacingPanel); + } + else + { + var selState = null; + var lineHeightInput = null; + + container.appendChild(this.createRelativeOption(mxResources.get('lineheight'), null, null, function(input) + { + var value = (input.value == '') ? 120 : parseInt(input.value); + value = Math.max(0, (isNaN(value)) ? 120 : value); + + if (selState != null) + { + graph.cellEditor.restoreSelection(selState); + selState = null; + } + + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null && node == graph.cellEditor.textarea && graph.cellEditor.textarea.firstChild != null) + { + if (graph.cellEditor.textarea.firstChild.nodeName != 'P') + { + graph.cellEditor.textarea.innerHTML = '

' + graph.cellEditor.textarea.innerHTML + '

'; + } + + node = graph.cellEditor.textarea.firstChild; + } + + if (node != null && node != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(node)) + { + node.style.lineHeight = value + '%'; + } + + input.value = value + ' %'; + }, function(input) + { + // Used in CSS handler to update current value + lineHeightInput = input; + + // KNOWN: Arrow up/down clear selection text in quirks/IE 8 + // Text size via arrow button limits to 16 in IE11. Why? + mxEvent.addListener(input, 'mousedown', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + mxEvent.addListener(input, 'touchstart', function() + { + if (document.activeElement == graph.cellEditor.textarea) + { + selState = graph.cellEditor.saveSelection(); + } + }); + + input.value = '120 %'; + })); + + var insertPanel = stylePanel.cloneNode(false); + insertPanel.style.paddingLeft = '0px'; + var insertBtns = this.editorUi.toolbar.addItems(['link', 'image'], insertPanel, true); + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-horizontalrule', mxResources.get('insertHorizontalRule'), + function() + { + document.execCommand('inserthorizontalrule', false); + }, insertPanel), + this.editorUi.toolbar.addMenuFunctionInContainer(insertPanel, 'geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.addInsertTableItem(menu); + }))]; + this.styleButtons(insertBtns); + this.styleButtons(btns); + + var wrapper2 = this.createPanel(); + wrapper2.style.paddingTop = '10px'; + wrapper2.style.paddingBottom = '10px'; + wrapper2.appendChild(this.createTitle(mxResources.get('insert'))); + wrapper2.appendChild(insertPanel); + container.appendChild(wrapper2); + + if (mxClient.IS_QUIRKS) + { + wrapper2.style.height = '70'; + } + + var tablePanel = stylePanel.cloneNode(false); + tablePanel.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex : 0)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null) + { + graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex + 1 : -1)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableCell != null) + { + graph.deleteColumn(currentTable, tableCell.cellIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex + 1)); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel), + this.editorUi.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'), + mxUtils.bind(this, function() + { + try + { + if (currentTable != null && tableRow != null) + { + graph.deleteRow(currentTable, tableRow.sectionRowIndex); + } + } + catch (e) + { + this.editorUi.handleError(e); + } + }), tablePanel)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + var wrapper3 = this.createPanel(); + wrapper3.style.paddingTop = '10px'; + wrapper3.style.paddingBottom = '10px'; + wrapper3.appendChild(this.createTitle(mxResources.get('table'))); + wrapper3.appendChild(tablePanel); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + wrapper3.style.height = '70'; + } + + var tablePanel2 = stylePanel.cloneNode(false); + tablePanel2.style.paddingLeft = '0px'; + + var btns = [ + this.editorUi.toolbar.addButton('geSprite-strokecolor', mxResources.get('borderColor'), + mxUtils.bind(this, function() + { + if (currentTable != null) + { + // Converts rgb(r,g,b) values + var color = currentTable.style.borderColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + currentTable.removeAttribute('border'); + currentTable.style.border = ''; + currentTable.style.borderCollapse = ''; + } + else + { + currentTable.setAttribute('border', '1'); + currentTable.style.border = '1px solid ' + newColor; + currentTable.style.borderCollapse = 'collapse'; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fillcolor', mxResources.get('backgroundColor'), + mxUtils.bind(this, function() + { + // Converts rgb(r,g,b) values + if (currentTable != null) + { + var color = currentTable.style.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + this.editorUi.pickColor(color, function(newColor) + { + if (newColor == null || newColor == mxConstants.NONE) + { + currentTable.style.backgroundColor = ''; + } + else + { + currentTable.style.backgroundColor = newColor; + } + }); + } + }), tablePanel2), + this.editorUi.toolbar.addButton('geSprite-fit', mxResources.get('spacing'), + function() + { + if (currentTable != null) + { + var value = currentTable.getAttribute('cellPadding') || 0; + + var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) + { + if (newValue != null && newValue.length > 0) + { + currentTable.setAttribute('cellPadding', newValue); + } + else + { + currentTable.removeAttribute('cellPadding'); + } + }), mxResources.get('spacing')); + ui.showDialog(dlg.container, 300, 80, true, true); + dlg.init(); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'left'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'center'); + } + }, tablePanel2), + this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), + function() + { + if (currentTable != null) + { + currentTable.setAttribute('align', 'right'); + } + }, tablePanel2)]; + this.styleButtons(btns); + btns[2].style.marginRight = '9px'; + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(wrapper3); + mxUtils.br(wrapper3); + } + + wrapper3.appendChild(tablePanel2); + container.appendChild(wrapper3); + + tableWrapper = wrapper3; + } + + function setSelected(elt, selected) + { + if (mxClient.IS_IE && (mxClient.IS_QUIRKS || document.documentMode < 10)) + { + elt.style.filter = (selected) ? 'progid:DXImageTransform.Microsoft.Gradient('+ + 'StartColorStr=\'#c5ecff\', EndColorStr=\'#87d4fb\', GradientType=0)' : ''; + } + else + { + elt.style.backgroundImage = (selected) ? 'linear-gradient(#c5ecff 0px,#87d4fb 100%)' : ''; + } + }; + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0); + setSelected(fontStyleItems[0], (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD); + setSelected(fontStyleItems[1], (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC); + setSelected(fontStyleItems[2], (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE); + fontMenu.firstChild.nodeValue = mxUtils.htmlEntities(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTFAMILY, Menus.prototype.defaultFont)); + + setSelected(verticalItem, mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, '1') == '0'); + + if (force || document.activeElement != input) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + var align = mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); + setSelected(left, align == mxConstants.ALIGN_LEFT); + setSelected(center, align == mxConstants.ALIGN_CENTER); + setSelected(right, align == mxConstants.ALIGN_RIGHT); + + var valign = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); + setSelected(top, valign == mxConstants.ALIGN_TOP); + setSelected(middle, valign == mxConstants.ALIGN_MIDDLE); + setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM); + + var pos = mxUtils.getValue(ss.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); + var vpos = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE); + + if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'top'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_TOP) + { + positionSelect.value = 'topRight'; + } + else if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomLeft'; + } + else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottom'; + } + else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_BOTTOM) + { + positionSelect.value = 'bottomRight'; + } + else if (pos == mxConstants.ALIGN_LEFT) + { + positionSelect.value = 'left'; + } + else if (pos == mxConstants.ALIGN_RIGHT) + { + positionSelect.value = 'right'; + } + else + { + positionSelect.value = 'center'; + } + + var dir = mxUtils.getValue(ss.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION); + + if (dir == mxConstants.TEXT_DIRECTION_RTL) + { + dirSelect.value = 'rightToLeft'; + } + else if (dir == mxConstants.TEXT_DIRECTION_LTR) + { + dirSelect.value = 'leftToRight'; + } + else if (dir == mxConstants.TEXT_DIRECTION_AUTO) + { + dirSelect.value = 'automatic'; + } + + if (force || document.activeElement != globalSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2)); + globalSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != topSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0)); + topSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != rightSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0)); + rightSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != bottomSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0)); + bottomSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != leftSpacing) + { + var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0)); + leftSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + globalUpdate = this.installInputHandler(globalSpacing, mxConstants.STYLE_SPACING, 2, -999, 999, ' pt'); + topUpdate = this.installInputHandler(topSpacing, mxConstants.STYLE_SPACING_TOP, 0, -999, 999, ' pt'); + rightUpdate = this.installInputHandler(rightSpacing, mxConstants.STYLE_SPACING_RIGHT, 0, -999, 999, ' pt'); + bottomUpdate = this.installInputHandler(bottomSpacing, mxConstants.STYLE_SPACING_BOTTOM, 0, -999, 999, ' pt'); + leftUpdate = this.installInputHandler(leftSpacing, mxConstants.STYLE_SPACING_LEFT, 0, -999, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(globalSpacing, listener); + this.addKeyHandler(topSpacing, listener); + this.addKeyHandler(rightSpacing, listener); + this.addKeyHandler(bottomSpacing, listener); + this.addKeyHandler(leftSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + if (graph.cellEditor.isContentEditing()) + { + var updating = false; + + var updateCssHandler = function() + { + if (!updating) + { + updating = true; + + window.setTimeout(function() + { + var selectedElement = graph.getSelectedElement(); + var node = selectedElement; + + while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) + { + node = node.parentNode; + } + + if (node != null) + { + // Workaround for commonAncestor on range in IE11 returning parent of common ancestor + if (node == graph.cellEditor.textarea && graph.cellEditor.textarea.children.length == 1 && + graph.cellEditor.textarea.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) + { + node = graph.cellEditor.textarea.firstChild; + } + + function getRelativeLineHeight(fontSize, lineHeight, elt) + { + if (elt.style.lineHeight.substring(elt.style.lineHeight.length - 1) == '%') + { + return parseInt(elt.style.lineHeight) / 100; + } + else + { + return (lineHeight.substring(lineHeight.length - 2) == 'px') ? + parseFloat(lineHeight) / fontSize : parseInt(lineHeight); + } + }; + + function getAbsoluteFontSize(fontSize) + { + if (fontSize.substring(fontSize.length - 2) == 'px') + { + return parseFloat(fontSize); + } + else + { + return mxConstants.DEFAULT_FONTSIZE; + } + } + + //var realCss = mxUtils.getCurrentStyle(selectedElement); + var css = mxUtils.getCurrentStyle(node); + var fontSize = getAbsoluteFontSize(css.fontSize); + var lineHeight = getRelativeLineHeight(fontSize, css.lineHeight, node); + + // Finds common font size + var elts = node.getElementsByTagName('*'); + + // IE does not support containsNode + if (elts.length > 0 && window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) + { + var selection = window.getSelection(); + + for (var i = 0; i < elts.length; i++) + { + if (selection.containsNode(elts[i], true)) + { + temp = mxUtils.getCurrentStyle(elts[i]); + fontSize = Math.max(getAbsoluteFontSize(temp.fontSize), fontSize); + var lh = getRelativeLineHeight(fontSize, temp.lineHeight, elts[i]); + + if (lh != lineHeight || isNaN(lh)) + { + lineHeight = ''; + } + } + } + } + + if (css != null) + { + setSelected(fontStyleItems[0], css.fontWeight == 'bold' || graph.getParentByName(node, 'B', graph.cellEditor.textarea) != null); + setSelected(fontStyleItems[1], css.fontStyle == 'italic' || graph.getParentByName(node, 'I', graph.cellEditor.textarea) != null); + setSelected(fontStyleItems[2], graph.getParentByName(node, 'U', graph.cellEditor.textarea) != null); + setSelected(left, css.textAlign == 'left'); + setSelected(center, css.textAlign == 'center'); + setSelected(right, css.textAlign == 'right'); + setSelected(full, css.textAlign == 'justify'); + setSelected(sup, graph.getParentByName(node, 'SUP', graph.cellEditor.textarea) != null); + setSelected(sub, graph.getParentByName(node, 'SUB', graph.cellEditor.textarea) != null); + + currentTable = graph.getParentByName(node, 'TABLE', graph.cellEditor.textarea); + tableRow = (currentTable == null) ? null : graph.getParentByName(node, 'TR', currentTable); + tableCell = (currentTable == null) ? null : graph.getParentByName(node, 'TD', currentTable); + tableWrapper.style.display = (currentTable != null) ? '' : 'none'; + + if (document.activeElement != input) + { + if (node.nodeName == 'FONT' && node.getAttribute('size') == '4' && + pendingFontSize != null) + { + node.removeAttribute('size'); + node.style.fontSize = pendingFontSize + ' pt'; + pendingFontSize = null; + } + else + { + input.value = (isNaN(fontSize)) ? '' : fontSize + ' pt'; + } + + var lh = parseFloat(lineHeight); + + if (!isNaN(lh)) + { + lineHeightInput.value = Math.round(lh * 100) + ' %'; + } + else + { + lineHeightInput.value = '100 %'; + } + } + + // Converts rgb(r,g,b) values + var color = css.color.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + var color2 = css.backgroundColor.replace( + /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, + function($0, $1, $2, $3) { + return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); + }); + + // Updates the color picker for the current font + if (fontColorApply != null) + { + if (color.charAt(0) == '#') + { + currentFontColor = color; + } + else + { + currentFontColor = '#000000'; + } + + fontColorApply(currentFontColor, true); + } + + if (bgColorApply != null) + { + if (color2.charAt(0) == '#') + { + currentBgColor = color2; + } + else + { + currentBgColor = null; + } + + bgColorApply(currentBgColor, true); + } + + // Workaround for firstChild is null or not an object + // in the log which seems to be IE8- only / 29.01.15 + if (fontMenu.firstChild != null) + { + // Strips leading and trailing quotes + var ff = css.fontFamily; + + if (ff.charAt(0) == '\'') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '\'') + { + ff = ff.substring(0, ff.length - 1); + } + + if (ff.charAt(0) == '"') + { + ff = ff.substring(1); + } + + if (ff.charAt(ff.length - 1) == '"') + { + ff = ff.substring(0, ff.length - 1); + } + + fontMenu.firstChild.nodeValue = ff; + } + } + } + + updating = false; + }, 0); + } + }; + + mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) + mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); + mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); + this.listeners.push({destroy: function() + { + // No need to remove listener since textarea is destroyed after edit + }}); + updateCssHandler(); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(StyleFormatPanel, BaseFormatPanel); + +/** + * + */ +StyleFormatPanel.prototype.defaultStrokeColor = 'black'; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + if (ss.containsImage && ss.vertices.length == 1 && ss.style.shape == 'image' && + ss.style.image != null && ss.style.image.substring(0, 19) == 'data:image/svg+xml;') + { + this.container.appendChild(this.addSvgStyles(this.createPanel())); + } + + if (!ss.containsImage || ss.style.shape == 'image') + { + this.container.appendChild(this.addFill(this.createPanel())); + } + + this.container.appendChild(this.addStroke(this.createPanel())); + this.container.appendChild(this.addLineJumps(this.createPanel())); + var opacityPanel = this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_OPACITY, 41); + opacityPanel.style.paddingTop = '8px'; + opacityPanel.style.paddingBottom = '8px'; + this.container.appendChild(opacityPanel); + this.container.appendChild(this.addEffects(this.createPanel())); + var opsPanel = this.addEditOps(this.createPanel()); + + if (opsPanel.firstChild != null) + { + mxUtils.br(opsPanel); + } + + this.container.appendChild(this.addStyleOps(opsPanel)); +}; + +/** + * Use browser for parsing CSS. + */ +StyleFormatPanel.prototype.getCssRules = function(css) +{ + var doc = document.implementation.createHTMLDocument(''); + var styleElement = document.createElement('style'); + + mxUtils.setTextContent(styleElement, css); + doc.body.appendChild(styleElement); + + return styleElement.sheet.cssRules; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgStyles = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + container.style.fontWeight = 'bold'; + container.style.display = 'none'; + + try + { + var exp = ss.style.editableCssRules; + + if (exp != null) + { + var regex = new RegExp(exp); + + var data = ss.style.image.substring(ss.style.image.indexOf(',') + 1); + var xml = (window.atob) ? atob(data) : Base64.decode(data, true); + var svg = mxUtils.parseXml(xml); + + if (svg != null) + { + var styles = svg.getElementsByTagName('style'); + + for (var i = 0; i < styles.length; i++) + { + var rules = this.getCssRules(mxUtils.getTextContent(styles[i])); + + for (var j = 0; j < rules.length; j++) + { + this.addSvgRule(container, rules[j], svg, styles[i], rules, j, regex); + } + } + } + } + } + catch (e) + { + // ignore + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addSvgRule = function(container, rule, svg, styleElem, rules, ruleIndex, regex) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + + if (regex.test(rule.selectorText)) + { + function rgb2hex(rgb) + { + rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); + + return (rgb && rgb.length === 4) ? "#" + + ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + + ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; + }; + + var addStyleRule = mxUtils.bind(this, function(rule, key, label) + { + if (rule.style[key] != '') + { + var option = this.createColorOption(label + ' ' + rule.selectorText, function() + { + return rgb2hex(rule.style[key]); + }, function(color) + { + rules[ruleIndex].style[key] = color; + var cssTxt = ''; + + for (var i = 0; i < rules.length; i++) + { + cssTxt += rules[i].cssText + ' '; + } + + styleElem.textContent = cssTxt; + var xml = mxUtils.getXml(svg.documentElement); + + graph.setCellStyles(mxConstants.STYLE_IMAGE, 'data:image/svg+xml,' + + ((window.btoa) ? btoa(xml) : Base64.encode(xml, true)), + graph.getSelectionCells()); + }, '#ffffff', + { + install: function(apply) + { + // ignore + }, + destroy: function() + { + // ignore + } + }); + + container.appendChild(option); + + // Shows container if rules are added + container.style.display = ''; + } + }); + + addStyleRule(rule, 'fill', mxResources.get('fill')); + addStyleRule(rule, 'stroke', mxResources.get('line')); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEditOps = function(div) +{ + var ss = this.format.getSelectionState(); + var btn = null; + + if (this.editorUi.editor.graph.getSelectionCount() == 1) + { + btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + + div.appendChild(btn); + } + + if (ss.image) + { + var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('image').funct(); + })); + + btn2.setAttribute('title', mxResources.get('editImage')); + btn2.style.marginBottom = '2px'; + + if (btn == null) + { + btn2.style.width = '202px'; + } + else + { + btn.style.width = '100px'; + btn2.style.width = '100px'; + btn2.style.marginLeft = '2px'; + } + + div.appendChild(btn2); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addFill = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + container.style.paddingTop = '6px'; + container.style.paddingBottom = '6px'; + + // Adds gradient direction option + var gradientSelect = document.createElement('select'); + gradientSelect.style.position = 'absolute'; + gradientSelect.style.marginTop = '-2px'; + gradientSelect.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + gradientSelect.style.width = '70px'; + + // Stops events from bubbling to color option event handler + mxEvent.addListener(gradientSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var gradientPanel = this.createCellColorOption(mxResources.get('gradient'), mxConstants.STYLE_GRADIENTCOLOR, '#ffffff', function(color) + { + if (color == null || color == mxConstants.NONE) + { + gradientSelect.style.display = 'none'; + } + else + { + gradientSelect.style.display = ''; + } + }); + + var fillKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BACKGROUND : mxConstants.STYLE_FILLCOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('background') : mxResources.get('fill'); + + var fillPanel = this.createCellColorOption(label, fillKey, '#ffffff'); + fillPanel.style.fontWeight = 'bold'; + + var tmpColor = mxUtils.getValue(ss.style, fillKey, null); + gradientPanel.style.display = (tmpColor != null && tmpColor != mxConstants.NONE && + ss.fill && ss.style.shape != 'image') ? '' : 'none'; + + var directions = [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST, + mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST]; + + for (var i = 0; i < directions.length; i++) + { + var gradientOption = document.createElement('option'); + gradientOption.setAttribute('value', directions[i]); + mxUtils.write(gradientOption, mxResources.get(directions[i])); + gradientSelect.appendChild(gradientOption); + } + + gradientPanel.appendChild(gradientSelect); + + var listener = mxUtils.bind(this, function() + { + ss = this.format.getSelectionState(); + var value = mxUtils.getValue(ss.style, mxConstants.STYLE_GRADIENT_DIRECTION, mxConstants.DIRECTION_SOUTH); + + // Handles empty string which is not allowed as a value + if (value == '') + { + value = mxConstants.DIRECTION_SOUTH; + } + + gradientSelect.value = value; + container.style.display = (ss.fill) ? '' : 'none'; + + var fillColor = mxUtils.getValue(ss.style, mxConstants.STYLE_FILLCOLOR, null); + + if (!ss.fill || ss.containsImage || fillColor == null || fillColor == mxConstants.NONE || ss.style.shape == 'filledEdge') + { + gradientPanel.style.display = 'none'; + } + else + { + gradientPanel.style.display = ''; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + mxEvent.addListener(gradientSelect, 'change', function(evt) + { + graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION, gradientSelect.value, graph.getSelectionCells()); + mxEvent.consume(evt); + }); + + container.appendChild(fillPanel); + container.appendChild(gradientPanel); + + // Adds custom colors + var custom = this.getCustomColors(); + + for (var i = 0; i < custom.length; i++) + { + container.appendChild(this.createCellColorOption(custom[i].title, custom[i].key, custom[i].defaultValue)); + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.getCustomColors = function() +{ + var ss = this.format.getSelectionState(); + var result = []; + + if (ss.style.shape == 'swimlane') + { + result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); + } + + return result; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStroke = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + var ss = this.format.getSelectionState(); + + container.style.paddingTop = '4px'; + container.style.paddingBottom = '4px'; + container.style.whiteSpace = 'normal'; + + var colorPanel = document.createElement('div'); + colorPanel.style.fontWeight = 'bold'; + + // Adds gradient direction option + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '72px'; + styleSelect.style.width = '80px'; + + var styles = ['sharp', 'rounded', 'curved']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED]; + // Default for rounded is 1 + var values = ['0', null]; + + if (styleSelect.value == 'rounded') + { + values = ['1', null]; + } + else if (styleSelect.value == 'curved') + { + values = [null, '1']; + } + + for (var i = 0; i < keys.length; i++) + { + graph.setCellStyles(keys[i], values[i], graph.getSelectionCells()); + } + + ui.fireEvent(new mxEventObject('styleChanged', 'keys', keys, + 'values', values, 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + var strokeKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BORDER : mxConstants.STYLE_STROKECOLOR; + var label = (ss.style.shape == 'image') ? mxResources.get('border') : mxResources.get('line'); + + var lineColor = this.createCellColorOption(label, strokeKey, '#000000'); + lineColor.appendChild(styleSelect); + colorPanel.appendChild(lineColor); + + // Used if only edges selected + var stylePanel = colorPanel.cloneNode(false); + stylePanel.style.fontWeight = 'normal'; + stylePanel.style.whiteSpace = 'nowrap'; + stylePanel.style.position = 'relative'; + stylePanel.style.paddingLeft = '16px' + stylePanel.style.marginBottom = '2px'; + stylePanel.style.marginTop = '2px'; + stylePanel.className = 'geToolbarContainer'; + + var addItem = mxUtils.bind(this, function(menu, width, cssName, keys, values) + { + var item = this.editorUi.menus.styleChange(menu, '', keys, values, 'geIcon', null); + + var pat = document.createElement('div'); + pat.style.width = width + 'px'; + pat.style.height = '1px'; + pat.style.borderBottom = '1px ' + cssName + ' ' + this.defaultStrokeColor; + pat.style.paddingTop = '6px'; + + item.firstChild.firstChild.style.padding = '0px 4px 0px 4px'; + item.firstChild.firstChild.style.width = width + 'px'; + item.firstChild.firstChild.appendChild(pat); + + return item; + }); + + var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 75, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 75, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + // Used for mixed selection (vertices and edges) + var altStylePanel = stylePanel.cloneNode(false); + + var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-connection', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) + { + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], [null, null, null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); + this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); + })); + + var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) + { + addItem(menu, 33, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); + addItem(menu, 33, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); + addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); + })); + + var stylePanel2 = stylePanel.cloneNode(false); + + // Stroke width + var input = document.createElement('input'); + input.style.textAlign = 'right'; + input.style.marginTop = '2px'; + input.style.width = '41px'; + input.setAttribute('title', mxResources.get('linewidth')); + + stylePanel.appendChild(input); + + var altInput = input.cloneNode(true); + altStylePanel.appendChild(altInput); + + function update(evt) + { + // Maximum stroke width is 999 + var value = parseInt(input.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + input.value = value + ' pt'; + mxEvent.consume(evt); + }; + + function altUpdate(evt) + { + // Maximum stroke width is 999 + var value = parseInt(altInput.value); + value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); + + if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) + { + graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], + 'values', [value], 'cells', graph.getSelectionCells())); + } + + altInput.value = value + ' pt'; + mxEvent.consume(evt); + }; + + var stepper = this.createStepper(input, update, 1, 9); + stepper.style.display = input.style.display; + stepper.style.marginTop = '2px'; + stylePanel.appendChild(stepper); + + var altStepper = this.createStepper(altInput, altUpdate, 1, 9); + altStepper.style.display = altInput.style.display; + altStepper.style.marginTop = '2px'; + altStylePanel.appendChild(altStepper); + + if (!mxClient.IS_QUIRKS) + { + input.style.position = 'absolute'; + input.style.right = '32px'; + input.style.height = '15px'; + stepper.style.right = '20px'; + + altInput.style.position = 'absolute'; + altInput.style.right = '32px'; + altInput.style.height = '15px'; + altStepper.style.right = '20px'; + } + else + { + input.style.height = '17px'; + altInput.style.height = '17px'; + } + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + mxEvent.addListener(altInput, 'blur', altUpdate); + mxEvent.addListener(altInput, 'change', altUpdate); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(stylePanel2); + mxUtils.br(stylePanel2); + } + + var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape != 'arrow') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); + + if (ss.style.shape == 'connector') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); + } + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); + } + })); + + var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-startclassic', mxResources.get('linestart'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-startclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-startclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-startopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-startopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['openAsync', 0], 'geIcon geSprite geSprite-startopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-startblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-startblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 1], 'geIcon geSprite geSprite-startasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-startoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-startdiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-startthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-startclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-startclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-startblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 0], 'geIcon geSprite geSprite-startasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-startovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-startdiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-startthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['dash', 0], 'geIcon geSprite geSprite-startdash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['cross', 0], 'geIcon geSprite geSprite-startcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-startcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circle', 1], 'geIcon geSprite geSprite-startcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERone', 0], 'geIcon geSprite geSprite-starterone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-starteronetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmany', 0], 'geIcon geSprite geSprite-startermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-starteronetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-starteroneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-startermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) + { + if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') + { + var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon', null, false); + item.setAttribute('title', mxResources.get('none')); + item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; + + if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-endclassic', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-endclassicthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-endopen', null, false).setAttribute('title', mxResources.get('openArrow')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-endopenthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['openAsync', 0], 'geIcon geSprite geSprite-endopenasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-endblock', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-endblockthin', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 1], 'geIcon geSprite geSprite-endasync', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-endoval', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-enddiamond', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-endthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-endclassictrans', null, false).setAttribute('title', mxResources.get('classic')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-endclassicthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-endblockthintrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 0], 'geIcon geSprite geSprite-endasynctrans', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-endovaltrans', null, false).setAttribute('title', mxResources.get('oval')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-enddiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-endthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); + + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['dash', 0], 'geIcon geSprite geSprite-enddash', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['cross', 0], 'geIcon geSprite geSprite-endcross', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-endcircleplus', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circle', 1], 'geIcon geSprite geSprite-endcircle', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERone', 0], 'geIcon geSprite geSprite-enderone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-enderonetoone', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmany', 0], 'geIcon geSprite geSprite-endermany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-enderonetomany', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-enderoneopt', null, false); + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-endermanyopt', null, false); + } + else + { + this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); + } + } + })); + + this.addArrow(edgeShape, 8); + this.addArrow(edgeStyle); + this.addArrow(lineStart); + this.addArrow(lineEnd); + + var symbol = this.addArrow(pattern, 9); + symbol.className = 'geIcon'; + symbol.style.width = '84px'; + + var altSymbol = this.addArrow(altPattern, 9); + altSymbol.className = 'geIcon'; + altSymbol.style.width = '22px'; + + var solid = document.createElement('div'); + solid.style.width = '85px'; + solid.style.height = '1px'; + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + solid.style.marginBottom = '9px'; + symbol.appendChild(solid); + + var altSolid = document.createElement('div'); + altSolid.style.width = '23px'; + altSolid.style.height = '1px'; + altSolid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + altSolid.style.marginBottom = '9px'; + altSymbol.appendChild(altSolid); + + pattern.style.height = '15px'; + altPattern.style.height = '15px'; + edgeShape.style.height = '15px'; + edgeStyle.style.height = '17px'; + lineStart.style.marginLeft = '3px'; + lineStart.style.height = '17px'; + lineEnd.style.marginLeft = '3px'; + lineEnd.style.height = '17px'; + + container.appendChild(colorPanel); + container.appendChild(altStylePanel); + container.appendChild(stylePanel); + + var arrowPanel = stylePanel.cloneNode(false); + arrowPanel.style.paddingBottom = '6px'; + arrowPanel.style.paddingTop = '4px'; + arrowPanel.style.fontWeight = 'normal'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '2px'; + span.style.fontWeight = 'normal'; + span.style.width = '76px'; + + mxUtils.write(span, mxResources.get('lineend')); + arrowPanel.appendChild(span); + + var endSpacingUpdate, endSizeUpdate; + var endSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + endSpacingUpdate.apply(this, arguments); + }); + var endSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + endSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + + var spacer = document.createElement('div'); + spacer.style.height = '8px'; + arrowPanel.appendChild(spacer); + + span = span.cloneNode(false); + mxUtils.write(span, mxResources.get('linestart')); + arrowPanel.appendChild(span); + + var startSpacingUpdate, startSizeUpdate; + var startSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() + { + startSpacingUpdate.apply(this, arguments); + }); + var startSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() + { + startSizeUpdate.apply(this, arguments); + }); + + mxUtils.br(arrowPanel); + this.addLabel(arrowPanel, mxResources.get('spacing'), 74, 50); + this.addLabel(arrowPanel, mxResources.get('size'), 20, 50); + mxUtils.br(arrowPanel); + + var perimeterPanel = colorPanel.cloneNode(false); + perimeterPanel.style.fontWeight = 'normal'; + perimeterPanel.style.position = 'relative'; + perimeterPanel.style.paddingLeft = '16px' + perimeterPanel.style.marginBottom = '2px'; + perimeterPanel.style.marginTop = '6px'; + perimeterPanel.style.borderWidth = '0px'; + perimeterPanel.style.paddingBottom = '18px'; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.marginLeft = '3px'; + span.style.marginBottom = '12px'; + span.style.marginTop = '1px'; + span.style.fontWeight = 'normal'; + span.style.width = '120px'; + mxUtils.write(span, mxResources.get('perimeter')); + perimeterPanel.appendChild(span); + + var perimeterUpdate; + var perimeterSpacing = this.addUnitInput(perimeterPanel, 'pt', 20, 41, function() + { + perimeterUpdate.apply(this, arguments); + }); + + if (ss.edges.length == graph.getSelectionCount()) + { + container.appendChild(stylePanel2); + + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + mxUtils.br(container); + } + + container.appendChild(arrowPanel); + } + else if (ss.vertices.length == graph.getSelectionCount()) + { + if (mxClient.IS_QUIRKS) + { + mxUtils.br(container); + } + + container.appendChild(perimeterPanel); + } + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + var color = mxUtils.getValue(ss.style, strokeKey, null); + + if (force || document.activeElement != input) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != altInput) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); + altInput.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + styleSelect.style.visibility = (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') ? '' : 'hidden'; + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + styleSelect.value = 'curved'; + } + else if (mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == '1') + { + styleSelect.value = 'rounded'; + } + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == '1') + { + if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null) + { + solid.style.borderBottom = '1px dashed ' + this.defaultStrokeColor; + } + else + { + solid.style.borderBottom = '1px dotted ' + this.defaultStrokeColor; + } + } + else + { + solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; + } + + altSolid.style.borderBottom = solid.style.borderBottom; + + // Updates toolbar icon for edge style + var edgeStyleDiv = edgeStyle.getElementsByTagName('div')[0]; + var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null); + + if (mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == '1') + { + es = null; + } + + if (es == 'orthogonalEdgeStyle' && mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') + { + edgeStyleDiv.className = 'geSprite geSprite-curved'; + } + else if (es == 'straight' || es == 'none' || es == null) + { + edgeStyleDiv.className = 'geSprite geSprite-straight'; + } + else if (es == 'entityRelationEdgeStyle') + { + edgeStyleDiv.className = 'geSprite geSprite-entity'; + } + else if (es == 'elbowEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalelbow' : 'geSprite-horizontalelbow'); + } + else if (es == 'isometricEdgeStyle') + { + edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, + mxConstants.STYLE_ELBOW, null) == 'vertical') ? + 'geSprite-verticalisometric' : 'geSprite-horizontalisometric'); + } + else + { + edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; + } + + // Updates icon for edge shape + var edgeShapeDiv = edgeShape.getElementsByTagName('div')[0]; + + if (ss.style.shape == 'link') + { + edgeShapeDiv.className = 'geSprite geSprite-linkedge'; + } + else if (ss.style.shape == 'flexArrow') + { + edgeShapeDiv.className = 'geSprite geSprite-arrow'; + } + else if (ss.style.shape == 'arrow') + { + edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; + } + else + { + edgeShapeDiv.className = 'geSprite geSprite-connection'; + } + + if (ss.edges.length == graph.getSelectionCount()) + { + altStylePanel.style.display = ''; + stylePanel.style.display = 'none'; + } + else + { + altStylePanel.style.display = 'none'; + stylePanel.style.display = ''; + } + + function updateArrow(marker, fill, elt, prefix) + { + var markerDiv = elt.getElementsByTagName('div')[0]; + + markerDiv.className = ui.getCssClassForMarker(prefix, ss.style.shape, marker, fill); + + if (markerDiv.className == 'geSprite geSprite-noarrow') + { + markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('none')); + markerDiv.style.backgroundImage = 'none'; + markerDiv.style.verticalAlign = 'top'; + markerDiv.style.marginTop = '5px'; + markerDiv.style.fontSize = '10px'; + markerDiv.style.filter = 'none'; + markerDiv.style.color = this.defaultStrokeColor; + markerDiv.nextSibling.style.marginTop = '0px'; + } + + return markerDiv; + }; + + var sourceDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null), + mxUtils.getValue(ss.style, 'startFill', '1'), lineStart, 'start'); + var targetDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null), + mxUtils.getValue(ss.style, 'endFill', '1'), lineEnd, 'end'); + + // Special cases for markers + if (ss.style.shape == 'arrow') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-endblocktrans'; + } + else if (ss.style.shape == 'link') + { + sourceDiv.className = 'geSprite geSprite-noarrow'; + targetDiv.className = 'geSprite geSprite-noarrow'; + } + + mxUtils.setOpacity(edgeStyle, (ss.style.shape == 'arrow') ? 30 : 100); + + if (ss.style.shape != 'connector' && ss.style.shape != 'flexArrow' && ss.style.shape != 'filledEdge') + { + mxUtils.setOpacity(lineStart, 30); + mxUtils.setOpacity(lineEnd, 30); + } + else + { + mxUtils.setOpacity(lineStart, 100); + mxUtils.setOpacity(lineEnd, 100); + } + + if (force || document.activeElement != startSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE)); + startSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0)); + startSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != endSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)); + endSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != startSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0)); + endSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + + if (force || document.activeElement != perimeterSpacing) + { + var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0)); + perimeterSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + startSizeUpdate = this.installInputHandler(startSize, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + startSpacingUpdate = this.installInputHandler(startSpacing, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0, -999, 999, ' pt'); + endSizeUpdate = this.installInputHandler(endSize, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); + endSpacingUpdate = this.installInputHandler(endSpacing, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0, -999, 999, ' pt'); + perimeterUpdate = this.installInputHandler(perimeterSpacing, mxConstants.STYLE_PERIMETER_SPACING, 0, 0, 999, ' pt'); + + this.addKeyHandler(input, listener); + this.addKeyHandler(startSize, listener); + this.addKeyHandler(startSpacing, listener); + this.addKeyHandler(endSize, listener); + this.addKeyHandler(endSpacing, listener); + this.addKeyHandler(perimeterSpacing, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return container; +}; + +/** + * Adds UI for configuring line jumps. + */ +StyleFormatPanel.prototype.addLineJumps = function(container) +{ + var ss = this.format.getSelectionState(); + + if (Graph.lineJumpsEnabled && ss.edges.length > 0 && + ss.vertices.length == 0 && ss.lineJumps) + { + container.style.padding = '8px 0px 24px 18px'; + + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + var span = document.createElement('div'); + span.style.position = 'absolute'; + span.style.fontWeight = 'bold'; + span.style.width = '80px'; + + mxUtils.write(span, mxResources.get('lineJumps')); + container.appendChild(span); + + var styleSelect = document.createElement('select'); + styleSelect.style.position = 'absolute'; + styleSelect.style.marginTop = '-2px'; + styleSelect.style.right = '76px'; + styleSelect.style.width = '62px'; + + var styles = ['none', 'arc', 'gap', 'sharp']; + + for (var i = 0; i < styles.length; i++) + { + var styleOption = document.createElement('option'); + styleOption.setAttribute('value', styles[i]); + mxUtils.write(styleOption, mxResources.get(styles[i])); + styleSelect.appendChild(styleOption); + } + + mxEvent.addListener(styleSelect, 'change', function(evt) + { + graph.getModel().beginUpdate(); + try + { + graph.setCellStyles('jumpStyle', styleSelect.value, graph.getSelectionCells()); + ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['jumpStyle'], + 'values', [styleSelect.value], 'cells', graph.getSelectionCells())); + } + finally + { + graph.getModel().endUpdate(); + } + + mxEvent.consume(evt); + }); + + // Stops events from bubbling to color option event handler + mxEvent.addListener(styleSelect, 'click', function(evt) + { + mxEvent.consume(evt); + }); + + container.appendChild(styleSelect); + + var jumpSizeUpdate; + + var jumpSize = this.addUnitInput(container, 'pt', 22, 33, function() + { + jumpSizeUpdate.apply(this, arguments); + }); + + jumpSizeUpdate = this.installInputHandler(jumpSize, 'jumpSize', + Graph.defaultJumpSize, 0, 999, ' pt'); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + styleSelect.value = mxUtils.getValue(ss.style, 'jumpStyle', 'none'); + + if (force || document.activeElement != jumpSize) + { + var tmp = parseInt(mxUtils.getValue(ss.style, 'jumpSize', Graph.defaultJumpSize)); + jumpSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; + } + }); + + this.addKeyHandler(jumpSize, listener); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + } + else + { + container.style.display = 'none'; + } + + return container; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addEffects = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + div.style.paddingTop = '0px'; + div.style.paddingBottom = '2px'; + + var table = document.createElement('table'); + + if (mxClient.IS_QUIRKS) + { + table.style.fontSize = '1em'; + } + + table.style.width = '100%'; + table.style.fontWeight = 'bold'; + table.style.paddingRight = '20px'; + var tbody = document.createElement('tbody'); + var row = document.createElement('tr'); + row.style.padding = '0px'; + var left = document.createElement('td'); + left.style.padding = '0px'; + left.style.width = '50%'; + left.setAttribute('valign', 'top'); + + var right = left.cloneNode(true); + right.style.paddingLeft = '8px'; + row.appendChild(left); + row.appendChild(right); + tbody.appendChild(row); + table.appendChild(tbody); + div.appendChild(table); + + var current = left; + var count = 0; + + var addOption = mxUtils.bind(this, function(label, key, defaultValue) + { + var opt = this.createCellOption(label, key, defaultValue); + opt.style.width = '100%'; + current.appendChild(opt); + current = (current == left) ? right : left; + count++; + }); + + var listener = mxUtils.bind(this, function(sender, evt, force) + { + ss = this.format.getSelectionState(); + + left.innerHTML = ''; + right.innerHTML = ''; + current = left; + + if (ss.rounded) + { + addOption(mxResources.get('rounded'), mxConstants.STYLE_ROUNDED, 0); + } + + if (ss.style.shape == 'swimlane') + { + addOption(mxResources.get('divider'), 'swimlaneLine', 1); + } + + if (!ss.containsImage) + { + addOption(mxResources.get('shadow'), mxConstants.STYLE_SHADOW, 0); + } + + if (ss.glass) + { + addOption(mxResources.get('glass'), mxConstants.STYLE_GLASS, 0); + } + + if (ss.comic) + { + addOption(mxResources.get('comic'), 'comic', 0); + } + + if (count == 0) + { + div.style.display = 'none'; + } + }); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + listener(); + + return div; +} + +/** + * Adds the label menu items to the given menu and parent. + */ +StyleFormatPanel.prototype.addStyleOps = function(div) +{ + div.style.paddingTop = '10px'; + div.style.paddingBottom = '10px'; + + var btn = mxUtils.button(mxResources.get('setAsDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('setAsDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('setAsDefaultStyle') + ' (' + this.editorUi.actions.get('setAsDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(DiagramFormatPanel, BaseFormatPanel); + +/** + * Switch to disable page view. + */ +DiagramFormatPanel.showPageView = true; + +/** + * Specifies if the background image option should be shown. Default is true. + */ +DiagramFormatPanel.prototype.showBackgroundImageOption = true; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.init = function() +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + this.container.appendChild(this.addView(this.createPanel())); + + if (graph.isEnabled()) + { + this.container.appendChild(this.addOptions(this.createPanel())); + this.container.appendChild(this.addPaperSize(this.createPanel())); + this.container.appendChild(this.addStyleOps(this.createPanel())); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addView = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('view'))); + + // Grid + this.addGridOption(div); + + if (graph.isEnabled()) + { + // Page View + if (DiagramFormatPanel.showPageView) + { + div.appendChild(this.createOption(mxResources.get('pageView'), function() + { + return graph.pageVisible; + }, function(checked) + { + ui.actions.get('pageView').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.pageVisible); + }; + + ui.addListener('pageViewChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + // Background + var bg = this.createColorOption(mxResources.get('background'), function() + { + return graph.background; + }, function(color) + { + var change = new ChangePageSetup(ui, color); + change.ignoreImage = true; + + graph.model.execute(change); + }, '#ffffff', + { + install: function(apply) + { + this.listener = function() + { + apply(graph.background); + }; + + ui.addListener('backgroundColorChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + if (this.showBackgroundImageOption) + { + var btn = mxUtils.button(mxResources.get('image'), function(evt) + { + ui.showBackgroundImageDialog(); + mxEvent.consume(evt); + }) + + btn.style.position = 'absolute'; + btn.className = 'geColorBtn'; + btn.style.marginTop = '-4px'; + btn.style.paddingBottom = (document.documentMode == 11 || mxClient.IS_MT) ? '0px' : '2px'; + btn.style.height = '22px'; + btn.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; + btn.style.width = '56px'; + + bg.appendChild(btn); + } + + div.appendChild(bg); + } + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addOptions = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + if (graph.isEnabled()) + { + // Connection arrows + div.appendChild(this.createOption(mxResources.get('connectionArrows'), function() + { + return graph.connectionArrowsEnabled; + }, function(checked) + { + ui.actions.get('connectionArrows').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionArrowsEnabled); + }; + + ui.addListener('connectionArrowsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Connection points + div.appendChild(this.createOption(mxResources.get('connectionPoints'), function() + { + return graph.connectionHandler.isEnabled(); + }, function(checked) + { + ui.actions.get('connectionPoints').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.connectionHandler.isEnabled()); + }; + + ui.addListener('connectionPointsChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + + // Guides + div.appendChild(this.createOption(mxResources.get('guides'), function() + { + return graph.graphHandler.guidesEnabled; + }, function(checked) + { + ui.actions.get('guides').funct(); + }, + { + install: function(apply) + { + this.listener = function() + { + apply(graph.graphHandler.guidesEnabled); + }; + + ui.addListener('guidesEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } + + return div; +}; + +/** + * + */ +DiagramFormatPanel.prototype.addGridOption = function(container) +{ + var ui = this.editorUi; + var graph = ui.editor.graph; + + var input = document.createElement('input'); + input.style.position = 'absolute'; + input.style.textAlign = 'right'; + input.style.width = '38px'; + input.value = graph.getGridSize() + ' pt'; + + var stepper = this.createStepper(input, update); + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + mxEvent.addListener(input, 'keydown', function(e) + { + if (e.keyCode == 13) + { + graph.container.focus(); + mxEvent.consume(e); + } + else if (e.keyCode == 27) + { + input.value = graph.getGridSize(); + graph.container.focus(); + mxEvent.consume(e); + } + }); + + function update(evt) + { + var value = parseInt(input.value); + value = Math.max(1, (isNaN(value)) ? 10 : value); + + if (value != graph.getGridSize()) + { + graph.setGridSize(value) + } + + input.value = value + ' pt'; + mxEvent.consume(evt); + }; + + mxEvent.addListener(input, 'blur', update); + mxEvent.addListener(input, 'change', update); + + if (mxClient.IS_SVG) + { + input.style.marginTop = '-2px'; + input.style.right = '84px'; + stepper.style.marginTop = '-16px'; + stepper.style.right = '72px'; + + var panel = this.createColorOption(mxResources.get('grid'), function() + { + var color = graph.view.gridColor; + + return (graph.isGridEnabled()) ? color : null; + }, function(color) + { + if (color == mxConstants.NONE) + { + graph.setGridEnabled(false); + } + else + { + graph.setGridEnabled(true); + ui.setGridColor(color); + } + + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, '#e0e0e0', + { + install: function(apply) + { + this.listener = function() + { + apply((graph.isGridEnabled()) ? graph.view.gridColor : null); + }; + + ui.addListener('gridColorChanged', this.listener); + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + }); + + panel.appendChild(input); + panel.appendChild(stepper); + container.appendChild(panel); + } + else + { + input.style.marginTop = '2px'; + input.style.right = '32px'; + stepper.style.marginTop = '2px'; + stepper.style.right = '20px'; + + container.appendChild(input); + container.appendChild(stepper); + + container.appendChild(this.createOption(mxResources.get('grid'), function() + { + return graph.isGridEnabled(); + }, function(checked) + { + graph.setGridEnabled(checked); + + if (graph.isGridEnabled()) + { + graph.view.gridColor = '#e0e0e0'; + } + + ui.fireEvent(new mxEventObject('gridEnabledChanged')); + }, + { + install: function(apply) + { + this.listener = function() + { + input.style.display = (graph.isGridEnabled()) ? '' : 'none'; + stepper.style.display = input.style.display; + + apply(graph.isGridEnabled()); + }; + + ui.addListener('gridEnabledChanged', this.listener); + }, + destroy: function() + { + ui.removeListener(this.listener); + } + })); + } +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addDocumentProperties = function(div) +{ + // Hook for subclassers + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('options'))); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addPaperSize = function(div) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + + div.appendChild(this.createTitle(mxResources.get('paperSize'))); + + var accessor = PageSetupDialog.addPageFormatPanel(div, 'formatpanel', graph.pageFormat, function(pageFormat) + { + if (graph.pageFormat == null || graph.pageFormat.width != pageFormat.width || + graph.pageFormat.height != pageFormat.height) + { + var change = new ChangePageSetup(ui, null, null, pageFormat); + change.ignoreColor = true; + change.ignoreImage = true; + + graph.model.execute(change); + } + }); + + this.addKeyHandler(accessor.widthInput, function() + { + accessor.set(graph.pageFormat); + }); + this.addKeyHandler(accessor.heightInput, function() + { + accessor.set(graph.pageFormat); + }); + + var listener = function() + { + accessor.set(graph.pageFormat); + }; + + ui.addListener('pageFormatChanged', listener); + this.listeners.push({destroy: function() { ui.removeListener(listener); }}); + + graph.getModel().addListener(mxEvent.CHANGE, listener); + this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.addStyleOps = function(div) +{ + var btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('editData').funct(); + })); + + btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); + btn.style.width = '202px'; + btn.style.marginBottom = '2px'; + div.appendChild(btn); + + mxUtils.br(div); + + btn = mxUtils.button(mxResources.get('clearDefaultStyle'), mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('clearDefaultStyle').funct(); + })); + + btn.setAttribute('title', mxResources.get('clearDefaultStyle') + ' (' + this.editorUi.actions.get('clearDefaultStyle').shortcut + ')'); + btn.style.width = '202px'; + div.appendChild(btn); + + return div; +}; + +/** + * Adds the label menu items to the given menu and parent. + */ +DiagramFormatPanel.prototype.destroy = function() +{ + BaseFormatPanel.prototype.destroy.apply(this, arguments); + + if (this.gridEnabledListener) + { + this.editorUi.removeListener(this.gridEnabledListener); + this.gridEnabledListener = null; + } +}; diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/ModelSelectPath.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/ModelSelectPath.js new file mode 100644 index 00000000..202c6cc7 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/ModelSelectPath.js @@ -0,0 +1,430 @@ +/** + * ============================================================================================================================== + * 初始化操作 + * ============================================================================================================================== + */ +//将菜单栏的高度设置为0(即不显示菜单栏) +EditorUi.prototype.menubarHeight = 0; +//设置 sliderbar 宽度 +EditorUi.prototype.hsplitPosition = (screen.width <= 640) ? 118 : 150; +//覆盖保存方法 +EditorUi.prototype.saveFile = function(forceDialog){ + var xml =mxUtils.getPrettyXml(this.editor.getGraphXml()); + $.post(window.mxSaveXmlUrl,{xml:xml},function(data){ + $.tips('保存成功!',2000); + }); +} + +EditorUi.prototype.afterCreated =function(){ + //防止连线未连接到节点上,即连线必须要连接两个节点 + this.editor.graph.setAllowDanglingEdges(false); + this.editor.graph.setDisconnectOnMove(false); + + //设置 cell 不可编辑 + this.editor.graph.isCellEditable = function(cell){ + return false; + } +} + +/** + * ============================================================================================================================== + * 模型列表加载器 + * ============================================================================================================================== + */ +ModelLoader =function(){ + this.list =[]; +} +ModelLoader.prototype.load =function(){ + var xhr =mxUtils.load("[(#{/tools/graph-editor/getModelList})]"); + this.list =JSON.parse(xhr.getText()); +} +ModelLoader.prototype.getList =function(){ + return this.list; +} +ModelLoader.prototype.getNameByCode =function(code){ + if(this.list && this.list.length>0){ + for(var i=0;i0){ + var type =cells[0].getAttribute('type'); + if(cells[0].isEdge() || type=='Condition' || type=='Model'){ + tabs.splice(0, 0, 'configure'); + } + } + return tabs; +} + +//重写插入边方法,加入自定义对象 +var insertEdge = mxConnectionHandler.prototype.insertEdge; +mxConnectionHandler.prototype.insertEdge = function(parent, id, value, source, target, style){ + var edge = mxUtils.createXmlDocument().createElement('Edge'); + edge.setAttribute('label', ''); + edge.setAttribute('value', ''); + edge.setAttribute('valueType', ''); + value = edge; + //验证模型节点不能连接模型节点 + var sourceNodeType = source.getAttribute("type").toLowerCase(); + var targetNodeType = target.getAttribute("type").toLowerCase(); + if(sourceNodeType == 'model' && targetNodeType == "model"){ + $.alert("模型节点之间无法连接!"); + //$.tips('模型节点之间无法连接!',2000); + return null; + } + return insertEdge.apply(this, arguments); +}; + + +/** + * ============================================================================================================================== + * 自定义配置面板 + * ============================================================================================================================== + */ +ConfigureFormatPanel = function(format, editorUi, container) +{ + BaseFormatPanel.call(this, format, editorUi, container); + this.init(); +}; + +mxUtils.extend(ConfigureFormatPanel, BaseFormatPanel); + +ConfigureFormatPanel.prototype.init = function() +{ + this.container.style.borderBottom = 'none'; + this.addUi(this.container); +}; + +ConfigureFormatPanel.prototype.addUi = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var selectedCells =graph.getSelectionCells(); + if(selectedCells && selectedCells.length>0){ + var selectedCell =selectedCells[0]; + var formPanel =this.createFormPanel(); + container.appendChild(formPanel); + + if(selectedCell.isEdge()){//当前选择的是连线 + this.createConnectionUi(graph,formPanel,selectedCell); + }else if(mxUtils.isNode(selectedCell.value)){//当前选择的是节点 + var type =selectedCell.getAttribute('type'); + if(type=='Model'){//当前选择的是模型节点 + this.createModelNodeUi(graph,formPanel,selectedCell); + }else if(type=='Condition'){//当前选择的是判断节点 + this.createConditionNodeUi(graph,formPanel,selectedCell); + } + } + } + return container; +} + +ConfigureFormatPanel.prototype.createModelNodeUi = function(graph,formPanel,selectedCell){ + var selectModelLabel =this.createLabel('请选择模型'); + formPanel.appendChild(selectModelLabel); + + var selectModelCombobox =this.createCombobox(); + formPanel.appendChild(selectModelCombobox); + + this.addOption(selectModelCombobox,'',''); + for (var i = 0; i < modelLoader.list.length; i++) + { + var model =modelLoader.list[i]; + this.addOption(selectModelCombobox,model.code,model.name); + } + + //设置下拉框选择框的值 + selectModelCombobox.value =selectedCell.getAttribute('code'); + + if (!graph.isEditing()) + { + mxEvent.addListener(selectModelCombobox, 'change', mxUtils.bind(this,function(evt) + { + var code =selectModelCombobox.value; + var name =modelLoader.getNameByCode(code); + if(code){ + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('code', code); + elt.setAttribute('name', name); + elt.setAttribute('label', name); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + mxEvent.consume(evt); + })); + } +} + +ConfigureFormatPanel.prototype.createConditionNodeUi = function(graph,formPanel,selectedCell){ + var expressionLabel =this.createLabel('表达式'); + formPanel.appendChild(expressionLabel); + + var expressionText =this.createText(); + expressionText.value =selectedCell.getAttribute('expression') || ''; + formPanel.appendChild(expressionText); + + if (!graph.isEditing()) + { + var applyHandler = function() + { + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('expression', expressionText.value); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + }; + + mxEvent.addListener(expressionText, 'keypress', function (evt) + { + if (evt.keyCode == /*enter*/13 && !mxEvent.isShiftDown(evt)){ + conditionText.blur(); + } + }); + + if (mxClient.IS_IE){ + mxEvent.addListener(expressionText, 'focusout', applyHandler); + }else{ + mxEvent.addListener(expressionText, 'blur', applyHandler); + } + } + + var conditionLabel =this.createLabel('描述'); + formPanel.appendChild(conditionLabel); + + var conditionText =this.createText(); + conditionText.value =selectedCell.getAttribute('condition') || ''; + formPanel.appendChild(conditionText); + + if (!graph.isEditing()) + { + var applyHandler = function() + { + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('condition', conditionText.value); + elt.setAttribute('label', conditionText.value); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + }; + + mxEvent.addListener(conditionText, 'keypress', function (evt) + { + if (evt.keyCode == /*enter*/13 && !mxEvent.isShiftDown(evt)){ + conditionText.blur(); + } + }); + + if (mxClient.IS_IE){ + mxEvent.addListener(conditionText, 'focusout', applyHandler); + }else{ + mxEvent.addListener(conditionText, 'blur', applyHandler); + } + } +} + +ConfigureFormatPanel.prototype.createConnectionUi = function(graph,formPanel,selectedCell){ + + var connectionTypeLabel =this.createLabel('值类型'); + formPanel.appendChild(connectionTypeLabel); + + var valueTypeCombobox =this.createCombobox(); + this.addOption(valueTypeCombobox,'string','字符值'); + this.addOption(valueTypeCombobox,'number','数值'); + this.addOption(valueTypeCombobox,'boolean','布尔值'); + valueTypeCombobox.value=selectedCell.getValue().getAttribute("valueType") || ''; + formPanel.appendChild(valueTypeCombobox); + + var connectionLabel =this.createLabel('条件值'); + formPanel.appendChild(connectionLabel); + + var valueText =this.createText(); + valueText.value =selectedCell.getValue().getAttribute("value") || ''; + formPanel.appendChild(valueText); + + if (!graph.isEditing()) + { + var applyHandler = function() + { + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + var selIndex = valueTypeCombobox.selectedIndex; + elt.setAttribute('label', '('+ valueTypeCombobox.options[selIndex].text+') '+valueText.value); + elt.setAttribute('value', valueText.value); + elt.setAttribute('valueType', valueTypeCombobox.value); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + }; + + mxEvent.addListener(valueText, 'keypress', function (evt) + { + if (evt.keyCode == /*enter*/13 && !mxEvent.isShiftDown(evt)){ + valueText.blur(); + } + }); + + if (mxClient.IS_IE){ + mxEvent.addListener(valueTypeCombobox, 'focusout', applyHandler); + mxEvent.addListener(valueText, 'focusout', applyHandler); + }else{ + mxEvent.addListener(valueTypeCombobox, 'blur', applyHandler); + mxEvent.addListener(valueText, 'focusout', applyHandler); + } + } +} + +ConfigureFormatPanel.prototype.createFormPanel = function(){ + var formPanel = this.createPanel(); + formPanel.style.paddingTop = '2px'; + formPanel.style.paddingBottom = '2px'; + formPanel.style.position = 'relative'; + formPanel.style.marginLeft = '-2px'; + formPanel.style.borderWidth = '0px'; + formPanel.className = 'geToolbarContainer'; + + if (mxClient.IS_QUIRKS){ + formPanel.style.display = 'block'; + } + return formPanel; +} + +ConfigureFormatPanel.prototype.createLabel = function(title){ + var label = this.createTitle(title); + label.style.width = '100%'; + label.style.paddingTop = '10px'; + label.style.paddingBottom = '6px'; + return label; +} + +ConfigureFormatPanel.prototype.createCombobox = function(){ + var result =document.createElement('select'); + result.style.width = '210px'; + return result; +} + +ConfigureFormatPanel.prototype.addOption = function(combobox,value,text){ + var option = document.createElement('option'); + option.setAttribute('value', value); + mxUtils.write(option, text); + combobox.appendChild(option); +} + +ConfigureFormatPanel.prototype.createText = function(){ + var result =document.createElement('input'); + result.type ='text'; + result.style.width = '210px'; + return result; +} \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/decisionTree.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/decisionTree.js new file mode 100644 index 00000000..fbf5409c --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/decisionTree.js @@ -0,0 +1,102 @@ +//初始化之后 +EditorUi.prototype.afterInitialize =function(){ + resourceLoader.load(); +} + +/** + * 添加自定义组件模板 + */ +Sidebar.prototype.addEntityPalette = function(expand) +{ + var fns = [ + this.addEntry('[(#{mxgraph.re.editor.component.start.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.start.title})]', new mxGeometry(0, 0, 50, 50), 'shape=ellipse;whiteSpace=wrap;html=1;aspect=fixed;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'Start'); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.start.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.condition.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.condition.title})]', new mxGeometry(0, 0, 120, 60), 'shape=rhombus;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'Condition'); + this.graph.setAttributeForCell(cell, 'condition', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.condition.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.expression.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.expression.title})]', new mxGeometry(0, 0, 100, 30), 'shape=rectangle;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'Expression'); + this.graph.setAttributeForCell(cell, 'expression', ''); + this.graph.setAttributeForCell(cell, 'commands', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.expression.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.resourceAbstract.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.resourceAbstract.title})]', new mxGeometry(0, 0, 100, 30), 'shape=rectangle;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'ResourceAbstract'); + this.graph.setAttributeForCell(cell, 'code', ''); + this.graph.setAttributeForCell(cell, 'version', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.resourceAbstract.title})]'); + })) + ]; + + this.addPaletteFunctions('EntityPalette', '[(#{mxgraph.re.editor.Palette.title})]', (expand != null) ? expand : true, fns); +}; + +/** + * ============================================================================================================================== + * 自定义Format面板显示的标签列表 + * ============================================================================================================================== + */ +var formatGetDiagramFormaters =Format.prototype.getDiagramFormaters; +//根据当前选择的节点显示格式化面板 +Format.prototype.getDiagramFormaters =function(){ + var tabs =formatGetDiagramFormaters.apply(this, arguments); + var graph = this.editorUi.editor.graph; + var cells =graph.getSelectionCells(); + if(cells && cells.length>0){ + var type =cells[0].getAttribute('type'); + if(cells[0].isEdge() || type=='Condition' || type=='Expression' || type=='ResourceAbstract'){ + tabs.splice(0, 0, 'configure'); + } + } + return tabs; +} + +/** + * ============================================================================================================================== + * 自定义配置面板 + * ============================================================================================================================== + */ +ConfigureFormatPanel.prototype.addUi = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var selectedCells =graph.getSelectionCells(); + if(selectedCells && selectedCells.length>0){ + var selectedCell =selectedCells[0]; + var formPanel =this.createFormPanel(); + container.appendChild(formPanel); + + if(selectedCell.isEdge()){ + this.createConnectionUi(graph,formPanel,selectedCell); + }else if(mxUtils.isNode(selectedCell.value)){ + var type =selectedCell.getAttribute('type'); + if(type=='Condition'){ + this.createConditionNodeUi(graph,formPanel,selectedCell); + }else if(type=='Expression'){ + this.createExpressionNodeUi(graph,formPanel,selectedCell); + }else if(type=='ResourceAbstract'){ + this.createResourceAbstractNodeUi(graph,formPanel,selectedCell); + } + } + } + return container; +} diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/executionFlow.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/executionFlow.js new file mode 100644 index 00000000..e37d025b --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/executionFlow.js @@ -0,0 +1,137 @@ +//初始化之后 +EditorUi.prototype.afterInitialize =function(){ + resourceLoader.load(); + modelLoader.load(); +} + +/** + * 添加自定义组件模板 + */ +Sidebar.prototype.addEntityPalette = function(expand) +{ + var fns = [ + this.addEntry('[(#{mxgraph.re.editor.component.start.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.start.title})]', new mxGeometry(0, 0, 50, 50), 'shape=ellipse;whiteSpace=wrap;html=1;aspect=fixed;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'Start'); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.start.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.condition.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.condition.title})]', new mxGeometry(0, 0, 120, 60), 'shape=rhombus;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'Condition'); + this.graph.setAttributeForCell(cell, 'condition', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.condition.title})]'); + })), + /* 以下组件支持并发器(同时执行后续多个操作),目前由于对具体的实现还未确定,暂停界面支持。但后台已经能够处理,只是在执行时,如何分离各自的结果数据方面还未确定方案 + this.addEntry('[(#{mxgraph.re.editor.component.parallel.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.parallel.title})]', new mxGeometry(0, 0, 120, 20), 'shape=rectangle;rounded=1;whiteSpace=wrap;html=1;comic=1;fillColor=#99FF99;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'VParallel'); + this.graph.setAttributeForCell(cell, 'description', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.parallel.title})]'); + })), + */ + this.addEntry('[(#{mxgraph.re.editor.component.commandSet.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('

[(#{mxgraph.re.editor.component.commandSet.title})]

', new mxGeometry(0, 0, 100, 50), 'shape=rectangle;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'CommandSet'); + this.graph.setAttributeForCell(cell, 'commands', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.commandSet.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.resourceAbstract.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.resourceAbstract.title})]', new mxGeometry(0, 0, 100, 30), 'shape=rectangle;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'ResourceAbstract'); + this.graph.setAttributeForCell(cell, 'code', ''); + this.graph.setAttributeForCell(cell, 'version', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.resourceAbstract.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.configurableResourceAbstract.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('

[(#{mxgraph.re.editor.component.configurableResourceAbstract.entity.inputCommands})]


[(#{mxgraph.re.editor.component.configurableResourceAbstract.entity.resource})]


[(#{mxgraph.re.editor.component.configurableResourceAbstract.entity.outputCommands})]

', new mxGeometry(0, 0, 100, 80), 'shape=rectangle;verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'ConfigurableResourceAbstract'); + this.graph.setAttributeForCell(cell, 'code', ''); + this.graph.setAttributeForCell(cell, 'version', ''); + this.graph.setAttributeForCell(cell, 'inputCommands', ''); + this.graph.setAttributeForCell(cell, 'outputCommands', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.configurableResourceAbstract.title})]'); + })), + this.addEntry('[(#{mxgraph.re.editor.component.submodel.title})]', mxUtils.bind(this, function() + { + var cell = new mxCell('[(#{mxgraph.re.editor.component.submodel.title})]', new mxGeometry(0, 0, 100, 30), 'shape=rectangle;rounded=1;whiteSpace=wrap;html=1;'); + cell.vertex = true; + this.graph.setAttributeForCell(cell, 'type', 'SubModelAbstract'); + this.graph.setAttributeForCell(cell, 'code', ''); + return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, '[(#{mxgraph.re.editor.component.submodel.title})]'); + })) + ]; + + this.addPaletteFunctions('EntityPalette', '[(#{mxgraph.re.editor.Palette.title})]', (expand != null) ? expand : true, fns); +}; + +/** + * ============================================================================================================================== + * 自定义Format面板显示的标签列表 + * ============================================================================================================================== + */ +var formatGetDiagramFormaters =Format.prototype.getDiagramFormaters; +//根据当前选择的节点显示格式化面板 +Format.prototype.getDiagramFormaters =function(){ + var tabs =formatGetDiagramFormaters.apply(this, arguments); + var graph = this.editorUi.editor.graph; + var cells =graph.getSelectionCells(); + if(cells && cells.length>0){ + var type =cells[0].getAttribute('type'); + if(cells[0].isEdge() || type=='Condition' || type=='Parallel' || type=='CommandSet' || type=='ResourceAbstract' || type=='ConfigurableResourceAbstract' || type=='SubModelAbstract'){ + tabs.splice(0, 0, 'configure'); + } + } + return tabs; +} + +/** + * ============================================================================================================================== + * 自定义配置面板 + * ============================================================================================================================== + */ +ConfigureFormatPanel.prototype.addUi = function(container) +{ + var ui = this.editorUi; + var editor = ui.editor; + var graph = editor.graph; + var ss = this.format.getSelectionState(); + + var selectedCells =graph.getSelectionCells(); + if(selectedCells && selectedCells.length>0){ + var selectedCell =selectedCells[0]; + var formPanel =this.createFormPanel(); + container.appendChild(formPanel); + + if(selectedCell.isEdge()){ + this.createConnectionUi(graph,formPanel,selectedCell); + }else if(mxUtils.isNode(selectedCell.value)){ + var type =selectedCell.getAttribute('type'); + if(type=='Condition'){ + this.createConditionNodeUi(graph,formPanel,selectedCell); + }else if(type=='Parallel'){ + this.createParallelNodeUi(graph,formPanel,selectedCell); + }else if(type=='CommandSet'){ + this.createCommandSetNodeUi(graph,formPanel,selectedCell); + }else if(type=='ResourceAbstract'){ + this.createResourceAbstractNodeUi(graph,formPanel,selectedCell); + }else if(type=='ConfigurableResourceAbstract'){ + this.createConfigurableResourceAbstractNodeUi(graph,formPanel,selectedCell); + }else if(type=='SubModelAbstract'){ + this.createSubModelAbstractNodeUi(graph,formPanel,selectedCell); + } + } + } + return container; +} \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/init.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/init.js new file mode 100644 index 00000000..3890bce4 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/init.js @@ -0,0 +1,43 @@ +(function() +{ + mxResources.loadDefaultBundle = false; + var bundle = mxResources.getDefaultBundle(RESOURCE_BASE, mxLanguage) || mxResources.getSpecialBundle(RESOURCE_BASE, mxLanguage); + + //初始化显示图表的 xml 获取 url + window.mxInitXmlUrl = window.mxInitXmlUrl; + + var resourceUrls =[]; + + if(mxInitXmlUrl){ + resourceUrls =[bundle, STYLE_PATH + '/default.xml',mxInitXmlUrl]; + }else{ + resourceUrls =[bundle, STYLE_PATH + '/default.xml']; + } + mxUtils.getAll(resourceUrls, function(xhr) + { + //解析国际化资源 + mxResources.parse(xhr[0].getText()); + + //设置界面主题 + var themes = new Object(); + themes[Graph.prototype.defaultThemeName] = xhr[1].getDocumentElement(); + + //显示编辑器主窗口,并将初始化图表加载到编辑器中 + var editorUi =new EditorUi(new Editor(urlParams['chrome'] == '0', themes,null,null,true)); + + if(mxInitXmlUrl){ + var node = xhr[2].getDocumentElement(); + var dec = new mxCodec(node.ownerDocument); + dec.decode(node, editorUi.editor.graph.getModel()); + } + + //执行初始化操作 + if(editorUi.afterCreated){ + editorUi.afterCreated(); + } + + }, function() + { + document.body.innerHTML = '
Error loading resource files. Please check browser console.
'; + }); +})(); \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/mxgraph-common.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/mxgraph-common.js new file mode 100644 index 00000000..4e99e674 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/mxgraph-common.js @@ -0,0 +1,812 @@ +/** + * ============================================================================================================================== + * CodeMirror(编辑器) Hints(提示)相关类 + * ============================================================================================================================== + */ +CodeMirrorCodeHint =function(){ + this.parentParameterNames =PARENT_PARAMETER_NAMES; //由 html 页面提供 + this.parameterNames =PARAMETER_NAMES; //由 html 页面提供 + this.functionNames =[ + 'PI', + 'E', + 'join(split,s1,s2,s3...,sn)', + 'abs(value)', + 'acos(value)', + 'asin(value)', + 'atan(value)', + 'atan2(value)', + 'cbrt(value)', + 'ceil(value)', + 'cos(value)', + 'cosh(value)', + 'exp(value)', + 'expm1(value)', + 'floor(value)', + 'IEEEremainder(value1,value2)', + 'ln(value)', + 'max(v1,v2,...)', + 'min(v1,v2,...)', + 'pow(x,y)', + 'random()', + 'rint(value)', + 'round(value)', + 'sin(value)', + 'sinh(value)', + 'sqrt(value)', + 'sum(v1,v2,...)', + 'tan(value)', + 'tanh(value)', + 'transformSequencing(value,sourceMin,sourceMax,targetMin,targetMax)', + 'toDegrees(value)', + 'toRadians(value)', + 'normalDistributioin(x)', + 'inverseNormalDistributioin(x)' + ]; +}; + +CodeMirrorCodeHint.prototype.getParentParameterNames =function(){ + return this.parentParameterNames; +}, +CodeMirrorCodeHint.prototype.setParentParameterNames =function(names){ + this.parentParameterNames =names; +}, +CodeMirrorCodeHint.prototype.getParameterNames =function(){ + return this.parameterNames; +}, +CodeMirrorCodeHint.prototype.setParameterNames =function(names){ + this.parameterNames =names; +}, +CodeMirrorCodeHint.prototype.getHints =function(curWord){ + var result =[],index =0; + for(var i=0;i=0){ + result[index++] =this.parameterNames[i]; + } + }else{ + result[index++] =this.parameterNames[i]; + } + } + + if(curWord){ + + }else{ + result[index++] ='[(#{re.processor.component.code.hints.availableParentParameters})]'; + } + + for(var i=0;i=0){ + result[index++] =this.parentParameterNames[i]; + } + }else{ + result[index++] =this.parentParameterNames[i]; + } + } + + if(curWord){ + + }else{ + result[index++] ='[(#{re.processor.component.code.hints.availableFunctions})]'; + } + + for(var i=0;i=0 && indexOfCurWord0){ + for(var i=0;i0){ + for(var i=0;i0){ + for(var i=0;i0){ + for(var i=0;i'; + if(commands){ + label +='
' + + '
' + + commands + + '
'; + } + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('expression', expression); + elt.setAttribute('commands', commands); + elt.setAttribute('label', label); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + + //expression + expressionLabel =this.createLabel('[(#{mxgraph.re.editor.component.expression.entity.expression})]',formPanel); + expressionText =this.createTextArea(100,formPanel); + expressionText.value =selectedCell.getAttribute('expression') || ''; + this.createCodemirrorButton(expressionText,eventHandler,formPanel); + + //commands + commandsLabel =this.createLabel('[(#{mxgraph.re.editor.component.expression.entity.commands})]',formPanel); + commandsText =this.createTextArea(100,formPanel); + commandsText.value =selectedCell.getAttribute('commands') || ''; + this.createCodemirrorButton(commandsText,eventHandler,formPanel); + + mxEvent.addListener(expressionText, 'change',eventHandler); + mxEvent.addListener(commandsText, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(并发器) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createParallelNodeUi = function(graph,formPanel,selectedCell){ + var descriptionText; + var eventHandler =function(){ + if (!graph.isEditing()){ + var description =descriptionText.value; + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('label', description); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + descriptionText =this.createTextArea(200,formPanel); + descriptionText.value =selectedCell.getAttribute('label') || ''; + mxEvent.addListener(descriptionText, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(指令集) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createCommandSetNodeUi = function(graph,formPanel,selectedCell){ + var commandsLabel,commandsText; + var eventHandler =function(){ + if (!graph.isEditing()){ + var commands =commandsText.value; + var label = '

' + + commands + + '

'; + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('commands', commands); + elt.setAttribute('label', label); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + commandsLabel =this.createLabel('[(#{mxgraph.re.editor.component.commandSet.entity.commands})]',formPanel); + commandsText =this.createTextArea(200,formPanel); + commandsText.value =selectedCell.getAttribute('commands') || ''; + this.createCodemirrorButton(commandsText,eventHandler,formPanel); + mxEvent.addListener(commandsText, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(资源摘要) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createResourceAbstractNodeUi = function(graph,formPanel,selectedCell){ + var resourceLabel,resourceCombobox; + + var eventHandler =function(){ + if (!graph.isEditing()){ + var resource =resourceLoader.findById(resourceCombobox.value); + var version =''; + if(resource.version!=null){ + version ='V' + resource.version; + } + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('code', resource.code); + elt.setAttribute('version', (resource.version||'')); + elt.setAttribute('label', resource.name + ' ' + version); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + + resourceLabel =this.createLabel('[(#{mxgraph.re.editor.component.resourceAbstract.entity.resource})]',formPanel); + resourceCombobox =this.createCombobox(formPanel); + this.addOption(resourceCombobox,'',''); + for (var i = 0; i < resourceLoader.list.length; i++) + { + var resource =resourceLoader.list[i]; + var version =''; + if(resource.version!=null){ + version ='V' + resource.version; + } + this.addOption(resourceCombobox,resource.id,resource.name + ' ' + version); + } + + var resource =resourceLoader.findByCodeAndVersion(selectedCell.getAttribute('code'),selectedCell.getAttribute('version')); + if(resource){ + resourceCombobox.value =resource.id; + } + mxEvent.addListener(resourceCombobox, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(可配置输入和输出指令的资源摘要) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createConfigurableResourceAbstractNodeUi = function(graph,formPanel,selectedCell){ + var resourceLabel,resourceCombobox,inputCommandsLabel,inputCommandsText,outputCommandsLabel,outputCommandsText; + + var eventHandler =function(){ + if (!graph.isEditing()){ + var resource =resourceLoader.findById(resourceCombobox.value); + var version =''; + if(resource.version!=null){ + version ='V' + resource.version; + } + + var inputCommands =inputCommandsText.value; + var outputCommands =outputCommandsText.value; + var label ='

' + inputCommands + '


' + resource.name + ' ' + version + '


' + outputCommands + '

'; + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('code', resource.code); + elt.setAttribute('version', (resource.version||'')); + elt.setAttribute('inputCommands', inputCommands); + elt.setAttribute('outputCommands', outputCommands); + elt.setAttribute('label', label); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + + resourceLabel =this.createLabel('[(#{mxgraph.re.editor.component.resourceAbstract.entity.resource})]',formPanel); + resourceCombobox =this.createCombobox(formPanel); + this.addOption(resourceCombobox,'',''); + for (var i = 0; i < resourceLoader.list.length; i++) + { + var resource =resourceLoader.list[i]; + var version =''; + if(resource.version!=null){ + version ='V' + resource.version; + } + this.addOption(resourceCombobox,resource.id,resource.name + ' ' + version); + } + + var resource =resourceLoader.findByCodeAndVersion(selectedCell.getAttribute('code'),selectedCell.getAttribute('version')); + if(resource){ + resourceCombobox.value =resource.id; + } + mxEvent.addListener(resourceCombobox, 'change',eventHandler); + + inputCommandsLabel =this.createLabel('[(#{mxgraph.re.editor.component.configurableResourceAbstract.entity.inputCommands})]',formPanel); + inputCommandsText =this.createTextArea(170,formPanel); + inputCommandsText.value =selectedCell.getAttribute('inputCommands') || ''; + this.createCodemirrorButton(inputCommandsText,eventHandler,formPanel); + mxEvent.addListener(inputCommandsText, 'change',eventHandler); + + outputCommandsLabel =this.createLabel('[(#{mxgraph.re.editor.component.configurableResourceAbstract.entity.outputCommands})]',formPanel); + outputCommandsText =this.createTextArea(170,formPanel); + outputCommandsText.value =selectedCell.getAttribute('outputCommands') || ''; + this.createCodemirrorButton(outputCommandsText,eventHandler,formPanel); + mxEvent.addListener(outputCommandsText, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(模型摘要) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createSubModelAbstractNodeUi = function(graph,formPanel,selectedCell){ + var modelLabel,modelCombobox; + var eventHandler =function(){ + if (!graph.isEditing()){ + var model =modelLoader.findById(modelCombobox.value); + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + elt.setAttribute('code', model.code); + elt.setAttribute('label', model.name); + graph.getModel().setValue(selectedCell, elt); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + + modelLabel =this.createLabel('[(#{mxgraph.re.editor.component.submodel.entity.model})]',formPanel); + modelCombobox =this.createCombobox(formPanel); + this.addOption(modelCombobox,'',''); + for (var i = 0; i < modelLoader.list.length; i++) + { + var model =modelLoader.list[i]; + this.addOption(modelCombobox,model.id,model.name); + } + + var model =modelLoader.findByCode(selectedCell.getAttribute('code')); + if(model){ + modelCombobox.value =model.id; + } + mxEvent.addListener(modelCombobox, 'change',eventHandler); +} + +/** + * ------------------------------------------------------------------------------------------------------------------------------ + * 自定义配置面板(连线) + * ------------------------------------------------------------------------------------------------------------------------------ + */ +ConfigureFormatPanel.prototype.createConnectionUi = function(graph,formPanel,selectedCell){ + var valueTypeLabel,valueTypeCombobox,valueLabel,valueText,commandsLabel,commandsText; + var eventHandler =function(){ + if (!graph.isEditing()){ + graph.getModel().beginUpdate(); + try + { + var elt = selectedCell.value.cloneNode(true); + var selIndex = valueTypeCombobox.selectedIndex; + elt.setAttribute('label', valueText.value); + elt.setAttribute('value', valueText.value); + elt.setAttribute('valueType', valueTypeCombobox.value); + elt.setAttribute('commands', commandsText.value); + graph.getModel().setValue(selectedCell, elt); + console.log(graph); + } + finally + { + graph.getModel().endUpdate(); + } + } + }; + + valueTypeLabel =this.createLabel('[(#{mxgraph.re.editor.component.condition.edge.type})]',formPanel); + valueTypeCombobox =this.createCombobox(formPanel); + this.addOption(valueTypeCombobox,'',''); + this.addOption(valueTypeCombobox,'java.lang.String','[(#{mxgraph.re.editor.component.condition.edge.type.string})]'); + this.addOption(valueTypeCombobox,'java.math.BigDecimal','[(#{mxgraph.re.editor.component.condition.edge.type.number})]'); + this.addOption(valueTypeCombobox,'java.lang.Boolean','[(#{mxgraph.re.editor.component.condition.edge.type.boolean})]'); + valueTypeCombobox.value=selectedCell.getValue().getAttribute("valueType") || ''; + mxEvent.addListener(valueTypeCombobox, 'change',eventHandler); + + valueLabel =this.createLabel('[(#{mxgraph.re.editor.component.condition.edge.conditionValue})]',formPanel); + valueText =this.createText(formPanel); + valueText.value =selectedCell.getValue().getAttribute("value") || ''; + mxEvent.addListener(valueText, 'change',eventHandler); + + commandsLabel =this.createLabel('[(#{mxgraph.re.editor.component.commandSet.entity.commands})]',formPanel); + commandsText =this.createTextArea(200,formPanel); + commandsText.value =selectedCell.getAttribute('commands') || ''; + this.createCodemirrorButton(commandsText,eventHandler,formPanel); + mxEvent.addListener(commandsText, 'change',eventHandler); +} + +/** + * 创建表单面板 + */ +ConfigureFormatPanel.prototype.createFormPanel = function(){ + var formPanel =this.createPanel(); + formPanel.style.borderWidth = '0px'; + if (mxClient.IS_QUIRKS){ + formPanel.style.display = 'block'; + } + return formPanel; +} + +/** + * 创建表单 + */ +ConfigureFormatPanel.prototype.createForm =function(parent){ + var form =document.createElement('form'); + if(parent){ + parent.appendChild(form); + } + return form; +} + +/** + * 创建 DIV + */ +ConfigureFormatPanel.prototype.createDiv = function(){ + var div =document.createElement('div'); + div.style.width = '100%'; + return div; +} + +/** + * 创建 Label + */ +ConfigureFormatPanel.prototype.createLabel = function(title,parent){ + var label = this.createTitle(title); + label.style.width = '100%'; + if(parent){ + parent.appendChild(label); + } + return label; +} + +/** + * 创建 Combobox + */ +ConfigureFormatPanel.prototype.createCombobox = function(parent){ + var combobox =document.createElement('select'); + combobox.style.width = '210px'; + if(parent){ + parent.appendChild(combobox); + } + return combobox; +} + +/** + * 给 Combobox 添加选项 + */ +ConfigureFormatPanel.prototype.addOption = function(combobox,value,text){ + var option = document.createElement('option'); + option.setAttribute('value', value); + mxUtils.write(option, text); + combobox.appendChild(option); +} + +/** + * 创建 Text + */ +ConfigureFormatPanel.prototype.createText = function(parent){ + var text =document.createElement('input'); + text.type ='text'; + text.style.width = '210px'; + if(parent){ + parent.appendChild(text); + } + return text; +} + +/** + * 创建 TextArea + */ +ConfigureFormatPanel.prototype.createTextArea = function(height,parent){ + var textarea =document.createElement('textarea'); + textarea.setAttribute('wrap', 'off'); + textarea.setAttribute('spellcheck', 'false'); + textarea.setAttribute('autocorrect', 'off'); + textarea.setAttribute('autocomplete', 'off'); + textarea.setAttribute('autocapitalize', 'off'); + textarea.style.overflow = 'auto'; + textarea.style.resize = 'none'; + textarea.style.width = '210px'; + if(height){ + textarea.style.height =height + 'px'; + } + textarea.style.borderColor = '#CCCCCC'; + //textarea.readOnly =true; + if(parent){ + parent.appendChild(textarea); + } + return textarea; +} + +/** + * 创建 Codemirror + */ +ConfigureFormatPanel.prototype.createCodemirrorButton =function(targetTextareaWidget,okCallback,parent){ + var div =this.createDiv(); + div.style ='text-align:right;margin-right:6px;'; + + var commandsBtn = mxUtils.button('[(#{edit})]', mxUtils.bind(this, function(evt) + { + this.editorUi.actions.get('codemirror').funct(targetTextareaWidget,okCallback); + })); + commandsBtn.style.width = '80px'; + div.appendChild(commandsBtn); + + if(parent){ + parent.appendChild(div); + } + return commandsBtn; +} \ No newline at end of file diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/urlParameter.js b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/urlParameter.js new file mode 100644 index 00000000..c73fb9e4 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/editor/urlParameter.js @@ -0,0 +1,35 @@ +// Parses URL parameters. Supported parameters are: +// - lang=xy: Specifies the language of the user interface. +// - touch=1: Enables a touch-style user interface. +// - storage=local: Enables HTML5 local storage. +// - chrome=0: Chromeless mode. +var urlParams = (function(url) +{ + var result = new Object(); + var idx = url.lastIndexOf('?'); + + if (idx > 0) + { + var params = url.substring(idx + 1).split('&'); + + for (var i = 0; i < params.length; i++) + { + idx = params[i].indexOf('='); + + if (idx > 0) + { + result[params[i].substring(0, idx)] = params[i].substring(idx + 1); + } + } + } + return result; +})(window.location.href); + +// 设置基本变量 +window.mxBasePath = '[(@{/webjars/mxgraph/3.9.12/})]'; +window.STYLE_PATH = '[(@{/tools/graph-editor/styles})]'; +window.RESOURCES_PATH ='[(@{/tools/graph-editor})]'; +window.RESOURCE_BASE = window.RESOURCES_PATH + '/resources/grapheditor'; + +mxLoadResources = false; +mxLanguage = '[(${#locale.language})]'; diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/decisionTree.html b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/decisionTree.html new file mode 100644 index 00000000..9210ddcf --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/decisionTree.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/executionFlow.html b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/executionFlow.html new file mode 100644 index 00000000..bfbed1c3 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/templates/io/sc/engine/rule/server/view/executionFlow.html @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/io.sc.engine.rule.server/src/main/resources/workflow/io/sc/engine/rule/Sample.bpmn b/io.sc.engine.rule.server/src/main/resources/workflow/io/sc/engine/rule/Sample.bpmn new file mode 100644 index 00000000..38cd4733 --- /dev/null +++ b/io.sc.engine.rule.server/src/main/resources/workflow/io/sc/engine/rule/Sample.bpmn @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/io.sc.engine.st.frontend/package.json b/io.sc.engine.st.frontend/package.json index 9853f3c8..40f8cb3c 100644 --- a/io.sc.engine.st.frontend/package.json +++ b/io.sc.engine.st.frontend/package.json @@ -92,7 +92,7 @@ "luckyexcel": "1.0.1", "mockjs": "1.1.0", "pinia": "2.1.7", - "platform-core": "8.1.231", + "platform-core": "8.1.245", "quasar": "2.15.3", "tailwindcss": "3.4.3", "vue": "3.4.24", diff --git a/io.sc.platform.app/build.gradle b/io.sc.platform.app/build.gradle index 285406a5..ff8838f6 100644 --- a/io.sc.platform.app/build.gradle +++ b/io.sc.platform.app/build.gradle @@ -1,5 +1,6 @@ dependencies { api( + project(":io.sc.platform.attachment"), project(":io.sc.platform.communication"), project(":io.sc.platform.csv"), project(":io.sc.platform.flowable"), diff --git a/io.sc.platform.attachment.api/src/main/java/io/sc/platform/attachment/api/DatabaseAttachmentVo.java b/io.sc.platform.attachment.api/src/main/java/io/sc/platform/attachment/api/DatabaseAttachmentVo.java index b1669a05..421978a8 100644 --- a/io.sc.platform.attachment.api/src/main/java/io/sc/platform/attachment/api/DatabaseAttachmentVo.java +++ b/io.sc.platform.attachment.api/src/main/java/io/sc/platform/attachment/api/DatabaseAttachmentVo.java @@ -1,16 +1,5 @@ package io.sc.platform.attachment.api; -import io.sc.platform.attachment.api.enums.PersistenceType; -import io.sc.platform.orm.api.vo.AuditorVo; - public class DatabaseAttachmentVo extends AttachmentVo { - private byte[] bytes; - - public byte[] getBytes() { - return bytes; - } - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } } diff --git a/io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/controller/AttachmentWebController.java b/io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/controller/AttachmentWebController.java index eb6d55fc..d4f3e7ac 100644 --- a/io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/controller/AttachmentWebController.java +++ b/io.sc.platform.attachment/src/main/java/io/sc/platform/attachment/controller/AttachmentWebController.java @@ -35,26 +35,12 @@ public class AttachmentWebController extends RestCrudController implements AttachmentService { + @Autowired private JdbcTemplate jdbcTemplate; @Autowired private SystemParameterService systemParameterService; - + @Override public List findByBussinessKey(String bussinessKey) { return EntityVoUtil.toVo(repository.findByBussinessKeyOrderByName(bussinessKey)); } - + @Override public Map> findByBussinessKeys(Set bussinessKeys) { @@ -78,14 +89,11 @@ public class AttachmentServiceImpl extends DaoServiceImpl - - - - - + + + + + - - + + @@ -36,5 +36,8 @@ + + + diff --git a/io.sc.platform.attachment/src/main/resources/liquibase/io/sc/platform/attachment/mysql/mysql.sql b/io.sc.platform.attachment/src/main/resources/liquibase/io/sc/platform/attachment/mysql/mysql.sql new file mode 100644 index 00000000..6c29003d --- /dev/null +++ b/io.sc.platform.attachment/src/main/resources/liquibase/io/sc/platform/attachment/mysql/mysql.sql @@ -0,0 +1 @@ +ALTER TABLE SYS_ATTACHMENT MODIFY COLUMN ATTACHMENT_ LONGBLOB NULL COMMENT '附件二进制内容'; \ No newline at end of file diff --git a/io.sc.platform.core.frontend/package.json b/io.sc.platform.core.frontend/package.json index 080d54c6..830c41bc 100644 --- a/io.sc.platform.core.frontend/package.json +++ b/io.sc.platform.core.frontend/package.json @@ -1,6 +1,6 @@ { "name": "platform-core", - "version": "8.1.244", + "version": "8.1.245", "description": "前端核心包,用于快速构建前端的脚手架", "//main": "库的主文件", "main": "dist/platform-core.js", diff --git a/io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue b/io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue index d8c931fa..4ce4af57 100644 --- a/io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue +++ b/io.sc.platform.core.frontend/src/platform/components/form/elements/WCodeMirror.vue @@ -20,7 +20,13 @@ >