├── .gitignore ├── config ├── prod.env.js ├── test.env.js ├── dev.env.js └── index.js ├── .gitattributes ├── screenshots ├── 项目截图1.png └── 项目截图2.png ├── src ├── components │ ├── D3Visualization │ │ ├── index.js │ │ ├── constants.js │ │ ├── lib │ │ │ └── visualization │ │ │ │ ├── utils │ │ │ │ ├── arrays.coffee │ │ │ │ ├── circularLayout.coffee │ │ │ │ ├── angleList.coffee │ │ │ │ ├── clickHandler.coffee │ │ │ │ ├── textMeasurement.coffee │ │ │ │ ├── adjacentAngles.coffee │ │ │ │ ├── straightArrow.coffee │ │ │ │ ├── loopArrow.coffee │ │ │ │ ├── circumferentialDistribution.coffee │ │ │ │ ├── circumferentialRelationshipRouting.coffee │ │ │ │ ├── pairwiseArcsRelationshipRouting.coffee │ │ │ │ └── arcArrow.coffee │ │ │ │ ├── components │ │ │ │ ├── renderer.coffee │ │ │ │ ├── relationship.coffee │ │ │ │ ├── node.coffee │ │ │ │ ├── collision.coffee │ │ │ │ ├── graphView.coffee │ │ │ │ ├── layout.coffee │ │ │ │ ├── graphGeometry.coffee │ │ │ │ └── graph.coffee │ │ │ │ ├── neod3.coffee │ │ │ │ ├── index.js │ │ │ │ └── renders │ │ │ │ ├── menu.coffee │ │ │ │ └── init.coffee │ │ ├── services │ │ │ ├── bolt │ │ │ │ ├── updateStatisticsFields.js │ │ │ │ ├── boltHelpers.js │ │ │ │ └── boltMappings.js │ │ │ ├── duckUtils.js │ │ │ ├── exceptionMessages.js │ │ │ ├── exceptions.js │ │ │ └── utils.js │ │ ├── components │ │ │ ├── RowExpandToggle.vue │ │ │ ├── Legend.vue │ │ │ ├── Graph.vue │ │ │ └── Inspector.vue │ │ ├── mapper.js │ │ ├── GraphEventHandler.js │ │ └── Visualization.vue │ └── Search.vue ├── index.js ├── App.vue ├── router │ └── index.js ├── app.js ├── utils │ └── index.js └── views │ ├── neo4j.vue │ └── page │ ├── AuthorKeywordSearch.vue │ ├── CsvUpload.vue │ ├── AuthorCoopratorSearch.vue │ ├── AuthorArticleSearch.vue │ ├── CoopratorArticleSearch.vue │ ├── ArticleAuthorSearch.vue │ └── AuthorSimilarSearch.vue ├── .babelrc ├── neo4j-vue.iml ├── oneapm.js ├── index.html ├── LICENSE ├── README.md ├── package.json ├── npm-debug.log └── flex.md /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | /.settings 3 | /.classpath 4 | /.project 5 | /node_modules -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | NODE_ENV: '"production"' 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto -------------------------------------------------------------------------------- /screenshots/项目截图1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendaye/vue-neo4j/HEAD/screenshots/项目截图1.png -------------------------------------------------------------------------------- /screenshots/项目截图2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chendaye/vue-neo4j/HEAD/screenshots/项目截图2.png -------------------------------------------------------------------------------- /src/components/D3Visualization/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chendaye666 on 17/7/31. 3 | */ 4 | 5 | export Visualization from './Visualization' -------------------------------------------------------------------------------- /config/test.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var devEnv = require('./dev.env') 3 | 4 | module.exports = merge(devEnv, { 5 | NODE_ENV: '"testing"' 6 | }) 7 | -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | var merge = require('webpack-merge') 2 | var prodEnv = require('./prod.env') 3 | 4 | module.exports = merge(prodEnv, { 5 | NODE_ENV: '"development"' 6 | }) 7 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chendaye666 on 17/8/5. 3 | */ 4 | // require('../oneapm'); 5 | 6 | import { 7 | app 8 | } from './app' 9 | 10 | app.$mount('#app') -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }], 4 | "stage-2" 5 | ], 6 | "plugins": [ 7 | "transform-export-extensions", 8 | "transform-vue-jsx" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 9 | 20 | -------------------------------------------------------------------------------- /src/components/D3Visualization/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chendaye666 on 17/7/31. 3 | */ 4 | 5 | export const font = { 6 | 7 | } 8 | 9 | export const dim = { 10 | // Editor bar 11 | editorbarHeight: 70, 12 | // Frame 13 | frameBodyHeight: 550, 14 | frameTitlebarHeight: 39, 15 | frameStatusbarHeight: 39, 16 | frameBodyPadding: 20 17 | } -------------------------------------------------------------------------------- /neo4j-vue.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chendaye666 on 17/8/5. 3 | */ 4 | import Vue from 'vue' 5 | import VueRouter from 'vue-router' 6 | Vue.use(VueRouter) 7 | 8 | export default new VueRouter({ 9 | mode: 'history', 10 | linkActiveClass: 'is-active', 11 | scrollBehavior: () => ({ 12 | y: 0 13 | }), 14 | routes: [{ 15 | path: '/', 16 | redirect: '/neo4j' 17 | }, { 18 | name: 'main', 19 | path: '/main', 20 | component: require('../views/Main') 21 | }, 22 | { 23 | name: 'neo4j', 24 | path: '/neo4j', 25 | component: require('../views/neo4j') 26 | } 27 | ] 28 | }) -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chendaye666 on 17/8/5. 3 | */ 4 | import Vue from 'vue' 5 | import ElementUI from 'element-ui'; 6 | import 'element-ui/lib/theme-chalk/index.css'; 7 | import VueCookies from 'vue-cookies' 8 | 9 | 10 | import App from './App.vue' 11 | Vue.use(ElementUI); 12 | Vue.use(VueCookies) 13 | 14 | 15 | import router from './router' 16 | 17 | import { 18 | sync 19 | } from 'vuex-router-sync' 20 | window.jQuery = window.$ = require('jquery/dist/jquery') 21 | const app = new Vue({ 22 | router, 23 | ...App 24 | }) 25 | 26 | 27 | export { 28 | app, 29 | router 30 | } -------------------------------------------------------------------------------- /oneapm.js: -------------------------------------------------------------------------------- 1 | /** 2 | * OneAPM agent configuration. 3 | * 4 | * See lib/config.defaults.js in the agent distribution for a more complete 5 | * description of configuration variables and their potential values. 6 | */ 7 | exports.config = { 8 | /** 9 | * Your Application name. 10 | */ 11 | app_name: 'neo4j community', 12 | /** 13 | * Your tier names. 14 | */ 15 | tier_name: ['nodejs'], 16 | /** 17 | * Your OneAPM license key. 18 | */ 19 | license_key: 'DAAKVgMMBVF489eUFV1NWQ8eX0691aFUVUkECQNQTb938wkAFQdWHgMF5c21CghIBgocUVM=', 20 | logging: { 21 | /** 22 | * Level at which to log. 'trace' is most useful to OneAPM when diagnosing 23 | * issues with the agent, 'info' and higher will impose the least overhead on 24 | * production applications. 25 | */ 26 | level: 'info' 27 | }, 28 | transaction_events: { 29 | enabled: true 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Neo4j 7 | 8 | 19 | 20 | 21 | 22 | 23 |
24 | 27 | 28 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/arrays.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.utils.cloneArray = (original) -> 24 | clone = new Array(original.length) 25 | clone[idx] = node for node, idx in original 26 | clone -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/renderer.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.Renderer 24 | constructor: (opts = {})-> 25 | neo.utils.extend(@, opts) 26 | @onGraphChange ?= -> 27 | @onTick ?= -> -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 郑辉强 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | export const copyArray = (array, start, end) => { 2 | let length = array.length; 3 | if (!length) return []; 4 | end = end > length ? length : end; 5 | let data = []; 6 | for (let i = start; i < end; i++) { 7 | data.push(array[i]); 8 | } 9 | return data; 10 | } 11 | export const implode = (arr, tag) => { 12 | var str = ""; 13 | for (var i = 0; i < arr.length; i++) { 14 | str += arr[i]; 15 | if (i < arr.length - 1) { 16 | str += tag; 17 | } 18 | } 19 | return str; 20 | } 21 | export const sortArray = (arr, key, order) => { 22 | if (order == "asc") { 23 | arr.sort((a, b) => a[key] - b[key]); 24 | } else if (order == "desc") { 25 | arr.sort((a, b) => b[key] - a[key]); 26 | } 27 | return arr; 28 | } 29 | 30 | export const parseWords = (data) => { 31 | let word = data.split("@"); 32 | let arr = []; 33 | for (let i = 0; i < word.length; i++) { 34 | let keyword = word[i].split(":"); 35 | if (keyword.length == 3) { 36 | arr.push({ 37 | id: parseInt(keyword[0]), 38 | word: keyword[1], 39 | frequency: parseInt(keyword[2]) 40 | }); 41 | } 42 | } 43 | return arr; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/bolt/updateStatisticsFields.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { plural: 'constraints', singular: 'constraint', verb: 'added', field: 'constraintsAdded' }, 3 | { plural: 'constraints', singular: 'constraint', verb: 'removed', field: 'constraintsRemoved' }, 4 | { plural: 'indexes', singular: 'index', verb: 'added', field: 'indexesAdded' }, 5 | { plural: 'indexes', singular: 'index', verb: 'removed', field: 'indexesRemoved' }, 6 | { plural: 'labels', singular: 'label', verb: 'added', field: 'labelsAdded' }, 7 | { plural: 'labels', singular: 'label', verb: 'removed', field: 'labelsRemoved' }, 8 | { plural: 'nodes', singular: 'node', verb: 'created', field: 'nodesCreated' }, 9 | { plural: 'nodes', singular: 'node', verb: 'deleted', field: 'nodesDeleted' }, 10 | { plural: 'properties', singular: 'property', verb: 'set', field: 'propertiesSet' }, 11 | { plural: 'relationships', singular: 'relationship', verb: 'deleted', field: 'relationshipDeleted' }, 12 | { plural: 'relationships', singular: 'relationship', verb: 'deleted', field: 'relationshipsDeleted' }, 13 | { plural: 'relationships', singular: 'relationship', verb: 'created', field: 'relationshipsCreated' } 14 | ] 15 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/duckUtils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | export const hydrate = (initialState, state) => { 22 | if (!state) return state 23 | return (state.hydrated) ? state : { ...initialState, ...state, hydrated: true } 24 | } 25 | 26 | export const dehydrate = (state) => { 27 | if (state) { 28 | delete state.hydrated 29 | } 30 | return state 31 | } 32 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/circularLayout.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.utils.circularLayout = (nodes, center, radius) -> 24 | unlocatedNodes = [] 25 | unlocatedNodes.push(node) for node in nodes when !(node.x? and node.y?) 26 | for n, i in unlocatedNodes 27 | n.x = center.x + radius * Math.sin(2 * Math.PI * i / unlocatedNodes.length) 28 | n.y = center.y + radius * Math.cos(2 * Math.PI * i / unlocatedNodes.length) 29 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/relationship.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.models.Relationship 24 | constructor: (@id, @source, @target, @type, properties) -> 25 | @propertyMap = properties 26 | @propertyList = for own key,value of @propertyMap 27 | { key: key, value: value } 28 | 29 | toJSON: -> 30 | @propertyMap 31 | 32 | isNode: false 33 | isRelationship: true 34 | isLoop: -> 35 | @source is @target -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | // see http://vuejs-templates.github.io/webpack for documentation. 2 | var path = require('path') 3 | 4 | module.exports = { 5 | build: { 6 | env: require('./prod.env'), 7 | index: path.resolve(__dirname, '../dist/index.html'), 8 | assetsRoot: path.resolve(__dirname, '../dist'), 9 | assetsSubDirectory: 'static', 10 | assetsPublicPath: '/', 11 | productionSourceMap: false, 12 | // Gzip off by default as many popular static hosts such as 13 | // Surge or Netlify already gzip all static assets for you. 14 | // Before setting to `true`, make sure to: 15 | // npm install --save-dev compression-webpack-plugin 16 | productionGzip: false, 17 | productionGzipExtensions: ['js', 'css'] 18 | }, 19 | dev: { 20 | env: require('./dev.env'), 21 | port: 8080, 22 | assetsSubDirectory: 'static', 23 | assetsPublicPath: '/', 24 | proxyTable: {}, 25 | // CSS Sourcemaps off by default because relative paths are "buggy" 26 | // with this option, according to the CSS-Loader README 27 | // (https://github.com/webpack/css-loader#sourcemaps) 28 | // In our experience, they generally work as expected, 29 | // just be aware of this issue when enabling this option. 30 | cssSourceMap: false 31 | }, 32 | setting: { 33 | neo4jUrl: 'neo4j://www.lengo.top:7687', 34 | neo4jUserName: 'neo4j', 35 | neo4jPassword: 'lengo' 36 | } 37 | } -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/node.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.models.Node 24 | constructor: (@id, @labels, properties) -> 25 | @propertyMap = properties 26 | @propertyList = for own key,value of properties 27 | { key: key, value: value } 28 | 29 | toJSON: -> 30 | @propertyMap 31 | 32 | isNode: true 33 | isRelationship: false 34 | 35 | relationshipCount: (graph) -> 36 | node = @ 37 | rels = [] 38 | rels.push[relationship] for relationship in graph.relationships() when relationship.source is node or relationship.target is node 39 | rels.length 40 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/neod3.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | window.neo = window.neo || {} 24 | 25 | neo.models = {} 26 | 27 | neo.renderers = 28 | menu: [] 29 | node: [] 30 | relationship: [] 31 | 32 | neo.utils = 33 | # Note: quick n' dirty. Only works for serializable objects 34 | copy: (src) -> 35 | JSON.parse(JSON.stringify(src)) 36 | 37 | extend: (dest, src) -> 38 | return if not neo.utils.isObject(dest) and neo.utils.isObject(src) 39 | dest[k] = v for own k, v of src 40 | return dest 41 | 42 | isArray: Array.isArray or (obj) -> 43 | Object::toString.call(obj) == '[object Array]'; 44 | 45 | isObject: (obj) -> Object(obj) is obj 46 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/angleList.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.angleList 24 | 25 | constructor: (@list) -> 26 | 27 | getAngle: (index) -> 28 | @list[index].angle 29 | 30 | fixed: (index) -> 31 | @list[index].fixed 32 | 33 | totalLength: -> 34 | @list.length 35 | 36 | length: (run) -> 37 | if run.start < run.end 38 | run.end - run.start 39 | else 40 | run.end + @list.length - run.start 41 | 42 | angle: (run) -> 43 | if run.start < run.end 44 | @list[run.end].angle - @list[run.start].angle 45 | else 46 | 360 - (@list[run.start].angle - @list[run.end].angle) 47 | 48 | wrapIndex: (index) -> 49 | if index == -1 50 | @list.length - 1 51 | else if index >= @list.length 52 | index - @list.length 53 | else 54 | index -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/collision.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.collision = do -> 24 | collision = {} 25 | 26 | collide = (node) -> 27 | r = node.radius + 10 28 | nx1 = node.x - r 29 | nx2 = node.x + r 30 | ny1 = node.y - r 31 | ny2 = node.y + r 32 | return (quad, x1, y1, x2, y2) -> 33 | if (quad.point && (quad.point != node)) 34 | x = node.x - quad.point.x 35 | y = node.y - quad.point.y 36 | l = Math.sqrt(x * x + y * y) 37 | r = node.radius + 10 + quad.point.radius 38 | if (l < r) 39 | l = (l - r) / l * .5 40 | node.x -= x *= l 41 | node.y -= y *= l 42 | quad.point.x += x 43 | quad.point.y += y 44 | x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1 45 | 46 | collision.avoidOverlap = (nodes) -> 47 | q = d3.geom.quadtree(nodes) 48 | for n in nodes 49 | q.visit collide(n) 50 | 51 | collision -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/clickHandler.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.utils.clickHandler = -> 24 | cc = (selection) -> 25 | 26 | # euclidean distance 27 | dist = (a, b) -> 28 | Math.sqrt Math.pow(a[0] - b[0], 2), Math.pow(a[1] - b[1], 2) 29 | down = undefined 30 | tolerance = 5 31 | last = undefined 32 | wait = null 33 | selection.on "mousedown", -> 34 | d3.event.target.__data__.fixed = yes 35 | down = d3.mouse(document.body) 36 | last = +new Date() 37 | d3.event.stopPropagation() 38 | 39 | selection.on "mouseup", -> 40 | if dist(down, d3.mouse(document.body)) > tolerance 41 | return 42 | else 43 | if wait 44 | window.clearTimeout wait 45 | wait = null 46 | event.dblclick d3.event.target.__data__ 47 | else 48 | event.click d3.event.target.__data__ 49 | wait = window.setTimeout(((e) -> 50 | -> 51 | wait = null 52 | )(d3.event), 250) 53 | 54 | event = d3.dispatch("click", "dblclick") 55 | d3.rebind cc, event, "on" 56 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/textMeasurement.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.utils.measureText = do -> 24 | measureUsingCanvas = (text, font) -> 25 | canvasSelection = d3.select('canvas#textMeasurementCanvas').data([this]) 26 | canvasSelection.enter().append('canvas') 27 | .attr('id', 'textMeasurementCanvas') 28 | .style('display', 'none') 29 | 30 | canvas = canvasSelection.node() 31 | context = canvas.getContext('2d') 32 | context.font = font 33 | context.measureText(text).width 34 | 35 | cache = do () -> 36 | cacheSize = 10000 37 | map = {} 38 | list = [] 39 | (key, calc) -> 40 | cached = map[key] 41 | if cached 42 | cached 43 | else 44 | result = calc() 45 | if (list.length > cacheSize) 46 | delete map[list.splice(0, 1)] 47 | list.push(key) 48 | map[key] = result 49 | 50 | return (text, fontFamily, fontSize) -> 51 | font = 'normal normal normal ' + fontSize + 'px/normal ' + fontFamily; 52 | cache(text + font, () -> 53 | measureUsingCanvas(text, font) 54 | ) 55 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/graphView.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.graphView 24 | constructor: (element, measureSize, @graph, @style) -> 25 | layout = neo.layout.force() 26 | @viz = neo.viz(element, measureSize, @graph, layout, @style) 27 | @callbacks = {} 28 | callbacks = @callbacks 29 | @viz.trigger = do -> 30 | (event, args...) -> 31 | callback.apply(null, args) for callback in (callbacks[event] or []) 32 | 33 | on: (event, callback) -> 34 | (@callbacks[event] ?= []).push(callback) 35 | @ 36 | 37 | layout: (value) -> 38 | return layout unless arguments.length 39 | layout = value 40 | @ 41 | 42 | grass: (value) -> 43 | return @style.toSheet() unless arguments.length 44 | @style.importGrass(value) 45 | @ 46 | 47 | update: -> 48 | @viz.update() 49 | @ 50 | 51 | resize: -> 52 | @viz.resize() 53 | @ 54 | 55 | boundingBox: -> 56 | @viz.boundingBox() 57 | 58 | collectStats: -> 59 | @viz.collectStats() 60 | 61 | zoomIn: (elem) -> 62 | @viz.zoomInClick(elem) 63 | 64 | zoomOut: (elem) -> 65 | @viz.zoomOutClick(elem) 66 | -------------------------------------------------------------------------------- /src/components/D3Visualization/components/RowExpandToggle.vue: -------------------------------------------------------------------------------- 1 | 72 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/exceptionMessages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | export const AddServerValidationError = 'Wrong format. It should be ":server add name username:password@host:port"' 22 | export const CreateDataSourceValidationError = 'Wrong format. It should be ":datasource create {"name": "myName", "command": "RETURN rand()", "bookmarkId":"uuid-of-existing-bookmark", "refreshInterval": 10, "parameters": {}}"' 23 | export const RemoveDataSourceValidationError = 'Wrong format. It should be ":datasource remove uuid-of-existing-datasource"' 24 | export const BoltConnectionError = 'No connection found, did you connect to Neo4j?' 25 | export const BoltError = '#code# - #message#' 26 | export const Neo4jError = '#message#' 27 | export const UnknownCommandError = 'Unknown command #cmd#' 28 | export const BookmarkNotFoundError = 'No connection with the name #name# found. Add a bookmark before trying to connect.' 29 | export const OpenConnectionNotFoundError = 'No open connection with the name #name# found. You have to connect to a bookmark before you can use it.' 30 | export const CouldNotFetchRemoteGuideError = 'Can not fetch remote guide: #error#' 31 | export const FetchURLError = 'Could not fetch URL: "#error#". This could be due to the remote server policy. See your web browsers error console for more information.' 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Vue 2 | 3 | ## 安装步骤 4 | 1. 克隆或下载该库到本地 5 | 2. 下载neo4j并安装到本地, 启动neo4j服务 6 | 3. 进入到该工程的根目录, 输入命令: npm run update 进行npm包的导入 7 | 4. 使用命令: npm run dev 启动该工程 8 | 5. 默认端口是:8081 9 | - neo4j账号信息: 10 | - url: bolt://localhost 11 | - userName: neo4j 12 | - passsword: password 13 | 14 | ### 启动服务 15 | `npm run dev` and point your web browser to `http://localhost:8081`. 16 | 17 | ### 项目截图 18 | ![image](https://github.com/zhq734/neo4j-vue/blob/master/screenshots/项目截图1.png) 19 | ![image](https://github.com/zhq734/neo4j-vue/blob/master/screenshots/项目截图2.png) 20 | 21 | ### 目录结构 22 | ├── index.html 入口页面
23 | ├── build 构建脚本目录
24 | │ ├── build-server.js 运行本地构建服务器,可以访问构建后的页面
25 | │ ├── build.js 生产环境构建脚本
26 | │ ├── dev-client.js 开发服务器热重载脚本,主要用来实现开发阶段的页面自动刷新
27 | │ ├── dev-server.js 运行本地开发服务器
28 | │ ├── utils.js 构建相关工具方法
29 | │ ├── webpack.base.conf.js wabpack基础配置
30 | │ ├── webpack.dev.conf.js wabpack开发环境配置
31 | │ └── webpack.prod.conf.js wabpack生产环境配置
32 | ├── config 项目配置
33 | │ ├── dev.env.js 开发环境变量
34 | │ ├── index.js 项目配置文件
35 | │ ├── prod.env.js 生产环境变量
36 | │ └── test.env.js 测试环境变量
37 | ├── package.json npm包配置文件,里面定义了项目的npm脚本,依赖包等信息
38 | ├── src 项目源码目录
39 | │ ├── index.js 入口js文件
40 | │ ├── app.vue 根组件
41 | │ ├── app.js
42 | │ ├── components 公共组件目录
43 | │ │ └── title.vue
44 | │ ├── routes 前端路由
45 | │ │ └── index.js
46 | │ └── views 页面目录
47 | │ └── Main.vue
48 | └── static 纯静态资源,不会被wabpack构建。
49 | 50 | 51 | # 性能测试 52 | 53 | [地址](http://browser.oneapm.com/index.html#/browser/9992975/overview) 54 | 55 | ``` 56 | account: 15271835241 57 | passwd: chendaye666 58 | ```git -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | import '../../lib/visualization/neod3' 21 | import '../../lib/visualization/components/collision' 22 | import '../../lib/visualization/components/graph' 23 | import '../../lib/visualization/components/graphGeometry' 24 | import '../../lib/visualization/components/graphView' 25 | import '../../lib/visualization/components/layout' 26 | import '../../lib/visualization/components/node' 27 | import '../../lib/visualization/components/relationship' 28 | import '../../lib/visualization/components/renderer' 29 | import '../../lib/visualization/components/style' 30 | import '../../lib/visualization/components/visualization' 31 | import '../../lib/visualization/renders/init' 32 | import '../../lib/visualization/renders/menu' 33 | import '../../lib/visualization/utils/adjacentAngles' 34 | import '../../lib/visualization/utils/angleList' 35 | import '../../lib/visualization/utils/arcArrow' 36 | import '../../lib/visualization/utils/arrays' 37 | import '../../lib/visualization/utils/circularLayout' 38 | import '../../lib/visualization/utils/circumferentialDistribution' 39 | import '../../lib/visualization/utils/circumferentialRelationshipRouting' 40 | import '../../lib/visualization/utils/clickHandler' 41 | import '../../lib/visualization/utils/loopArrow' 42 | import '../../lib/visualization/utils/pairwiseArcsRelationshipRouting' 43 | import '../../lib/visualization/utils/straightArrow' 44 | import '../../lib/visualization/utils/textMeasurement' 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neo4j-vue", 3 | "version": "0.1.0", 4 | "description": "neo4j", 5 | "license": "MIT", 6 | "author": { 7 | "name": "chendaye666", 8 | "email": "chendaye666@gmail.com" 9 | }, 10 | "keywords": [ 11 | "neo4j", 12 | "d3", 13 | "vue" 14 | ], 15 | "engines": { 16 | "node": ">=4", 17 | "npm": ">=3" 18 | }, 19 | "scripts": { 20 | "update": "npm update --registry=https://registry.npm.taobao.org", 21 | "build": "cross-env NODE_ENV=production node build/build.js", 22 | "clean": "rm -rf dist", 23 | "dev": "cross-env NODE_ENV=development node build/dev-server.js" 24 | }, 25 | "dependencies": { 26 | "element-ui": "^2.15.1", 27 | "jquery": "^3.2.1", 28 | "jschardet": "^3.0.0", 29 | "neo4j-driver": "^4.2.2", 30 | "papaparse": "^5.3.0", 31 | "uuid": "^8.3.2", 32 | "vue": "^2.6.12", 33 | "vue-cookies": "^1.7.4", 34 | "vue-neo4j": "^0.4.5", 35 | "vue-router": "^3.5.1", 36 | "vue-styled-components": "^1.6.0", 37 | "vuex": "^3.6.2", 38 | "vuex-router-sync": "^4.2.0" 39 | }, 40 | "devDependencies": { 41 | "autoprefixer": "^7.1.1", 42 | "babel-core": "^6.25.0", 43 | "babel-eslint": "^7.2.3", 44 | "babel-helper-vue-jsx-merge-props": "^2.0.2", 45 | "babel-loader": "^7.0.0", 46 | "babel-plugin-syntax-jsx": "^6.18.0", 47 | "babel-plugin-transform-export-extensions": "^6.22.0", 48 | "babel-plugin-transform-vue-jsx": "^3.4.3", 49 | "babel-polyfill": "^6.23.0", 50 | "babel-preset-es2015": "^6.24.1", 51 | "babel-preset-stage-2": "^6.24.1", 52 | "coffee-loader": "^0.7.3", 53 | "coffee-script": "^1.12.7", 54 | "connect-history-api-fallback": "^1.3.0", 55 | "cross-env": "^5.0.1", 56 | "css-loader": "^3.6.0", 57 | "express": "^4.15.3", 58 | "extract-text-webpack-plugin": "^2.1.2", 59 | "file-loader": "^0.11.2", 60 | "html-webpack-plugin": "^2.28.0", 61 | "http-proxy-middleware": "^0.17.4", 62 | "imports-loader": "^0.7.1", 63 | "json-loader": "^0.5.4", 64 | "ora": "^1.3.0", 65 | "serve-favicon": "^2.4.3", 66 | "shelljs": "^0.7.8", 67 | "url-loader": "^0.5.8", 68 | "vue-html-loader": "^1.2.4", 69 | "vue-loader": "^12.2.2", 70 | "vue-style-loader": "^3.0.1", 71 | "vue-template-compiler": "^2.4.2", 72 | "webpack": "^2.5.0", 73 | "webpack-dev-middleware": "^1.10.2", 74 | "webpack-hot-middleware": "^2.18.0", 75 | "webpack-merge": "^4.1.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/adjacentAngles.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.adjacentAngles 24 | 25 | findRuns: (angleList, minSeparation) -> 26 | 27 | p = 0 28 | start = 0 29 | end = 0 30 | runs = [] 31 | minStart = () -> 32 | if runs.length == 0 33 | 0 34 | else 35 | runs[0].start 36 | 37 | scanForDensePair = -> 38 | start = p 39 | end = angleList.wrapIndex(p + 1) 40 | if end == minStart() 41 | 'done' 42 | else 43 | p = end 44 | if tooDense(start, end) 45 | extendEnd 46 | 47 | else 48 | scanForDensePair 49 | 50 | extendEnd = -> 51 | if p == minStart() 52 | 'done' 53 | 54 | else if tooDense(start, angleList.wrapIndex(p + 1)) 55 | end = angleList.wrapIndex(p + 1) 56 | p = end 57 | extendEnd 58 | 59 | else 60 | p = start 61 | extendStart 62 | 63 | extendStart = -> 64 | candidateStart = angleList.wrapIndex(p - 1) 65 | if tooDense(candidateStart, end) and candidateStart != end 66 | start = candidateStart 67 | p = start 68 | extendStart 69 | 70 | else 71 | runs.push 72 | start: start 73 | end: end 74 | p = end 75 | scanForDensePair 76 | 77 | tooDense = (start, end) -> 78 | run = 79 | start: start 80 | end: end 81 | angleList.angle(run) < angleList.length(run) * minSeparation 82 | 83 | stepCount = 0 84 | step = scanForDensePair 85 | while step != 'done' 86 | if stepCount++ > angleList.totalLength() * 10 87 | console.log 'Warning: failed to layout arrows', ("#{ key }: #{ value.angle }" for own key, value of angleList.list).join('\n'), minSeparation 88 | break 89 | step = step() 90 | 91 | runs -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/straightArrow.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.straightArrow 24 | 25 | constructor: (startRadius, endRadius, centreDistance, shaftWidth, headWidth, headHeight, captionLayout) -> 26 | 27 | @length = centreDistance - (startRadius + endRadius) 28 | 29 | @shaftLength = @length - headHeight 30 | startArrow = startRadius 31 | endShaft = startArrow + @shaftLength 32 | endArrow = startArrow + @length 33 | shaftRadius = shaftWidth / 2 34 | headRadius = headWidth / 2 35 | 36 | @midShaftPoint = 37 | x: startArrow + @shaftLength / 2 38 | y: 0 39 | 40 | @outline = (shortCaptionLength) -> 41 | if captionLayout is "external" 42 | startBreak = startArrow + (@shaftLength - shortCaptionLength) / 2 43 | endBreak = endShaft - (@shaftLength - shortCaptionLength) / 2 44 | 45 | [ 46 | 'M', startArrow, shaftRadius, 47 | 'L', startBreak, shaftRadius, 48 | 'L', startBreak, -shaftRadius, 49 | 'L', startArrow, -shaftRadius, 50 | 'Z' 51 | 'M', endBreak, shaftRadius, 52 | 'L', endShaft, shaftRadius, 53 | 'L', endShaft, headRadius, 54 | 'L', endArrow, 0, 55 | 'L', endShaft, -headRadius, 56 | 'L', endShaft, -shaftRadius, 57 | 'L', endBreak, -shaftRadius, 58 | 'Z' 59 | ].join(' ') 60 | else 61 | [ 62 | 'M', startArrow, shaftRadius, 63 | 'L', endShaft, shaftRadius, 64 | 'L', endShaft, headRadius, 65 | 'L', endArrow, 0, 66 | 'L', endShaft, -headRadius, 67 | 'L', endShaft, -shaftRadius, 68 | 'L', startArrow, -shaftRadius, 69 | 'Z' 70 | ].join(' ') 71 | 72 | @overlay = (minWidth) -> 73 | radius = Math.max(minWidth / 2, shaftRadius) 74 | [ 75 | 'M', startArrow, radius, 76 | 'L', endArrow, radius, 77 | 'L', endArrow, -radius, 78 | 'L', startArrow, -radius, 79 | 'Z' 80 | ].join(' ') 81 | 82 | deflection: 0 83 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/bolt/boltHelpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* global location */ 22 | import bolt from './bolt' 23 | import { getUrlInfo } from '../utils' 24 | 25 | export const getEncryptionMode = () => { 26 | return location.protocol === 'https:' 27 | } 28 | 29 | export const getDiscoveryEndpoint = () => { 30 | const url = location.host ? location.href : 'http://localhost:7474/' 31 | const info = getUrlInfo(url) 32 | return `${info.protocol}//${info.host}/` 33 | } 34 | 35 | export const getServerConfig = (includePrefixes = []) => { 36 | return getJmxValues([['Configuration']]) 37 | .then((confs) => { 38 | const conf = confs[0] 39 | let filtered 40 | if (conf) { 41 | Object.keys(conf) 42 | .filter((key) => includePrefixes.length < 1 || includePrefixes.some((pfx) => key.startsWith(pfx))) 43 | .forEach((key) => (filtered = {...filtered, [key]: bolt.itemIntToNumber(conf[key].value)})) 44 | } 45 | return filtered || conf 46 | }) 47 | } 48 | 49 | export const getJmxValues = (nameAttributePairs = []) => { 50 | if (!nameAttributePairs.length) return Promise.reject(null) 51 | return bolt.directTransaction('CALL dbms.queryJmx("org.neo4j:*")') 52 | .then((res) => { 53 | let out = [] 54 | nameAttributePairs.forEach((pair) => { 55 | const [name, attribute = null] = pair 56 | if (!name) return out.push(null) 57 | const part = res.records.filter((record) => record.get('name').match(new RegExp(name + '$'))) 58 | if (!part.length) return out.push(null) 59 | const attributes = part[0].get('attributes') 60 | if (!attribute) return out.push(attributes) 61 | const key = attribute 62 | if (typeof attributes[key] === 'undefined') return out.push(null) 63 | const val = bolt.itemIntToNumber(attributes[key].value) 64 | out.push({ [key]: val }) 65 | }) 66 | return out 67 | }).catch((e) => { 68 | return null 69 | }) 70 | } 71 | 72 | export const isConfigValTruthy = (val) => [true, 'true', 'yes', 1, '1'].indexOf(val) > -1 73 | export const isConfigValFalsy = (val) => [false, 'false', 'no', 0, '0'].indexOf(val) > -1 74 | -------------------------------------------------------------------------------- /src/components/D3Visualization/mapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | export function createGraph (nodes, relationships) { 22 | let graph = new neo.models.Graph() 23 | graph.addNodes(mapNodes(nodes)) 24 | graph.addRelationships(mapRelationships(relationships, graph)) 25 | graph.display = { initialNodeDisplay: 300, nodeCount: 1 } 26 | return graph 27 | } 28 | 29 | export function mapNodes (nodes) { 30 | return nodes.map((node) => new neo.models.Node(node.id, node.labels, node.properties)) 31 | } 32 | 33 | export function mapRelationships (relationships, graph) { 34 | return relationships.map((rel) => { 35 | const source = graph.findNode(rel.startNodeId) 36 | const target = graph.findNode(rel.endNodeId) 37 | return new neo.models.Relationship(rel.id, source, target, rel.type, rel.properties) 38 | }) 39 | } 40 | 41 | export function getGraphStats (graph) { 42 | let labelStats = {} 43 | let relTypeStats = {} 44 | 45 | graph.nodes().forEach((node) => { 46 | node.labels.forEach((label) => { 47 | if (labelStats['*']) { 48 | labelStats['*'].count = labelStats['*'].count + 1 49 | } else { 50 | labelStats['*'] = { 51 | count: 1, 52 | properties: [] 53 | } 54 | } 55 | if (labelStats[label]) { 56 | labelStats[label].count = labelStats[label].count + 1 57 | labelStats[label].properties = Object.assign({}, labelStats[label].properties, node.propertyMap) 58 | } else { 59 | labelStats[label] = { 60 | count: 1, 61 | properties: node.propertyMap 62 | } 63 | } 64 | }) 65 | }) 66 | graph.relationships().forEach((rel) => { 67 | if (relTypeStats['*']) { 68 | relTypeStats['*'].count = relTypeStats['*'].count + 1 69 | } else { 70 | relTypeStats['*'] = { 71 | count: 1, 72 | properties: [] 73 | } 74 | } 75 | if (relTypeStats[rel.type]) { 76 | relTypeStats[rel.type].count = relTypeStats[rel.type].count + 1 77 | relTypeStats[rel.type].properties = Object.assign({}, relTypeStats[rel.type].properties, rel.propertyMap) 78 | } else { 79 | relTypeStats[rel.type] = { 80 | count: 1, 81 | properties: rel.propertyMap 82 | } 83 | } 84 | }) 85 | return {labels: labelStats, relTypes: relTypeStats} 86 | } 87 | -------------------------------------------------------------------------------- /npm-debug.log: -------------------------------------------------------------------------------- 1 | 0 info it worked if it ends with ok 2 | 1 verbose cli [ '/usr/bin/node', '/usr/bin/npm', 'run', 'dev' ] 3 | 2 info using npm@3.5.2 4 | 3 info using node@v8.10.0 5 | 4 verbose run-script [ 'predev', 'dev', 'postdev' ] 6 | 5 info lifecycle neo4j-vue@0.1.0~predev: neo4j-vue@0.1.0 7 | 6 silly lifecycle neo4j-vue@0.1.0~predev: no script for predev, continuing 8 | 7 info lifecycle neo4j-vue@0.1.0~dev: neo4j-vue@0.1.0 9 | 8 verbose lifecycle neo4j-vue@0.1.0~dev: unsafe-perm in lifecycle true 10 | 9 verbose lifecycle neo4j-vue@0.1.0~dev: PATH: /usr/share/npm/bin/node-gyp-bin:/data/neo4j-vue/node_modules/.bin:/usr/local/jdk-11.0.7/bin:/root/.yarn/bin:/root/.config/yarn/global/node_modules/.bin:/usr/lib/jvm/jdk1.8.0_162/bin:/usr/local/sbt/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/hive/bin:/usr/local/scala/bin:/usr/local/spark/bin:/usr/local/spark/sbin:/usr/local/hadoop/bin:/usr/local/scala/bin:/usr/local/hbase/bin:/usr/local/flume/bin:/usr/local/sbt/bin:/usr/local/sqoop/bin 11 | 10 verbose lifecycle neo4j-vue@0.1.0~dev: CWD: /data/neo4j-vue 12 | 11 silly lifecycle neo4j-vue@0.1.0~dev: Args: [ '-c', 13 | 11 silly lifecycle 'cross-env NODE_ENV=development node build/dev-server.js' ] 14 | 12 silly lifecycle neo4j-vue@0.1.0~dev: Returned: code: 1 signal: null 15 | 13 info lifecycle neo4j-vue@0.1.0~dev: Failed to exec dev script 16 | 14 verbose stack Error: neo4j-vue@0.1.0 dev: `cross-env NODE_ENV=development node build/dev-server.js` 17 | 14 verbose stack Exit status 1 18 | 14 verbose stack at EventEmitter. (/usr/share/npm/lib/utils/lifecycle.js:232:16) 19 | 14 verbose stack at emitTwo (events.js:126:13) 20 | 14 verbose stack at EventEmitter.emit (events.js:214:7) 21 | 14 verbose stack at ChildProcess. (/usr/share/npm/lib/utils/spawn.js:24:14) 22 | 14 verbose stack at emitTwo (events.js:126:13) 23 | 14 verbose stack at ChildProcess.emit (events.js:214:7) 24 | 14 verbose stack at maybeClose (internal/child_process.js:925:16) 25 | 14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:209:5) 26 | 15 verbose pkgid neo4j-vue@0.1.0 27 | 16 verbose cwd /data/neo4j-vue 28 | 17 error Linux 4.15.0-135-generic 29 | 18 error argv "/usr/bin/node" "/usr/bin/npm" "run" "dev" 30 | 19 error node v8.10.0 31 | 20 error npm v3.5.2 32 | 21 error code ELIFECYCLE 33 | 22 error neo4j-vue@0.1.0 dev: `cross-env NODE_ENV=development node build/dev-server.js` 34 | 22 error Exit status 1 35 | 23 error Failed at the neo4j-vue@0.1.0 dev script 'cross-env NODE_ENV=development node build/dev-server.js'. 36 | 23 error Make sure you have the latest version of node.js and npm installed. 37 | 23 error If you do, this is most likely a problem with the neo4j-vue package, 38 | 23 error not with npm itself. 39 | 23 error Tell the author that this fails on your system: 40 | 23 error cross-env NODE_ENV=development node build/dev-server.js 41 | 23 error You can get information on how to open an issue for this project with: 42 | 23 error npm bugs neo4j-vue 43 | 23 error Or if that isn't available, you can get their info via: 44 | 23 error npm owner ls neo4j-vue 45 | 23 error There is likely additional logging output above. 46 | 24 verbose exit [ 1, true ] 47 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/loopArrow.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.loopArrow 24 | 25 | constructor: (nodeRadius, straightLength, spreadDegrees, shaftWidth, headWidth, headLength, captionHeight) -> 26 | 27 | spread = spreadDegrees * Math.PI / 180 28 | r1 = nodeRadius 29 | r2 = nodeRadius + headLength 30 | r3 = nodeRadius + straightLength 31 | loopRadius = r3 * Math.tan(spread / 2) 32 | shaftRadius = shaftWidth / 2 33 | @shaftLength = loopRadius * 3 + shaftWidth 34 | 35 | class Point 36 | constructor: (@x, @y) -> 37 | toString: -> 38 | "#{@x} #{@y}" 39 | 40 | normalPoint = (sweep, radius, displacement) -> 41 | localLoopRadius = radius * Math.tan(spread / 2) 42 | cy = radius / Math.cos(spread / 2) 43 | new Point( 44 | (localLoopRadius + displacement) * Math.sin(sweep), 45 | cy + (localLoopRadius + displacement) * Math.cos(sweep) 46 | ) 47 | @midShaftPoint = normalPoint(0, r3, shaftRadius + captionHeight / 2 + 2) 48 | startPoint = (radius, displacement) -> 49 | normalPoint((Math.PI + spread) / 2, radius, displacement) 50 | endPoint = (radius, displacement) -> 51 | normalPoint(-(Math.PI + spread) / 2, radius, displacement) 52 | 53 | @outline = -> 54 | inner = loopRadius - shaftRadius 55 | outer = loopRadius + shaftRadius 56 | [ 57 | 'M', startPoint(r1, shaftRadius) 58 | 'L', startPoint(r3, shaftRadius) 59 | 'A', outer, outer, 0, 1, 1, endPoint(r3, shaftRadius) 60 | 'L', endPoint(r2, shaftRadius) 61 | 'L', endPoint(r2, -headWidth / 2) 62 | 'L', endPoint(r1, 0) 63 | 'L', endPoint(r2, headWidth / 2) 64 | 'L', endPoint(r2, -shaftRadius) 65 | 'L', endPoint(r3, -shaftRadius) 66 | 'A', inner, inner, 0, 1, 0, startPoint(r3, -shaftRadius) 67 | 'L', startPoint(r1, -shaftRadius) 68 | 'Z' 69 | ].join(' ') 70 | 71 | @overlay = (minWidth) -> 72 | displacement = Math.max(minWidth / 2, shaftRadius) 73 | inner = loopRadius - displacement 74 | outer = loopRadius + displacement 75 | [ 76 | 'M', startPoint(r1, displacement) 77 | 'L', startPoint(r3, displacement) 78 | 'A', outer, outer, 0, 1, 1, endPoint(r3, displacement) 79 | 'L', endPoint(r2, displacement) 80 | 'L', endPoint(r2, -displacement) 81 | 'L', endPoint(r3, -displacement) 82 | 'A', inner, inner, 0, 1, 0, startPoint(r3, -displacement) 83 | 'L', startPoint(r1, -displacement) 84 | 'Z' 85 | ].join(' ') -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/layout.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.layout = do -> 24 | _layout = {} 25 | 26 | _layout.force = -> 27 | _force = {} 28 | 29 | _force.init = (render) -> 30 | forceLayout = {} 31 | 32 | linkDistance = 45 33 | 34 | d3force = d3.layout.force() 35 | .linkDistance((relationship) -> relationship.source.radius + relationship.target.radius + linkDistance) 36 | .charge(-1000) 37 | 38 | newStatsBucket = -> 39 | bucket = 40 | layoutTime: 0 41 | layoutSteps: 0 42 | bucket 43 | 44 | currentStats = newStatsBucket() 45 | 46 | forceLayout.collectStats = -> 47 | latestStats = currentStats 48 | currentStats = newStatsBucket() 49 | latestStats 50 | 51 | accelerateLayout = -> 52 | maxStepsPerTick = 100 53 | maxAnimationFramesPerSecond = 60 54 | maxComputeTime = 1000 / maxAnimationFramesPerSecond 55 | now = if window.performance and window.performance.now 56 | () -> 57 | window.performance.now() 58 | else 59 | () -> 60 | Date.now() 61 | 62 | d3Tick = d3force.tick 63 | d3force.tick = -> 64 | startTick = now() 65 | step = maxStepsPerTick 66 | while step-- and now() - startTick < maxComputeTime 67 | startCalcs = now() 68 | currentStats.layoutSteps++ 69 | 70 | neo.collision.avoidOverlap d3force.nodes() 71 | 72 | if d3Tick() 73 | maxStepsPerTick = 2 74 | return true 75 | currentStats.layoutTime += now() - startCalcs 76 | render() 77 | false 78 | 79 | accelerateLayout() 80 | 81 | oneRelationshipPerPairOfNodes = (graph) -> 82 | (pair.relationships[0] for pair in graph.groupedRelationships()) 83 | 84 | forceLayout.update = (graph, size) -> 85 | 86 | nodes = neo.utils.cloneArray(graph.nodes()) 87 | relationships = oneRelationshipPerPairOfNodes(graph) 88 | 89 | radius = nodes.length * linkDistance / (Math.PI * 2) 90 | center = 91 | x: size[0] / 2 92 | y: size[1] / 2 93 | neo.utils.circularLayout(nodes, center, radius) 94 | 95 | d3force 96 | .nodes(nodes) 97 | .links(relationships) 98 | .size(size) 99 | .start() 100 | 101 | forceLayout.drag = d3force.drag 102 | forceLayout 103 | 104 | _force 105 | 106 | _layout -------------------------------------------------------------------------------- /src/components/D3Visualization/services/exceptions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import * as messages from './exceptionMessages' 22 | 23 | export function getErrorMessage (errorObject) { 24 | let str = messages[errorObject.type] 25 | if (!str) return 26 | const keys = Object.keys(errorObject) 27 | keys.forEach((prop) => { 28 | const re = new RegExp('(#' + prop + '#)', 'g') 29 | str = str.replace(re, errorObject[prop]) 30 | }) 31 | return str 32 | } 33 | 34 | export function createErrorObject (ErrorType, ...rest) { 35 | const obj = new ErrorType(...rest) 36 | if (!obj.code) obj.code = obj.type 37 | obj.message = getErrorMessage(obj) 38 | return obj 39 | } 40 | 41 | export function UserException (message) { 42 | return { 43 | type: 'UserException', 44 | message 45 | } 46 | } 47 | 48 | export function ConnectionException (message, code = 'Connection Error') { 49 | return {fields: [{ 50 | code, 51 | message 52 | }]} 53 | } 54 | 55 | export function AddServerValidationError () { 56 | return { 57 | type: 'AddServerValidationError' 58 | } 59 | } 60 | 61 | export function CreateDataSourceValidationError () { 62 | return { 63 | type: 'CreateDataSourceValidationError' 64 | } 65 | } 66 | 67 | export function RemoveDataSourceValidationError () { 68 | return { 69 | type: 'RemoveDataSourceValidationError' 70 | } 71 | } 72 | 73 | export function BoltConnectionError () { 74 | return { 75 | type: 'BoltConnectionError' 76 | } 77 | } 78 | 79 | export function BoltError (obj) { 80 | return { 81 | type: 'BoltError', 82 | code: obj.fields[0].code, 83 | message: obj.fields[0].message 84 | } 85 | } 86 | 87 | export function Neo4jError (obj) { 88 | return { 89 | type: 'Neo4jError', 90 | message: obj.message 91 | } 92 | } 93 | 94 | export function ConnectionNotFoundError (name) { 95 | return { 96 | type: 'ConnectionNotFoundError', 97 | name 98 | } 99 | } 100 | 101 | export function OpenConnectionNotFoundError (name) { 102 | return { 103 | type: 'OpenConnectionNotFoundError', 104 | name 105 | } 106 | } 107 | 108 | export function UnknownCommandError (cmd) { 109 | return { 110 | type: 'UnknownCommandError', 111 | cmd 112 | } 113 | } 114 | 115 | export function CouldNotFetchRemoteGuideError (error) { 116 | return { 117 | type: 'CouldNotFetchRemoteGuideError', 118 | error 119 | } 120 | } 121 | 122 | export function FetchURLError (error) { 123 | return { 124 | type: 'FetchURLError', 125 | error 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /flex.md: -------------------------------------------------------------------------------- 1 | # 容器属性 2 | 3 | ## flex-direction 4 | 5 | > 决定主轴的方向(即项目的排列方向) 6 | 7 | - row(默认值):主轴为水平方向,起点在左端。 8 | - row-reverse:主轴为水平方向,起点在右端。 9 | - column:主轴为垂直方向,起点在上沿。 10 | - column-reverse:主轴为垂直方向,起点在下沿。 11 | 12 | ```css 13 | .box { 14 | flex-direction: row | row-reverse | column | column-reverse; 15 | } 16 | ``` 17 | 18 | ## flex-wrap 19 | 20 | > 默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap 属性定义,如果一条轴线排不下,如何换行 21 | 22 | - nowrap(默认):不换行。 23 | - wrap:换行,第一行在下方。 24 | - wrap-reverse:换行,第一行在上方。 25 | 26 | ```css 27 | .box { 28 | flex-wrap: nowrap | wrap | wrap-reverse; 29 | } 30 | ``` 31 | 32 | ## flex-flow 33 | 34 | > flex-flow 属性是 flex-direction 属性和 flex-wrap 属性的简写形式,默认值为 row nowrap 35 | 36 | ```css 37 | .box { 38 | flex-flow: || ; 39 | } 40 | ``` 41 | 42 | ## justify-content 43 | 44 | > justify-content 属性定义了项目在主轴上的对齐方式。 45 | 46 | - flex-start(默认值):左对齐 47 | - flex-end:右对齐 48 | - center: 居中 49 | - space-between:两端对齐,项目之间的间隔都相等。 50 | - space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。 51 | 52 | ```css 53 | .box { 54 | justify-content: flex-start | flex-end | center | space-between | space-around; 55 | } 56 | ``` 57 | 58 | ## align-items 59 | 60 | > align-items 属性定义项目在交叉轴(纵轴)上如何对齐。 61 | 62 | - flex-start:交叉轴的起点对齐。 63 | - flex-end:交叉轴的终点对齐。 64 | - center:交叉轴的中点对齐。 65 | - baseline: 项目的第一行文字的基线对齐。 66 | - stretch(默认值):如果项目未设置高度或设为 auto,将占满整个容器的高度。 67 | 68 | ```css 69 | .box { 70 | align-items: flex-start | flex-end | center | baseline | stretch; 71 | } 72 | ``` 73 | 74 | ## align-content 75 | 76 | > align-content 属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。 77 | 78 | - flex-start:与交叉轴的起点对齐。 79 | - flex-end:与交叉轴的终点对齐。 80 | - center:与交叉轴的中点对齐。 81 | - space-between:与交叉轴两端对齐,轴线之间的间隔平均分布。 82 | - space-around:每根轴线两侧的间隔都相等。所以,轴线之间的间隔比轴线与边框的间隔大一倍。 83 | - stretch(默认值):轴线占满整个交叉轴。 84 | 85 | ```css 86 | .box { 87 | align-content: flex-start | flex-end | center | space-between | space-around | 88 | stretch; 89 | } 90 | ``` 91 | 92 | # 项目的属性 93 | 94 | ## order 95 | 96 | > order 属性定义项目的排列顺序。数值越小,排列越靠前,默认为 0。 97 | 98 | ```css 99 | .item { 100 | order: ; 101 | } 102 | ``` 103 | 104 | ## flex-grow 105 | 106 | > flex-grow 属性定义项目的放大比例,默认为 0,即如果存在剩余空间,也不放大 107 | > 如果所有项目的 flex-grow 属性都为 1,则它们将等分剩余空间(如果有的话)。 108 | > 如果一个项目的 flex-grow 属性为 2,其他项目都为 1,则前者占据的剩余空间将比其他项多一倍。 109 | 110 | ```css 111 | .item { 112 | flex-grow: ; /* default 0 */ 113 | } 114 | ``` 115 | 116 | ## flex-shrink 117 | 118 | > flex-shrink 属性定义了项目的缩小比例,默认为 1,即如果空间不足,该项目将缩小。 119 | > 如果所有项目的 flex-shrink 属性都为 1,当空间不足时,都将等比例缩小。 120 | > 如果一个项目的 flex-shrink 属性为 0,其他项目都为 1,则空间不足时,前者不缩小。 121 | > 负值对该属性无效。 122 | 123 | ```css 124 | .item { 125 | flex-shrink: ; /* default 1 */ 126 | } 127 | ``` 128 | 129 | ## flex-basis 130 | 131 | > flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。 132 | > 浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小 133 | > 它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。 134 | 135 | ```css 136 | .item { 137 | flex-basis: | auto; /* default auto */ 138 | } 139 | ``` 140 | 141 | ## flex 142 | 143 | > flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。 144 | > 该属性有两个快捷值:auto (1 1 auto) 和 none (0 0 auto)。 145 | > 建议优先使用这个属性,而不是单独写三个分离的属性,因为浏览器会推算相关值。 146 | 147 | 148 | ```css 149 | .item { 150 | flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] 151 | } 152 | ``` 153 | 154 | ## align-self 155 | 156 | > align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。 157 | > 默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch。 158 | > 该属性可能取6个值,除了auto,其他都与align-items属性完全一致。 159 | 160 | 161 | ```css 162 | .item { 163 | align-self: auto | flex-start | flex-end | center | baseline | stretch; 164 | } 165 | ``` -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/circumferentialDistribution.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | neo.utils.distributeCircular = (arrowAngles, minSeparation) -> 24 | list = [] 25 | for key, angle of arrowAngles.floating 26 | list.push 27 | key: key 28 | angle: angle 29 | fixed: false 30 | for key, angle of arrowAngles.fixed 31 | list.push 32 | key: key 33 | angle: angle 34 | fixed: true 35 | 36 | list.sort((a, b) -> a.angle - b.angle) 37 | 38 | angleList = new neo.utils.angleList(list) 39 | runsOfTooDenseArrows = new neo.utils.adjacentAngles().findRuns(angleList, minSeparation) 40 | 41 | wrapAngle = (angle) -> 42 | if angle >= 360 43 | angle - 360 44 | else if angle < 0 45 | angle + 360 46 | else 47 | angle 48 | 49 | result = {} 50 | 51 | splitByFixedArrows = (run) -> 52 | runs = [] 53 | currentStart = run.start 54 | for i in [1..angleList.length(run)] 55 | wrapped = angleList.wrapIndex(run.start + i) 56 | if angleList.fixed(wrapped) 57 | runs.push 58 | start: currentStart 59 | end: wrapped 60 | currentStart = wrapped 61 | if not angleList.fixed(run.end) 62 | runs.push 63 | start: currentStart 64 | end: run.end 65 | runs 66 | 67 | for tooDenseRun in runsOfTooDenseArrows 68 | moveableRuns = splitByFixedArrows(tooDenseRun) 69 | for run in moveableRuns 70 | runLength = angleList.length(run) 71 | if angleList.fixed(run.start) and angleList.fixed(run.end) 72 | separation = angleList.angle(run) / runLength 73 | for i in [0..runLength] 74 | rawAngle = list[run.start].angle + i * separation 75 | result[list[angleList.wrapIndex(run.start + i)].key] = wrapAngle(rawAngle) 76 | else if angleList.fixed(run.start) and not angleList.fixed(run.end) 77 | for i in [0..runLength] 78 | rawAngle = list[run.start].angle + i * minSeparation 79 | result[list[angleList.wrapIndex(run.start + i)].key] = wrapAngle(rawAngle) 80 | else if not angleList.fixed(run.start) and angleList.fixed(run.end) 81 | for i in [0..runLength] 82 | rawAngle = list[run.end].angle - (runLength - i) * minSeparation 83 | result[list[angleList.wrapIndex(run.start + i)].key] = wrapAngle(rawAngle) 84 | else 85 | center = list[run.start].angle + angleList.angle(run) / 2 86 | for i in [0..runLength] 87 | rawAngle = center + (i - runLength / 2) * minSeparation 88 | result[list[angleList.wrapIndex(run.start + i)].key] = wrapAngle(rawAngle) 89 | 90 | 91 | for key of arrowAngles.floating 92 | if !result.hasOwnProperty(key) 93 | result[key] = arrowAngles.floating[key] 94 | 95 | for key of arrowAngles.fixed 96 | if !result.hasOwnProperty(key) 97 | result[key] = arrowAngles.fixed[key] 98 | 99 | result -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/graphGeometry.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.NeoD3Geometry 24 | square = (distance) -> distance * distance 25 | 26 | constructor: (@style) -> 27 | @relationshipRouting = new neo.utils.pairwiseArcsRelationshipRouting(@style) 28 | 29 | addShortenedNextWord = (line, word, measure) -> 30 | until word.length <= 2 31 | word = word.substr(0, word.length - 2) + '\u2026' 32 | if measure(word) < line.remainingWidth 33 | line.text += " " + word 34 | break 35 | 36 | noEmptyLines = (lines) -> 37 | for line in lines 38 | if line.text.length is 0 then return false 39 | true 40 | 41 | fitCaptionIntoCircle = (node, style) -> 42 | template = style.forNode(node).get("caption") 43 | captionText = style.interpolate(template, node) 44 | fontFamily = 'sans-serif' 45 | fontSize = parseFloat(style.forNode(node).get('font-size')) 46 | lineHeight = fontSize 47 | measure = (text) -> 48 | neo.utils.measureText(text, fontFamily, fontSize) 49 | 50 | words = captionText.split(" ") 51 | 52 | emptyLine = (lineCount, iLine) -> 53 | baseline = (1 + iLine - lineCount / 2) * lineHeight 54 | if (style.forNode(node).get("icon-code")) 55 | baseline = baseline + node.radius/3 56 | constainingHeight = if iLine < lineCount / 2 then baseline - lineHeight else baseline 57 | lineWidth = Math.sqrt(square(node.radius) - square(constainingHeight)) * 2 58 | { 59 | node: node 60 | text: '' 61 | baseline: baseline 62 | remainingWidth: lineWidth 63 | } 64 | 65 | fitOnFixedNumberOfLines = (lineCount) -> 66 | lines = [] 67 | iWord = 0; 68 | for iLine in [0..lineCount - 1] 69 | line = emptyLine(lineCount, iLine) 70 | while iWord < words.length and measure(" " + words[iWord]) < line.remainingWidth 71 | line.text += " " + words[iWord] 72 | line.remainingWidth -= measure(" " + words[iWord]) 73 | iWord++ 74 | lines.push line 75 | if iWord < words.length 76 | addShortenedNextWord(lines[lineCount - 1], words[iWord], measure) 77 | [lines, iWord] 78 | 79 | consumedWords = 0 80 | maxLines = node.radius * 2 / fontSize 81 | 82 | lines = [emptyLine(1, 0)] 83 | for lineCount in [1..maxLines] 84 | [candidateLines, candidateWords] = fitOnFixedNumberOfLines(lineCount) 85 | if noEmptyLines(candidateLines) 86 | [lines, consumedWords] = [candidateLines, candidateWords] 87 | if consumedWords >= words.length 88 | return lines 89 | lines 90 | 91 | formatNodeCaptions: (nodes) -> 92 | for node in nodes 93 | node.caption = fitCaptionIntoCircle(node, @style) 94 | 95 | formatRelationshipCaptions: (relationships) -> 96 | for relationship in relationships 97 | template = @style.forRelationship(relationship).get("caption") 98 | relationship.caption = @style.interpolate(template, relationship) 99 | 100 | setNodeRadii: (nodes) -> 101 | for node in nodes 102 | node.radius = parseFloat(@style.forNode(node).get("diameter")) / 2 103 | 104 | onGraphChange: (graph) -> 105 | @setNodeRadii(graph.nodes()) 106 | @formatNodeCaptions(graph.nodes()) 107 | @formatRelationshipCaptions(graph.relationships()) 108 | @relationshipRouting.measureRelationshipCaptions(graph.relationships()) 109 | 110 | onTick: (graph) -> 111 | @relationshipRouting.layoutRelationships(graph) 112 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/components/graph.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.models.Graph 24 | constructor: () -> 25 | @nodeMap = {} 26 | @_nodes = [] 27 | @relationshipMap = {} 28 | @_relationships = [] 29 | 30 | nodes: -> 31 | @_nodes 32 | 33 | relationships: -> 34 | @_relationships 35 | 36 | groupedRelationships: -> 37 | class NodePair 38 | constructor: (node1, node2) -> 39 | @relationships = [] 40 | if node1.id < node2.id 41 | @nodeA = node1 42 | @nodeB = node2 43 | else 44 | @nodeA = node2 45 | @nodeB = node1 46 | 47 | isLoop: -> 48 | @nodeA is @nodeB 49 | 50 | toString: -> 51 | "#{@nodeA.id}:#{@nodeB.id}" 52 | groups = {} 53 | for relationship in @_relationships 54 | nodePair = new NodePair(relationship.source, relationship.target) 55 | nodePair = groups[nodePair] ? nodePair 56 | nodePair.relationships.push relationship 57 | groups[nodePair] = nodePair 58 | (pair for ignored, pair of groups) 59 | 60 | addNodes: (nodes) => 61 | for node in nodes 62 | if !@findNode(node.id)? 63 | @nodeMap[node.id] = node 64 | @_nodes.push(node) 65 | @ 66 | 67 | removeNode: (node) => 68 | if @findNode(node.id)? 69 | delete @nodeMap[node.id] 70 | @_nodes.splice(@_nodes.indexOf(node), 1) 71 | @ 72 | 73 | updateNode: (node) => 74 | if @findNode(node.id)? 75 | @removeNode node 76 | node.expanded = false 77 | node.minified = true 78 | @addNodes [node] 79 | @ 80 | 81 | removeConnectedRelationships: (node) => 82 | for r in @findAllRelationshipToNode node 83 | @updateNode r.source 84 | @updateNode r.target 85 | @_relationships.splice(@_relationships.indexOf(r), 1) 86 | delete @relationshipMap[r.id] 87 | @ 88 | 89 | addRelationships: (relationships) => 90 | for relationship in relationships 91 | existingRelationship = @findRelationship(relationship.id) 92 | if existingRelationship? 93 | existingRelationship.internal = false 94 | else 95 | relationship.internal = false 96 | @relationshipMap[relationship.id] = relationship 97 | @_relationships.push(relationship) 98 | @ 99 | 100 | addInternalRelationships: (relationships) => 101 | for relationship in relationships 102 | relationship.internal = true 103 | if not @findRelationship(relationship.id)? 104 | @relationshipMap[relationship.id] = relationship 105 | @_relationships.push(relationship) 106 | @ 107 | 108 | pruneInternalRelationships: => 109 | relationships = @_relationships.filter((relationship) -> not relationship.internal) 110 | @relationshipMap = {} 111 | @_relationships = [] 112 | @addRelationships(relationships) 113 | 114 | findNode: (id) => @nodeMap[id] 115 | 116 | findNodeNeighbourIds: (id) => 117 | @_relationships 118 | .filter((relationship) -> relationship.source.id is id or relationship.target.id is id) 119 | .map((relationship) -> 120 | if relationship.target.id is id 121 | return relationship.source.id 122 | return relationship.target.id 123 | ) 124 | 125 | findRelationship: (id) => @relationshipMap[id] 126 | 127 | findAllRelationshipToNode: (node) => 128 | @_relationships 129 | .filter((relationship) -> relationship.source.id is node.id or relationship.target.id is node.id) 130 | 131 | resetGraph: -> 132 | @nodeMap = {} 133 | @_nodes = [] 134 | @relationshipMap = {} 135 | @_relationships = [] 136 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/renders/menu.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2015 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | do -> 24 | noop = -> 25 | 26 | numberOfItemsInContextMenu = 3 27 | 28 | arc = (radius, itemNumber, width = 30) -> 29 | itemNumber = itemNumber - 1 30 | startAngle = ((2*Math.PI)/numberOfItemsInContextMenu) * itemNumber 31 | endAngle = startAngle + ((2*Math.PI)/numberOfItemsInContextMenu) 32 | innerRadius = Math.max(radius + 8, 20) 33 | d3.svg.arc().innerRadius(innerRadius).outerRadius(innerRadius + width).startAngle(startAngle).endAngle(endAngle).padAngle(.03) 34 | 35 | getSelectedNode = (node) -> if node.selected then [node] else [] 36 | 37 | attachContextEvent = (event, elems, viz, content, label) -> 38 | for elem in elems 39 | elem.on('mousedown.drag', -> 40 | d3.event.stopPropagation() 41 | null) 42 | elem.on('mouseup', (node) -> 43 | viz.trigger(event, node)) 44 | elem.on('mouseover', (node) -> 45 | node.contextMenu = 46 | menuSelection: event 47 | menuContent: content 48 | label:label 49 | viz.trigger('menuMouseOver', node)) 50 | elem.on('mouseout', (node) -> 51 | delete node.contextMenu 52 | viz.trigger('menuMouseOut', node)) 53 | 54 | createMenuItem = (selection, viz, eventName, itemNumber, className, position, textValue, helpValue) -> 55 | path = selection.selectAll('path.' + className).data(getSelectedNode) 56 | textpath = selection.selectAll('text.' + className).data(getSelectedNode) 57 | 58 | tab = path.enter() 59 | .append('path') 60 | .classed(className, true) 61 | .classed('context-menu-item', true) 62 | .attr 63 | d: (node) -> arc(node.radius, itemNumber, 1)() 64 | 65 | text = textpath.enter() 66 | .append('text') 67 | .classed('context-menu-item', true) 68 | .text(textValue) 69 | .attr("transform", "scale(0.1)") 70 | .attr 71 | 'font-family': 'FontAwesome' 72 | fill: (node) -> viz.style.forNode(node).get('text-color-internal') 73 | x: (node) -> arc(node.radius, itemNumber).centroid()[0] + position[0] 74 | y: (node) -> arc(node.radius, itemNumber).centroid()[1] + position[1] 75 | 76 | attachContextEvent(eventName, [tab, text], viz, helpValue, textValue) 77 | 78 | tab 79 | .transition() 80 | .duration(200) 81 | .attr 82 | d: (node) -> arc(node.radius, itemNumber)() 83 | 84 | text 85 | .attr("transform", "scale(1)") 86 | 87 | path 88 | .exit() 89 | .transition() 90 | .duration(200) 91 | .attr 92 | d: (node) -> arc(node.radius, itemNumber, 1)() 93 | .remove() 94 | 95 | textpath 96 | .exit() 97 | .attr("transform", "scale(0)") 98 | .remove() 99 | 100 | donutRemoveNode = new neo.Renderer( 101 | onGraphChange: (selection, viz) -> createMenuItem(selection, viz, 'nodeClose', 1, 'remove_node', [-4, 0], '\uf00d', 'Remove node from the visualization') 102 | 103 | onTick: noop 104 | ) 105 | 106 | donutExpandNode = new neo.Renderer( 107 | onGraphChange: (selection, viz) -> createMenuItem(selection, viz, 'nodeDblClicked', 2, 'expand_node', [0, 4], '\uf0b2', 'Expand child relationships') 108 | 109 | onTick: noop 110 | ) 111 | 112 | donutUnlockNode = new neo.Renderer( 113 | onGraphChange: (selection, viz) -> createMenuItem(selection, viz, 'nodeUnlock', 3, 'unlock_node', [4, 0], '\uf09c', 'Unlock the node to re-layout the graph') 114 | 115 | onTick: noop 116 | ) 117 | 118 | neo.renderers.menu.push(donutExpandNode) 119 | neo.renderers.menu.push(donutRemoveNode) 120 | neo.renderers.menu.push(donutUnlockNode) 121 | -------------------------------------------------------------------------------- /src/components/Search.vue: -------------------------------------------------------------------------------- 1 | 57 | 131 | 132 | -------------------------------------------------------------------------------- /src/views/neo4j.vue: -------------------------------------------------------------------------------- 1 | 50 | 120 | 121 | -------------------------------------------------------------------------------- /src/components/D3Visualization/GraphEventHandler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import {mapNodes, mapRelationships, getGraphStats} from './mapper' 22 | 23 | export class GraphEventHandler { 24 | constructor (graph, graphView, getNodeNeighbours, onItemMouseOver, onItemSelected, onGraphModelChange) { 25 | this.graph = graph 26 | this.graphView = graphView 27 | this.getNodeNeighbours = getNodeNeighbours 28 | this.selectedItem = null 29 | this.onItemMouseOver = onItemMouseOver 30 | this.onItemSelected = onItemSelected 31 | this.onGraphModelChange = onGraphModelChange 32 | } 33 | 34 | graphModelChanged () { 35 | this.onGraphModelChange(getGraphStats(this.graph)) 36 | } 37 | 38 | selectItem (item) { 39 | if (this.selectedItem) { 40 | this.selectedItem.selected = false 41 | } 42 | this.selectedItem = item 43 | item.selected = true 44 | this.graphView.update() 45 | } 46 | 47 | deselectItem () { 48 | if (this.selectedItem) { 49 | this.selectedItem.selected = false 50 | this.selectedItem = null 51 | } 52 | this.onItemSelected({type: 'canvas', item: {nodeCount: this.graph.nodes().length, relationshipCount: this.graph.relationships().length}}) 53 | this.graphView.update() 54 | } 55 | 56 | nodeClose (d) { 57 | this.graph.removeConnectedRelationships(d) 58 | this.graph.removeNode(d) 59 | this.deselectItem() 60 | this.graphView.update() 61 | this.graphModelChanged() 62 | } 63 | 64 | nodeClicked (d) { 65 | d.fixed = true 66 | if (!d.selected) { 67 | this.selectItem(d) 68 | this.onItemSelected({type: 'node', item: {id: d.id, labels: d.labels, properties: d.propertyList}}) 69 | } else { 70 | this.deselectItem() 71 | } 72 | } 73 | 74 | nodeUnlock (d) { 75 | d.fixed = false 76 | this.deselectItem() 77 | } 78 | 79 | nodeDblClicked (d) { 80 | const graph = this.graph 81 | const graphView = this.graphView 82 | const graphModelChanged = this.graphModelChanged.bind(this) 83 | this.getNodeNeighbours(d, this.graph.findNodeNeighbourIds(d.id), function ({nodes, relationships}) { 84 | graph.addNodes(mapNodes(nodes)) 85 | graph.addRelationships(mapRelationships(relationships, graph)) 86 | graphView.update() 87 | graphModelChanged() 88 | }) 89 | } 90 | 91 | onNodeMouseOver (node) { 92 | if (!node.contextMenu) { 93 | this.onItemMouseOver({type: 'node', item: {id: node.id, labels: node.labels, properties: node.propertyList}}) 94 | } 95 | } 96 | onMenuMouseOver (itemWithMenu) { 97 | this.onItemMouseOver({type: 'context-menu-item', item: {label: itemWithMenu.contextMenu.label, content: itemWithMenu.contextMenu.menuContent, selection: itemWithMenu.contextMenu.menuSelection}}) 98 | } 99 | onRelationshipMouseOver (relationship) { 100 | this.onItemMouseOver({type: 'relationship', item: {id: relationship.id, type: relationship.type, properties: relationship.propertyList}}) 101 | } 102 | 103 | onRelationshipClicked (relationship) { 104 | if (!relationship.selected) { 105 | this.selectItem(relationship) 106 | this.onItemSelected({type: 'relationship', item: {id: relationship.id, type: relationship.type, properties: relationship.propertyList}}) 107 | } else { 108 | this.deselectItem() 109 | } 110 | } 111 | 112 | onCanvasClicked () { 113 | this.deselectItem() 114 | } 115 | 116 | onItemMouseOut (item) { 117 | this.onItemMouseOver({type: 'canvas', item: {nodeCount: this.graph.nodes().length, relationshipCount: this.graph.relationships().length}}) 118 | } 119 | 120 | bindEventHandlers () { 121 | this.graphView 122 | .on('nodeMouseOver', this.onNodeMouseOver.bind(this)) 123 | .on('nodeMouseOut', this.onItemMouseOut.bind(this)) 124 | .on('menuMouseOver', this.onMenuMouseOver.bind(this)) 125 | .on('menuMouseOut', this.onItemMouseOut.bind(this)) 126 | .on('relMouseOver', this.onRelationshipMouseOver.bind(this)) 127 | .on('relMouseOut', this.onItemMouseOut.bind(this)) 128 | .on('relationshipClicked', this.onRelationshipClicked.bind(this)) 129 | .on('canvasClicked', this.onCanvasClicked.bind(this)) 130 | .on('nodeClose', this.nodeClose.bind(this)) 131 | .on('nodeClicked', this.nodeClicked.bind(this)) 132 | .on('nodeDblClicked', this.nodeDblClicked.bind(this)) 133 | .on('nodeUnlock', this.nodeUnlock.bind(this)) 134 | this.onItemMouseOut() 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/renders/init.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2015 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | do -> 24 | noop = -> 25 | 26 | nodeRingStrokeSize = 8 27 | 28 | nodeOutline = new neo.Renderer( 29 | onGraphChange: (selection, viz) -> 30 | circles = selection.selectAll('circle.outline').data((node) -> [node]) 31 | 32 | circles.enter() 33 | .append('circle') 34 | .classed('outline', true) 35 | .attr 36 | cx: 0 37 | cy: 0 38 | 39 | circles 40 | .attr 41 | r: (node) -> node.radius 42 | fill: (node) -> viz.style.forNode(node).get('color') 43 | stroke: (node) -> viz.style.forNode(node).get('border-color') 44 | 'stroke-width': (node) -> viz.style.forNode(node).get('border-width') 45 | 46 | circles.exit().remove() 47 | onTick: noop 48 | ) 49 | 50 | nodeCaption = new neo.Renderer( 51 | onGraphChange: (selection, viz) -> 52 | text = selection.selectAll('text.caption').data((node) -> node.caption) 53 | 54 | text.enter().append('text') 55 | # .classed('caption', true) 56 | .attr('text-anchor': 'middle') 57 | .attr('pointer-events': 'none') 58 | 59 | text 60 | .text((line) -> line.text) 61 | .attr('y', (line) -> line.baseline ) 62 | .attr('font-size', (line) -> viz.style.forNode(line.node).get('font-size')) 63 | .attr('fill': (line) -> viz.style.forNode(line.node).get('text-color-internal')) 64 | 65 | text.exit().remove() 66 | 67 | onTick: noop 68 | ) 69 | 70 | nodeIcon = new neo.Renderer( 71 | onGraphChange: (selection, viz) -> 72 | text = selection.selectAll('text').data((node) -> node.caption) 73 | 74 | text.enter().append('text') 75 | .attr('text-anchor': 'middle') 76 | .attr('pointer-events': 'none') 77 | .attr('font-family': 'streamline') 78 | 79 | text 80 | .text((line) -> viz.style.forNode(line.node).get('icon-code')) 81 | .attr('dy', (line) -> line.node.radius/16) 82 | .attr('font-size', (line) -> line.node.radius) 83 | .attr('fill': (line) -> viz.style.forNode(line.node).get('text-color-internal')) 84 | 85 | text.exit().remove() 86 | 87 | onTick: noop 88 | ) 89 | 90 | nodeRing = new neo.Renderer( 91 | onGraphChange: (selection) -> 92 | circles = selection.selectAll('circle.ring').data((node) -> [node]) 93 | circles.enter() 94 | .insert('circle', '.outline') 95 | .classed('ring', true) 96 | .attr 97 | cx: 0 98 | cy: 0 99 | 'stroke-width': nodeRingStrokeSize + 'px' 100 | 101 | circles 102 | .attr 103 | r: (node) -> node.radius + 4 104 | 105 | circles.exit().remove() 106 | 107 | onTick: noop 108 | ) 109 | 110 | arrowPath = new neo.Renderer( 111 | name: 'arrowPath' 112 | onGraphChange: (selection, viz) -> 113 | paths = selection.selectAll('path.outline').data((rel) -> [rel]) 114 | 115 | paths.enter() 116 | .append('path') 117 | .classed('outline', true) 118 | 119 | paths 120 | .attr('fill', (rel) -> viz.style.forRelationship(rel).get('color')) 121 | .attr('stroke', 'none') 122 | 123 | paths.exit().remove() 124 | 125 | onTick: (selection) -> 126 | selection.selectAll('path') 127 | .attr('d', (d) -> d.arrow.outline(d.shortCaptionLength)) 128 | ) 129 | 130 | relationshipType = new neo.Renderer( 131 | name: 'relationshipType' 132 | onGraphChange: (selection, viz) -> 133 | texts = selection.selectAll("text").data((rel) -> [rel]) 134 | 135 | texts.enter().append("text") 136 | .attr("text-anchor": "middle") 137 | .attr('pointer-events': 'none') 138 | 139 | texts 140 | .attr('font-size', (rel) -> viz.style.forRelationship(rel).get('font-size')) 141 | .attr('fill', (rel) -> viz.style.forRelationship(rel).get('text-color-' + rel.captionLayout)) 142 | 143 | texts.exit().remove() 144 | 145 | onTick: (selection, viz) -> 146 | selection.selectAll('text') 147 | .attr('x', (rel) -> rel.arrow.midShaftPoint.x) 148 | .attr('y', (rel) -> rel.arrow.midShaftPoint.y + parseFloat(viz.style.forRelationship(rel).get('font-size')) / 2 - 1) 149 | .attr('transform', (rel) -> 150 | if rel.naturalAngle < 90 or rel.naturalAngle > 270 151 | "rotate(180 #{ rel.arrow.midShaftPoint.x } #{ rel.arrow.midShaftPoint.y })" 152 | else 153 | null) 154 | .text((rel) -> rel.shortCaption) 155 | ) 156 | 157 | relationshipOverlay = new neo.Renderer( 158 | name: 'relationshipOverlay' 159 | onGraphChange: (selection) -> 160 | rects = selection.selectAll('path.overlay').data((rel) -> [rel]) 161 | 162 | rects.enter() 163 | .append('path') 164 | .classed('overlay', true) 165 | 166 | rects.exit().remove() 167 | 168 | onTick: (selection) -> 169 | band = 16 170 | 171 | selection.selectAll('path.overlay') 172 | .attr('d', (d) -> d.arrow.overlay(band)) 173 | ) 174 | 175 | neo.renderers.node.push(nodeOutline) 176 | neo.renderers.node.push(nodeIcon) 177 | neo.renderers.node.push(nodeCaption) 178 | neo.renderers.node.push(nodeRing) 179 | neo.renderers.relationship.push(arrowPath) 180 | neo.renderers.relationship.push(relationshipType) 181 | neo.renderers.relationship.push(relationshipOverlay) 182 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/circumferentialRelationshipRouting.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.circumferentialRelationshipRouting 24 | constructor: (@style) -> 25 | 26 | measureRelationshipCaption: (relationship, caption) -> 27 | fontFamily = 'sans-serif' 28 | fontSize = parseFloat(@style.forRelationship(relationship).get('font-size')) 29 | padding = parseFloat(@style.forRelationship(relationship).get('padding')) 30 | neo.utils.measureText(caption, fontFamily, fontSize) + padding * 2 31 | 32 | captionFitsInsideArrowShaftWidth: (relationship) -> 33 | parseFloat(@style.forRelationship(relationship).get('shaft-width')) > 34 | parseFloat(@style.forRelationship(relationship).get('font-size')) 35 | 36 | measureRelationshipCaptions: (relationships) -> 37 | for relationship in relationships 38 | relationship.captionLength = @measureRelationshipCaption(relationship, relationship.type) 39 | relationship.captionLayout = 40 | if @captionFitsInsideArrowShaftWidth(relationship) 41 | "internal" 42 | else 43 | "external" 44 | 45 | shortenCaption: (relationship, caption, targetWidth) -> 46 | shortCaption = caption 47 | while true 48 | if shortCaption.length <= 2 49 | return ['', 0] 50 | shortCaption = shortCaption.substr(0, shortCaption.length - 2) + '\u2026' 51 | width = @measureRelationshipCaption(relationship, shortCaption) 52 | if width < targetWidth 53 | return [shortCaption, width] 54 | layoutRelationships: (graph) -> 55 | for relationship in graph.relationships() 56 | dx = relationship.target.x - relationship.source.x 57 | dy = relationship.target.y - relationship.source.y 58 | relationship.naturalAngle = ((Math.atan2(dy, dx) / Math.PI * 180) + 180) % 360 59 | delete relationship.arrow 60 | 61 | sortedNodes = graph.nodes().sort((a, b) -> 62 | b.relationshipCount(graph) - a.relationshipCount(graph)) 63 | 64 | for node in sortedNodes 65 | relationships = [] 66 | relationships.push(relationship) for relationship in graph.relationships() when relationship.source is node or relationship.target is node 67 | 68 | arrowAngles = { floating: {}, fixed: {} } 69 | relationshipMap = {} 70 | for relationship in relationships 71 | relationshipMap[relationship.id] = relationship 72 | 73 | if node == relationship.source 74 | if relationship.hasOwnProperty('arrow') 75 | arrowAngles.fixed[relationship.id] = relationship.naturalAngle + relationship.arrow.deflection 76 | else 77 | arrowAngles.floating[relationship.id] = relationship.naturalAngle 78 | if node == relationship.target 79 | if relationship.hasOwnProperty('arrow') 80 | arrowAngles.fixed[relationship.id] = (relationship.naturalAngle - relationship.arrow.deflection + 180) % 360 81 | else 82 | arrowAngles.floating[relationship.id] = (relationship.naturalAngle + 180) % 360 83 | 84 | distributedAngles = {} 85 | for id, angle of arrowAngles.floating 86 | distributedAngles[id] = angle 87 | for id, angle of arrowAngles.fixed 88 | distributedAngles[id] = angle 89 | 90 | if (relationships.length > 1) 91 | distributedAngles = neo.utils.distributeCircular(arrowAngles, 30) 92 | 93 | for id, angle of distributedAngles 94 | relationship = relationshipMap[id] 95 | if not relationship.hasOwnProperty('arrow') 96 | deflection = if node == relationship.source 97 | angle - relationship.naturalAngle 98 | else 99 | (relationship.naturalAngle - angle + 180) % 360 100 | 101 | shaftRadius = (parseFloat(@style.forRelationship(relationship).get('shaft-width')) / 2) or 2 102 | headRadius = shaftRadius + 3 103 | headHeight = headRadius * 2 104 | 105 | dx = relationship.target.x - relationship.source.x 106 | dy = relationship.target.y - relationship.source.y 107 | 108 | square = (distance) -> distance * distance 109 | centreDistance = Math.sqrt(square(dx) + square(dy)) 110 | 111 | if Math.abs(deflection) < Math.PI / 180 112 | relationship.arrow = new neo.utils.straightArrow( 113 | relationship.source.radius, 114 | relationship.target.radius, 115 | centreDistance, 116 | shaftRadius, 117 | headRadius, 118 | headHeight, 119 | relationship.captionLayout 120 | ) 121 | else 122 | relationship.arrow = new neo.utils.arcArrow( 123 | relationship.source.radius, 124 | relationship.target.radius, 125 | centreDistance, 126 | deflection, 127 | shaftRadius * 2, 128 | headRadius * 2, 129 | headHeight, 130 | relationship.captionLayout 131 | ) 132 | 133 | [relationship.shortCaption, relationship.shortCaptionLength] = if relationship.arrow.shaftLength > relationship.captionLength 134 | [relationship.caption, relationship.captionLength] 135 | else 136 | @shortenCaption(relationship, relationship.caption, relationship.arrow.shaftLength) 137 | -------------------------------------------------------------------------------- /src/views/page/AuthorKeywordSearch.vue: -------------------------------------------------------------------------------- 1 | 37 | 181 | 182 | -------------------------------------------------------------------------------- /src/components/D3Visualization/components/Legend.vue: -------------------------------------------------------------------------------- 1 | 161 | -------------------------------------------------------------------------------- /src/views/page/CsvUpload.vue: -------------------------------------------------------------------------------- 1 | 40 | 200 | 201 | -------------------------------------------------------------------------------- /src/views/page/AuthorCoopratorSearch.vue: -------------------------------------------------------------------------------- 1 | 37 | 179 | 180 | -------------------------------------------------------------------------------- /src/views/page/AuthorArticleSearch.vue: -------------------------------------------------------------------------------- 1 | 43 | 184 | 185 | -------------------------------------------------------------------------------- /src/views/page/CoopratorArticleSearch.vue: -------------------------------------------------------------------------------- 1 | 43 | 185 | 186 | -------------------------------------------------------------------------------- /src/components/D3Visualization/components/Graph.vue: -------------------------------------------------------------------------------- 1 | 20 | 211 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/bolt/boltMappings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | import updateStatsFields from './updateStatisticsFields' 22 | 23 | export function toObjects (records, intChecker, intConverter) { 24 | const recordValues = records.map((record) => { 25 | let out = [] 26 | record.forEach((val, key) => out.push(itemIntToString(val, intChecker, intConverter))) 27 | return out 28 | }) 29 | return recordValues 30 | } 31 | 32 | export function recordsToTableArray (records, intChecker, intConverter) { 33 | const recordValues = toObjects(records, intChecker, intConverter) 34 | const keys = records[0].keys 35 | return [[...keys], ...recordValues] 36 | } 37 | 38 | export function itemIntToString (item, intChecker, intConverter) { 39 | if (intChecker(item)) return intConverter(item) 40 | if (Array.isArray(item)) return arrayIntToString(item, intChecker, intConverter) 41 | if (['number', 'string', 'boolean'].indexOf(typeof item) !== -1) return item 42 | if (item === null) return item 43 | if (typeof item === 'object') return objIntToString(item, intChecker, intConverter) 44 | } 45 | 46 | export function arrayIntToString (arr, intChecker, intConverter) { 47 | return arr.map((item) => itemIntToString(item, intChecker, intConverter)) 48 | } 49 | 50 | export function objIntToString (obj, intChecker, intConverter) { 51 | let newObj = {} 52 | Object.keys(obj).forEach((key) => { 53 | newObj[key] = itemIntToString(obj[key], intChecker, intConverter) 54 | }) 55 | return newObj 56 | } 57 | 58 | export function extractPlan (result) { 59 | if (result.summary && (result.summary.plan || result.summary.profile)) { 60 | const rawPlan = result.summary.profile || result.summary.plan 61 | const boltPlanToRESTPlanShared = (plan) => { 62 | return { 63 | operatorType: plan.operatorType, 64 | LegacyExpression: plan.arguments.LegacyExpression, 65 | ExpandExpression: plan.arguments.ExpandExpression, 66 | DbHits: plan.dbHits, 67 | Rows: plan.rows, 68 | EstimatedRows: plan.arguments.EstimatedRows, 69 | identifiers: plan.identifiers, 70 | Index: plan.arguments.Index, 71 | children: plan.children.map(boltPlanToRESTPlanShared) 72 | } 73 | } 74 | let obj = boltPlanToRESTPlanShared(rawPlan) 75 | obj['runtime-impl'] = rawPlan.arguments['runtime-impl'] 76 | obj['planner-impl'] = rawPlan.arguments['planner-impl'] 77 | obj['version'] = rawPlan.arguments['version'] 78 | obj['KeyNames'] = rawPlan.arguments['KeyNames'] 79 | obj['planner'] = rawPlan.arguments['planner'] 80 | obj['runtime'] = rawPlan.arguments['runtime'] 81 | return {root: obj} 82 | } 83 | return null 84 | } 85 | 86 | export function extractNodesAndRelationshipsFromRecords (records, types) { 87 | if (records.length === 0) { 88 | return { nodes: [], relationships: [] } 89 | } 90 | 91 | let keys = records[0].keys 92 | let rawNodes = [] 93 | let rawRels = [] 94 | records.forEach((record) => { 95 | let graphItems = keys.map((key) => record.get(key)) 96 | rawNodes = [...rawNodes, ...graphItems.filter((item) => item instanceof types.Node)] 97 | rawRels = [...rawRels, ...graphItems.filter((item) => item instanceof types.Relationship)] 98 | let paths = graphItems.filter((item) => item instanceof types.Path) 99 | paths.forEach((item) => extractNodesAndRelationshipsFromPath(item, rawNodes, rawRels, types)) 100 | }) 101 | return { nodes: rawNodes, relationships: rawRels } 102 | } 103 | 104 | const resultContainsGraphKeys = (keys) => { 105 | return (keys.includes('nodes') && keys.includes('relationships')) 106 | } 107 | 108 | export function extractNodesAndRelationshipsFromRecordsForOldVis (records, types, filterRels, intChecker, intConverter) { 109 | if (records.length === 0) { 110 | return { nodes: [], relationships: [] } 111 | } 112 | let keys = records[0].keys 113 | let rawNodes = [] 114 | let rawRels = [] 115 | if (resultContainsGraphKeys(keys)) { 116 | rawNodes = [...rawNodes, ...records[0].get(keys[0])] 117 | rawRels = [...rawRels, ...records[0].get(keys[1])] 118 | } else { 119 | records.forEach((record) => { 120 | let graphItems = keys.map((key) => record.get(key)) 121 | rawNodes = [...rawNodes, ...graphItems.filter((item) => item instanceof types.Node)] 122 | rawRels = [...rawRels, ...graphItems.filter((item) => item instanceof types.Relationship)] 123 | let paths = graphItems.filter((item) => item instanceof types.Path) 124 | paths.forEach((item) => extractNodesAndRelationshipsFromPath(item, rawNodes, rawRels, types)) 125 | }) 126 | } 127 | const nodes = rawNodes.map((item) => { 128 | return {id: item.identity.toString(), labels: item.labels, properties: itemIntToString(item.properties, intChecker, intConverter)} 129 | }) 130 | let relationships = rawRels 131 | if (filterRels) { 132 | relationships = rawRels.filter((item) => nodes.filter((node) => node.id === item.start.toString()).length > 0 && nodes.filter((node) => node.id === item.end.toString()).length > 0) 133 | } 134 | relationships = relationships.map((item) => { 135 | return {id: item.identity.toString(), startNodeId: item.start.toString(), endNodeId: item.end.toString(), type: item.type, properties: itemIntToString(item.properties, intChecker, intConverter)} 136 | }) 137 | return { nodes: nodes, relationships: relationships } 138 | } 139 | 140 | const extractNodesAndRelationshipsFromPath = (item, rawNodes, rawRels) => { 141 | let paths = Array.isArray(item) ? item : [item] 142 | paths.forEach((path) => { 143 | path.segments.forEach((segment) => { 144 | rawNodes.push(segment.start) 145 | rawNodes.push(segment.end) 146 | rawRels.push(segment.relationship) 147 | }) 148 | }) 149 | } 150 | 151 | export const retrieveFormattedUpdateStatistics = (result) => { 152 | if (result.summary.counters) { 153 | const stats = result.summary.counters._stats 154 | const statsMessages = updateStatsFields.filter(field => stats[field.field] > 0).map(field => `${field.verb} ${stats[field.field]} ${stats[field.field] === 1 ? field.singular : field.plural}`) 155 | return statsMessages.join(', ') 156 | } else return null 157 | } 158 | 159 | export const flattenProperties = (rows) => { 160 | return rows.map((row) => row.map((entry) => (entry && entry.properties) ? entry.properties : entry)) 161 | } 162 | -------------------------------------------------------------------------------- /src/components/D3Visualization/services/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2017 "Neo Technology," 3 | * Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | * 5 | * This file is part of Neo4j. 6 | * 7 | * Neo4j is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | export const deepEquals = (x, y) => { 22 | if (x && y && typeof x === 'object' && typeof y === 'object') { 23 | if (Object.keys(x).length !== Object.keys(y).length) return false 24 | return Object.keys(x).every((key) => deepEquals(x[key], y[key])) 25 | } 26 | return (x === y) 27 | } 28 | 29 | export const flatten = arr => arr.reduce((a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), []) 30 | 31 | export const moveInArray = (fromIndex, toIndex, arr) => { 32 | if (!Array.isArray(arr)) return false 33 | if (fromIndex < 0 || fromIndex >= arr.length) return false 34 | if (toIndex < 0 || toIndex >= arr.length) return false 35 | const newArr = [].concat(arr) 36 | const el = arr[fromIndex] 37 | newArr.splice(fromIndex, 1) 38 | newArr.splice(toIndex, 0, el) 39 | return newArr 40 | } 41 | 42 | export const debounce = (fn, time, context = null) => { 43 | let pending 44 | return (...args) => { 45 | if (pending) clearTimeout(pending) 46 | pending = setTimeout(() => typeof fn === 'function' && fn.apply(context, args), parseInt(time)) 47 | } 48 | } 49 | 50 | export const throttle = (fn, time, context = null) => { 51 | let blocking 52 | return (...args) => { 53 | if (blocking) return 54 | blocking = true 55 | typeof fn === 'function' && fn.apply(context, args) 56 | setTimeout(() => (blocking = false), parseInt(time)) 57 | } 58 | } 59 | 60 | export const isRoutingHost = (host) => { 61 | return /^bolt\+routing:\/\//.test(host) 62 | } 63 | 64 | export const toBoltHost = (host) => { 65 | return 'bolt://' + (host || '') // prepend with bolt:// 66 | .split('bolt://').join('') // remove bolt:// 67 | .split('bolt+routing://').join('') // remove bolt+routing:// 68 | } 69 | 70 | export const hostIsAllowed = (uri, whitelist = null) => { 71 | if (whitelist === '*') return true 72 | const urlInfo = getUrlInfo(uri) 73 | const hostname = urlInfo.hostname 74 | const hostnamePlusProtocol = urlInfo.protocol + '//' + hostname 75 | 76 | let whitelistedHosts = ['guides.neo4j.com', 'localhost'] 77 | if (whitelist && whitelist !== '') { 78 | whitelistedHosts = whitelist.split(',') 79 | } 80 | return whitelistedHosts.indexOf(hostname) > -1 || 81 | whitelistedHosts.indexOf(hostnamePlusProtocol) > -1 82 | } 83 | 84 | export const getUrlInfo = (url) => { 85 | const reURLInformation = new RegExp([ 86 | '^(?:(https?:)//)?', // protocol 87 | '(([^:/?#]*)(?::([0-9]+))?)', // host (hostname and port) 88 | '(/{0,1}[^?#]*)', // pathname 89 | '(\\?[^#]*|)', // search 90 | '(#.*|)$' // hash 91 | ].join('')) 92 | const match = url.match(reURLInformation) 93 | return match && { 94 | protocol: match[1], 95 | host: match[2], 96 | hostname: match[3], 97 | port: match[4], 98 | pathname: match[5], 99 | search: match[6], 100 | hash: match[7] 101 | } 102 | } 103 | 104 | export const getUrlParamValue = (name, url) => { 105 | if (!url) return false 106 | let out = [] 107 | const re = new RegExp('[\\?&]' + name + '=([^&#]*)', 'g') 108 | let results 109 | while ((results = re.exec(url)) !== null) { 110 | if (results && results[1]) out.push(results[1]) 111 | } 112 | if (!out.length) return undefined 113 | return out 114 | } 115 | 116 | export const toHumanReadableBytes = (input) => { 117 | let number = +input 118 | if (!isFinite(number)) { return '-' } 119 | 120 | if (number < 1024) { 121 | return `${number} B` 122 | } 123 | 124 | number /= 1024 125 | let units = ['KiB', 'MiB', 'GiB', 'TiB'] 126 | 127 | for (let unit of Array.from(units)) { 128 | if (number < 1024) { return `${number.toFixed(2)} ${unit}` } 129 | number /= 1024 130 | } 131 | 132 | return `${number.toFixed(2)} PiB` 133 | } 134 | 135 | export const getBrowserName = function () { 136 | if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) { 137 | return 'Opera' 138 | } 139 | if (typeof InstallTrigger !== 'undefined') { 140 | return 'Firefox' 141 | } 142 | if (navigator.userAgent.match(/Version\/[\d.]+.*Safari/)) { 143 | return 'Safari' 144 | } 145 | if (window.chrome) { 146 | return 'Chrome' 147 | } 148 | if (document.documentMode) { 149 | return 'Internet Explorer' 150 | } 151 | if (window.StyleMedia) { 152 | return 'Edge' 153 | } 154 | return 'Unknown' 155 | } 156 | 157 | export const removeComments = (string) => { 158 | return string.split(/\r?\n/).filter((line) => !line.startsWith('//')).join('\r\n') 159 | } 160 | 161 | export const canUseDOM = () => !!( 162 | (typeof window !== 'undefined' && 163 | window.document && window.document.createElement) 164 | ) 165 | 166 | export const stringifyMod = () => { 167 | const toString = Object.prototype.toString 168 | const isArray = Array.isArray || function (a) { return toString.call(a) === '[object Array]' } 169 | const escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t'} 170 | const escFunc = function (m) { return escMap[m] || '\\u' + (m.charCodeAt(0) + 0x10000).toString(16).substr(1) } 171 | const escRE = /[\\"\u0000-\u001F\u2028\u2029]/g 172 | return function stringify (value, modFn = null) { 173 | if (modFn) { 174 | const modVal = modFn && modFn(value) 175 | if (typeof modVal !== 'undefined') return modVal 176 | } 177 | if (value == null) return 'null' 178 | if (typeof value === 'number') return isFinite(value) ? value.toString() : 'null' 179 | if (typeof value === 'boolean') return value.toString() 180 | if (typeof value === 'object') { 181 | if (typeof value.toJSON === 'function') { 182 | return stringify(value.toJSON(), modFn) 183 | } else if (isArray(value)) { 184 | let res = '[' 185 | for (let i = 0; i < value.length; i++) { 186 | res += (i ? ',' : '') + stringify(value[i], modFn) 187 | } 188 | return res + ']' 189 | } else if (toString.call(value) === '[object Object]') { 190 | let tmp = [] 191 | for (const k in value) { 192 | if (value.hasOwnProperty(k)) tmp.push(stringify(k, modFn) + ':' + stringify(value[k], modFn)) 193 | } 194 | return '{' + tmp.join(',') + '}' 195 | } 196 | } 197 | return '"' + value.toString().replace(escRE, escFunc) + '"' 198 | } 199 | } 200 | 201 | // Epic helpers 202 | export const put = (dispatch) => (action) => dispatch(action) 203 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/pairwiseArcsRelationshipRouting.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.pairwiseArcsRelationshipRouting 24 | constructor: (@style) -> 25 | 26 | measureRelationshipCaption: (relationship, caption) -> 27 | fontFamily = 'sans-serif' 28 | padding = parseFloat(@style.forRelationship(relationship).get('padding')) 29 | neo.utils.measureText(caption, fontFamily, relationship.captionHeight) + padding * 2 30 | 31 | captionFitsInsideArrowShaftWidth: (relationship) -> 32 | parseFloat(@style.forRelationship(relationship).get('shaft-width')) > relationship.captionHeight 33 | 34 | measureRelationshipCaptions: (relationships) -> 35 | for relationship in relationships 36 | relationship.captionHeight = parseFloat(@style.forRelationship(relationship).get('font-size')) 37 | relationship.captionLength = @measureRelationshipCaption(relationship, relationship.caption) 38 | relationship.captionLayout = 39 | if @captionFitsInsideArrowShaftWidth(relationship) and not relationship.isLoop() 40 | "internal" 41 | else 42 | "external" 43 | 44 | shortenCaption: (relationship, caption, targetWidth) -> 45 | shortCaption = caption || 'caption' 46 | while true 47 | if shortCaption.length <= 2 48 | return ['', 0] 49 | shortCaption = shortCaption.substr(0, shortCaption.length - 2) + '\u2026' 50 | width = @measureRelationshipCaption(relationship, shortCaption) 51 | if width < targetWidth 52 | return [shortCaption, width] 53 | 54 | computeGeometryForNonLoopArrows: (nodePairs) -> 55 | square = (distance) -> distance * distance 56 | for nodePair in nodePairs 57 | if not nodePair.isLoop() 58 | dx = nodePair.nodeA.x - nodePair.nodeB.x 59 | dy = nodePair.nodeA.y - nodePair.nodeB.y 60 | angle = ((Math.atan2(dy, dx) / Math.PI * 180) + 360) % 360 61 | centreDistance = Math.sqrt(square(dx) + square(dy)) 62 | for relationship in nodePair.relationships 63 | relationship.naturalAngle = if relationship.target is nodePair.nodeA 64 | (angle + 180) % 360 65 | else 66 | angle 67 | relationship.centreDistance = centreDistance 68 | 69 | distributeAnglesForLoopArrows: (nodePairs, relationships) -> 70 | for nodePair in nodePairs 71 | if nodePair.isLoop() 72 | angles = [] 73 | node = nodePair.nodeA 74 | for relationship in relationships 75 | if not relationship.isLoop() 76 | if relationship.source is node 77 | angles.push relationship.naturalAngle 78 | if relationship.target is node 79 | angles.push relationship.naturalAngle + 180 80 | angles = angles.map((a) -> (a + 360) % 360).sort((a, b) -> a - b) 81 | if angles.length > 0 82 | biggestGap = 83 | start: 0 84 | end: 0 85 | for angle, i in angles 86 | start = angle 87 | end = if i == angles.length - 1 88 | angles[0] + 360 89 | else 90 | angles[i + 1] 91 | if end - start > biggestGap.end - biggestGap.start 92 | biggestGap.start = start 93 | biggestGap.end = end 94 | separation = (biggestGap.end - biggestGap.start) / (nodePair.relationships.length + 1) 95 | for relationship, i in nodePair.relationships 96 | relationship.naturalAngle = (biggestGap.start + (i + 1) * separation - 90) % 360 97 | else 98 | separation = 360 / nodePair.relationships.length 99 | for relationship, i in nodePair.relationships 100 | relationship.naturalAngle = i * separation 101 | 102 | layoutRelationships: (graph) -> 103 | nodePairs = graph.groupedRelationships() 104 | @computeGeometryForNonLoopArrows(nodePairs) 105 | @distributeAnglesForLoopArrows(nodePairs, graph.relationships()) 106 | 107 | for nodePair in nodePairs 108 | for relationship in nodePair.relationships 109 | delete relationship.arrow 110 | 111 | middleRelationshipIndex = (nodePair.relationships.length - 1) / 2 112 | defaultDeflectionStep = 30 113 | maximumTotalDeflection = 150 114 | numberOfSteps = nodePair.relationships.length - 1 115 | totalDeflection = defaultDeflectionStep * numberOfSteps 116 | 117 | deflectionStep = if totalDeflection > maximumTotalDeflection then maximumTotalDeflection/numberOfSteps else defaultDeflectionStep 118 | 119 | for relationship, i in nodePair.relationships 120 | 121 | shaftWidth = parseFloat(@style.forRelationship(relationship).get('shaft-width')) or 2 122 | headWidth = shaftWidth + 6 123 | headHeight = headWidth 124 | 125 | if nodePair.isLoop() 126 | relationship.arrow = new neo.utils.loopArrow( 127 | relationship.source.radius, 128 | 40, 129 | defaultDeflectionStep, 130 | shaftWidth, 131 | headWidth, 132 | headHeight, 133 | relationship.captionHeight 134 | ) 135 | else 136 | if i == middleRelationshipIndex 137 | relationship.arrow = new neo.utils.straightArrow( 138 | relationship.source.radius, 139 | relationship.target.radius, 140 | relationship.centreDistance, 141 | shaftWidth, 142 | headWidth, 143 | headHeight, 144 | relationship.captionLayout 145 | ) 146 | else 147 | deflection = deflectionStep * (i - middleRelationshipIndex) 148 | 149 | if nodePair.nodeA isnt relationship.source 150 | deflection *= -1 151 | 152 | relationship.arrow = new neo.utils.arcArrow( 153 | relationship.source.radius, 154 | relationship.target.radius, 155 | relationship.centreDistance, 156 | deflection, 157 | shaftWidth, 158 | headWidth, 159 | headHeight, 160 | relationship.captionLayout 161 | ) 162 | 163 | [relationship.shortCaption, relationship.shortCaptionLength] = if relationship.arrow.shaftLength > relationship.captionLength 164 | [relationship.caption, relationship.captionLength] 165 | else 166 | @shortenCaption(relationship, relationship.caption, relationship.arrow.shaftLength) 167 | -------------------------------------------------------------------------------- /src/components/D3Visualization/lib/visualization/utils/arcArrow.coffee: -------------------------------------------------------------------------------- 1 | ###! 2 | Copyright (c) 2002-2017 "Neo Technology," 3 | Network Engine for Objects in Lund AB [http://neotechnology.com] 4 | 5 | This file is part of Neo4j. 6 | 7 | Neo4j is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | ### 20 | 21 | 'use strict' 22 | 23 | class neo.utils.arcArrow 24 | 25 | constructor: (startRadius, endRadius, endCentre, @deflection, arrowWidth, headWidth, headLength, captionLayout) -> 26 | square = (l) -> 27 | l * l 28 | 29 | deflectionRadians = @deflection * Math.PI / 180 30 | startAttach = 31 | x: Math.cos( deflectionRadians ) * (startRadius), 32 | y: Math.sin( deflectionRadians ) * (startRadius) 33 | 34 | radiusRatio = startRadius / (endRadius + headLength) 35 | homotheticCenter = -endCentre * radiusRatio / (1 - radiusRatio) 36 | 37 | intersectWithOtherCircle = (fixedPoint, radius, xCenter, polarity) -> 38 | gradient = fixedPoint.y / (fixedPoint.x - homotheticCenter) 39 | hc = fixedPoint.y - gradient * fixedPoint.x 40 | 41 | A = 1 + square(gradient) 42 | B = 2 * (gradient * hc - xCenter) 43 | C = square(hc) + square(xCenter) - square(radius) 44 | 45 | intersection = { x: (-B + polarity * Math.sqrt(square(B) - 4 * A * C)) / (2 * A) } 46 | intersection.y = (intersection.x - homotheticCenter) * gradient 47 | 48 | intersection 49 | 50 | endAttach = intersectWithOtherCircle(startAttach, endRadius + headLength, endCentre, -1) 51 | 52 | g1 = -startAttach.x / startAttach.y 53 | c1 = startAttach.y + (square(startAttach.x) / startAttach.y) 54 | g2 = -(endAttach.x - endCentre) / endAttach.y 55 | c2 = endAttach.y + (endAttach.x - endCentre) * endAttach.x / endAttach.y 56 | 57 | cx = ( c1 - c2 ) / (g2 - g1) 58 | cy = g1 * cx + c1 59 | 60 | arcRadius = Math.sqrt(square(cx - startAttach.x) + square(cy - startAttach.y)) 61 | startAngle = Math.atan2(startAttach.x - cx, cy - startAttach.y) 62 | endAngle = Math.atan2(endAttach.x - cx, cy - endAttach.y) 63 | sweepAngle = endAngle - startAngle 64 | if @deflection > 0 65 | sweepAngle = 2 * Math.PI - sweepAngle 66 | 67 | @shaftLength = sweepAngle * arcRadius 68 | if startAngle > endAngle 69 | @shaftLength = 0 70 | 71 | midShaftAngle = (startAngle + endAngle) / 2 72 | if @deflection > 0 73 | midShaftAngle += Math.PI 74 | @midShaftPoint = 75 | x: cx + arcRadius * Math.sin(midShaftAngle) 76 | y: cy - arcRadius * Math.cos(midShaftAngle) 77 | 78 | startTangent = (dr) -> 79 | dx = (if dr < 0 then 1 else -1) * Math.sqrt(square(dr) / (1 + square(g1))) 80 | dy = g1 * dx 81 | { 82 | x: startAttach.x + dx, 83 | y: startAttach.y + dy 84 | } 85 | 86 | endTangent = (dr) -> 87 | dx = (if dr < 0 then -1 else 1) * Math.sqrt(square(dr) / (1 + square(g2))) 88 | dy = g2 * dx 89 | { 90 | x: endAttach.x + dx, 91 | y: endAttach.y + dy 92 | } 93 | 94 | angleTangent = (angle, dr) -> 95 | { 96 | x: cx + (arcRadius + dr) * Math.sin(angle), 97 | y: cy - (arcRadius + dr) * Math.cos(angle) 98 | } 99 | 100 | endNormal = (dc) -> 101 | dx = (if dc < 0 then -1 else 1) * Math.sqrt(square(dc) / (1 + square(1 / g2))) 102 | dy = dx / g2 103 | { 104 | x: endAttach.x + dx 105 | y: endAttach.y - dy 106 | } 107 | 108 | endOverlayCorner = (dr, dc) -> 109 | shoulder = endTangent(dr) 110 | arrowTip = endNormal(dc) 111 | { 112 | x: shoulder.x + arrowTip.x - endAttach.x, 113 | y: shoulder.y + arrowTip.y - endAttach.y 114 | } 115 | 116 | coord = (point) -> 117 | "#{point.x},#{point.y}" 118 | 119 | shaftRadius = arrowWidth / 2 120 | headRadius = headWidth / 2 121 | positiveSweep = if startAttach.y > 0 then 0 else 1 122 | negativeSweep = if startAttach.y < 0 then 0 else 1 123 | 124 | @outline = (shortCaptionLength) -> 125 | if startAngle > endAngle 126 | return [ 127 | 'M', coord(endTangent(-headRadius)), 128 | 'L', coord(endNormal(headLength)), 129 | 'L', coord(endTangent(headRadius)), 130 | 'Z' 131 | ].join(' ') 132 | 133 | if captionLayout is 'external' 134 | captionSweep = shortCaptionLength / arcRadius 135 | if @deflection > 0 136 | captionSweep *= -1 137 | 138 | startBreak = midShaftAngle - captionSweep / 2 139 | endBreak = midShaftAngle + captionSweep / 2 140 | 141 | [ 142 | 'M', coord(startTangent(shaftRadius)), 143 | 'L', coord(startTangent(-shaftRadius)), 144 | 'A', arcRadius - shaftRadius, arcRadius - shaftRadius, 0, 0, positiveSweep, coord(angleTangent(startBreak, -shaftRadius)), 145 | 'L', coord(angleTangent(startBreak, shaftRadius)), 146 | 'A', arcRadius + shaftRadius, arcRadius + shaftRadius, 0, 0, negativeSweep, coord(startTangent(shaftRadius)) 147 | 'Z', 148 | 'M', coord(angleTangent(endBreak, shaftRadius)), 149 | 'L', coord(angleTangent(endBreak, -shaftRadius)), 150 | 'A', arcRadius - shaftRadius, arcRadius - shaftRadius, 0, 0, positiveSweep, coord(endTangent(-shaftRadius)), 151 | 'L', coord(endTangent(-headRadius)), 152 | 'L', coord(endNormal(headLength)), 153 | 'L', coord(endTangent(headRadius)), 154 | 'L', coord(endTangent(shaftRadius)), 155 | 'A', arcRadius + shaftRadius, arcRadius + shaftRadius, 0, 0, negativeSweep, coord(angleTangent(endBreak, shaftRadius)) 156 | ].join(' ') 157 | else 158 | [ 159 | 'M', coord(startTangent(shaftRadius)), 160 | 'L', coord(startTangent(-shaftRadius)), 161 | 'A', arcRadius - shaftRadius, arcRadius - shaftRadius, 0, 0, positiveSweep, coord(endTangent(-shaftRadius)), 162 | 'L', coord(endTangent(-headRadius)), 163 | 'L', coord(endNormal(headLength)), 164 | 'L', coord(endTangent(headRadius)), 165 | 'L', coord(endTangent(shaftRadius)), 166 | 'A', arcRadius + shaftRadius, arcRadius + shaftRadius, 0, 0, negativeSweep, coord(startTangent(shaftRadius)) 167 | ].join(' ') 168 | 169 | @overlay = (minWidth) -> 170 | radius = Math.max(minWidth / 2, shaftRadius) 171 | 172 | [ 173 | 'M', coord(startTangent(radius)), 174 | 'L', coord(startTangent(-radius)), 175 | 'A', arcRadius - radius, arcRadius - radius, 0, 0, positiveSweep, coord(endTangent(-radius)), 176 | 'L', coord(endOverlayCorner(-radius, headLength)), 177 | 'L', coord(endOverlayCorner(radius, headLength)), 178 | 'L', coord(endTangent(radius)), 179 | 'A', arcRadius + radius, arcRadius + radius, 0, 0, negativeSweep, coord(startTangent(radius)) 180 | ].join(' ') 181 | -------------------------------------------------------------------------------- /src/views/page/ArticleAuthorSearch.vue: -------------------------------------------------------------------------------- 1 | 58 | 207 | 208 | -------------------------------------------------------------------------------- /src/components/D3Visualization/components/Inspector.vue: -------------------------------------------------------------------------------- 1 | 169 | -------------------------------------------------------------------------------- /src/components/D3Visualization/Visualization.vue: -------------------------------------------------------------------------------- 1 | 25 | 226 | 229 | -------------------------------------------------------------------------------- /src/views/page/AuthorSimilarSearch.vue: -------------------------------------------------------------------------------- 1 | 59 | 249 | 250 | --------------------------------------------------------------------------------