├── .gitignore ├── bower.json ├── index.html ├── readme.adoc ├── scripts ├── codemirror-cypher.js ├── codemirror.js ├── cy2neod3.js ├── cypher.datatable.js ├── data.js ├── jquery.dataTables.min.js ├── neo4d3.js ├── neod3-visualization.js ├── neod3.js ├── sweet-alert.min.js └── vendor.js └── styles ├── codemirror-neo.css ├── codemirror.css ├── cy2neo.css ├── datatable.css ├── fonts ├── FontAwesome.otf ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── gh-fork-ribbon.css ├── images └── maze-black.png ├── neod3.css ├── sweet-alert.css └── vendor.css /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | issues.txt 3 | node_modules 4 | .DS_Store 5 | *.map 6 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cy2neo", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "Michael Hunger " 6 | ], 7 | "description": "Cy2Neo Tiny Neo4j Cypher Workbench with Alchemy.js Viz", 8 | "main": "cy2neo.js", 9 | "keywords": [ 10 | "neo4j", 11 | "cypher", 12 | "graph", 13 | "database", 14 | "repl", 15 | "shell", 16 | "console", 17 | "graphviz", 18 | "d3", 19 | "graph" 20 | ], 21 | "license": "MIT", 22 | "homepage": "http://jexp.github.io/cy2neo", 23 | "private": true, 24 | "ignore": [ 25 | "**/.*", 26 | "node_modules", 27 | "bower_components", 28 | "test", 29 | "tests" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Cy2NeoD3 - Tiny Neo4j Cypher Workbench 12 | 13 | 14 |
15 |
16 | 17 |
18 | 23 | 24 |
25 | 26 |
27 | 28 | 29 | 33 | 34 | 35 |
36 |
37 |
 
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 | Fork me on GitHub 47 |
48 |
49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 73 | 74 | -------------------------------------------------------------------------------- /readme.adoc: -------------------------------------------------------------------------------- 1 | == Cy2Neo - Tiny Neo4j Cypher Workbench with custom D3 Visualization 2 | 3 | Cy2Neo is a tiny *Neo4j query console*, in a single HTML-page using client-side JavaScript only. 4 | 5 | http://neo4j.com/developer[Neo4j] is an open source graph database and http://neo4j.com/developer/cypher[Cypher] is Neo4j's graph query language. 6 | 7 | Cypher queries are highlighted with http://codemirror.net/[CodeMirror]. 8 | 9 | Graph query results are rendered with https://d3js.org/[D3] using a custom renderer based on neo4j's browser rendering that is https://github.com/jexp/cy2neo/blob/neod3/scripts/neod3.js[part of this project]. 10 | 11 | Cy2Neo uses a simple Neo4j HTTP-connector that posts Cypher queries to Neo4j's transactional http://neo4j.com/docs/developer-manual/current/#rest-api-transactional[Cypher HTTP endpoint] using jQuery Ajax requests. 12 | 13 | You can http://jexp.github.io/cy2neo[try it live here], it should be able to connect to any Neo4j 2.x and 3.x instance that's accessible from your machine. 14 | Just enter the Neo4j-URL, username and password in the boxes on the right side. 15 | 16 | [NOTE] 17 | I wrote most of it while flying from OSCON 2014 in Portland,OR to Chicago on my way home to Dresden, Germany. 18 | -------------------------------------------------------------------------------- /scripts/codemirror-cypher.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.6.3 2 | /*! 3 | Copyright (c) 2002-2014 "Neo Technology," 4 | Network Engine for Objects in Lund AB [http://neotechnology.com] 5 | 6 | This file is part of Neo4j. 7 | 8 | Neo4j is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | 23 | (function() { 24 | var wordRegexp; 25 | 26 | wordRegexp = function(words) { 27 | return new RegExp("^(?:" + words.join("|") + ")$", "i"); 28 | }; 29 | 30 | CodeMirror.defineMode("cypher", function(config) { 31 | var curPunc, funcs, indentUnit, keywords, operatorChars, popContext, preds, pushContext, tokenBase, tokenLiteral; 32 | tokenBase = function(stream, state) { 33 | var ch, curPunc, type, word; 34 | ch = stream.next(); 35 | curPunc = null; 36 | if (ch === "\"" || ch === "'") { 37 | stream.match(/.+?["']/); 38 | return "string"; 39 | } 40 | if (/[{}\(\),\.;\[\]]/.test(ch)) { 41 | curPunc = ch; 42 | return "node"; 43 | } else if (ch === "/" && stream.eat("/")) { 44 | stream.skipToEnd(); 45 | return "comment"; 46 | } else if (operatorChars.test(ch)) { 47 | stream.eatWhile(operatorChars); 48 | return null; 49 | } else { 50 | stream.eatWhile(/[_\w\d]/); 51 | if (stream.eat(":")) { 52 | stream.eatWhile(/[\w\d_\-]/); 53 | return "atom"; 54 | } 55 | word = stream.current(); 56 | type = void 0; 57 | if (funcs.test(word)) { 58 | return "builtin"; 59 | } 60 | if (preds.test(word)) { 61 | return "def"; 62 | } else if (keywords.test(word)) { 63 | return "keyword"; 64 | } else { 65 | return "variable"; 66 | } 67 | } 68 | }; 69 | tokenLiteral = function(quote) { 70 | return function(stream, state) { 71 | var ch, escaped; 72 | escaped = false; 73 | ch = void 0; 74 | while ((ch = stream.next()) != null) { 75 | if (ch === quote && !escaped) { 76 | state.tokenize = tokenBase; 77 | break; 78 | } 79 | escaped = !escaped && ch === "\\"; 80 | } 81 | return "string"; 82 | }; 83 | }; 84 | pushContext = function(state, type, col) { 85 | return state.context = { 86 | prev: state.context, 87 | indent: state.indent, 88 | col: col, 89 | type: type 90 | }; 91 | }; 92 | popContext = function(state) { 93 | state.indent = state.context.indent; 94 | return state.context = state.context.prev; 95 | }; 96 | indentUnit = config.indentUnit; 97 | curPunc = void 0; 98 | funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "right", "round", "rtrim", "shortestPath", "sign", "sin", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "trim", "type", "upper"]); 99 | preds = wordRegexp(["all", "and", "any", "has", "in", "none", "not", "or", "single", "xor"]); 100 | keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "distinct", "drop", "else", "end", "false", "foreach", "from", "headers", "in", "index", "is", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "remove", "return", "scan", "set", "skip", "start", "then", "true", "union", "unique", "using", "when", "where", "with"]); 101 | operatorChars = /[*+\-<>=&|~%^]/; 102 | return { 103 | startState: function(base) { 104 | return { 105 | tokenize: tokenBase, 106 | context: null, 107 | indent: 0, 108 | col: 0 109 | }; 110 | }, 111 | token: function(stream, state) { 112 | var style; 113 | if (stream.sol()) { 114 | if (state.context && (state.context.align == null)) { 115 | state.context.align = false; 116 | } 117 | state.indent = stream.indentation(); 118 | } 119 | if (stream.eatSpace()) { 120 | return null; 121 | } 122 | style = state.tokenize(stream, state); 123 | if (style !== "comment" && state.context && (state.context.align == null) && state.context.type !== "pattern") { 124 | state.context.align = true; 125 | } 126 | if (curPunc === "(") { 127 | pushContext(state, ")", stream.column()); 128 | } else if (curPunc === "[") { 129 | pushContext(state, "]", stream.column()); 130 | } else if (curPunc === "{") { 131 | pushContext(state, "}", stream.column()); 132 | } else if (/[\]\}\)]/.test(curPunc)) { 133 | while (state.context && state.context.type === "pattern") { 134 | popContext(state); 135 | } 136 | if (state.context && curPunc === state.context.type) { 137 | popContext(state); 138 | } 139 | } else if (curPunc === "." && state.context && state.context.type === "pattern") { 140 | popContext(state); 141 | } else if (/atom|string|variable/.test(style) && state.context) { 142 | if (/[\}\]]/.test(state.context.type)) { 143 | pushContext(state, "pattern", stream.column()); 144 | } else if (state.context.type === "pattern" && !state.context.align) { 145 | state.context.align = true; 146 | state.context.col = stream.column(); 147 | } 148 | } 149 | return style; 150 | }, 151 | indent: function(state, textAfter) { 152 | var closing, context, firstChar; 153 | firstChar = textAfter && textAfter.charAt(0); 154 | context = state.context; 155 | if (/[\]\}]/.test(firstChar)) { 156 | while (context && context.type === "pattern") { 157 | context = context.prev; 158 | } 159 | } 160 | closing = context && firstChar === context.type; 161 | if (!context) { 162 | return 0; 163 | } else if (context.type === "keywords") { 164 | return newlineAndIndent; 165 | } else if (context.align) { 166 | return context.col + (closing ? 0 : 1); 167 | } else { 168 | return context.indent + (closing ? 0 : indentUnit); 169 | } 170 | } 171 | }; 172 | }); 173 | 174 | CodeMirror.modeExtensions["cypher"] = { 175 | autoFormatLineBreaks: function(text) { 176 | var i, lines, reProcessedPortion; 177 | lines = text.split("\n"); 178 | reProcessedPortion = /\s+\b(return|where|order by|match|with|skip|limit|create|delete|set)\b\s/g; 179 | i = 0; 180 | while (i < lines.length) { 181 | lines[i] = lines[i].replace(reProcessedPortion, " \n$1 ").trim(); 182 | i++; 183 | } 184 | return lines.join("\n"); 185 | } 186 | }; 187 | 188 | CodeMirror.defineMIME("application/x-cypher-query", "cypher"); 189 | 190 | }).call(this); 191 | -------------------------------------------------------------------------------- /scripts/cy2neod3.js: -------------------------------------------------------------------------------- 1 | function Cy2NeoD3(config, graphId, tableId, sourceId, execId, urlSource, renderGraph, cbResult) { 2 | function createEditor() { 3 | return CodeMirror.fromTextArea(document.getElementById(sourceId), { 4 | parserfile: ["codemirror-cypher.js"], 5 | path: "scripts", 6 | stylesheet: "styles/codemirror-neo.css", 7 | autoMatchParens: true, 8 | lineNumbers: true, 9 | enterMode: "keep", 10 | value: "some value" 11 | }); 12 | } 13 | var neod3 = new Neod3Renderer(); 14 | var neo = new Neo(urlSource); 15 | var editor = createEditor(); 16 | $("#"+execId).click(function(evt) { 17 | try { 18 | evt.preventDefault(); 19 | var query = editor.getValue(); 20 | console.log("Executing Query",query); 21 | var execButton = $(this).find('i'); 22 | execButton.toggleClass('fa-play-circle-o fa-spinner fa-spin') 23 | neo.executeQuery(query,{},function(err,res) { 24 | execButton.toggleClass('fa-spinner fa-spin fa-play-circle-o') 25 | res = res || {} 26 | var graph=res.graph; 27 | if (renderGraph) { 28 | if (graph) { 29 | var c=$("#"+graphId); 30 | c.empty(); 31 | neod3.render(graphId, c ,graph); 32 | renderResult(tableId, res.table); 33 | } else { 34 | if (err) { 35 | console.log(err); 36 | if (err.length > 0) { 37 | sweetAlert("Cypher error", err[0].code + "\n" + err[0].message, "error"); 38 | } else { 39 | sweetAlert("Ajax " + err.statusText, "Status " + err.status + ": " + err.state(), "error"); 40 | } 41 | } 42 | } 43 | } 44 | if(cbResult) cbResult(res); 45 | }); 46 | } catch(e) { 47 | console.log(e); 48 | sweetAlert("Catched error", e, "error"); 49 | } 50 | return false; 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /scripts/cypher.datatable.js: -------------------------------------------------------------------------------- 1 | function convertResult(data) { 2 | var charWidth = 14; 3 | var result = { columns: [], data: []}; 4 | var columns = Object.keys(data[0]); 5 | var count = columns.length; 6 | var rows = data.length; 7 | for (var col = 0; col < count; col++) { 8 | result.columns[col] = {"sTitle": columns[col], sWidth: columns[col].length * charWidth}; 9 | } 10 | for (var row = 0; row < rows; row++) { 11 | var currentRow = data[row]; 12 | var newRow = []; 13 | for (var col = 0; col < count; col++) { 14 | var value = convertCell(currentRow[columns[col]]); 15 | newRow[col] = value; 16 | result.columns[col].sWidth = Math.max(value.length * charWidth, result.columns[col].sWidth); 17 | } 18 | result.data[row] = newRow; 19 | } 20 | var width = 0; 21 | for (var col = 0; col < count; col++) { 22 | width += result.columns[col].sWidth; 23 | } 24 | var windowWith = $(window).width() / 2; 25 | for (var col = 0; col < count; col++) { 26 | // result.columns[col].sWidth=windowWith * result.columns[col].sWidth / width; 27 | result.columns[col].sWidth = "" + Math.round(100 * result.columns[col].sWidth / width) + "%"; 28 | // console.log(result.columns[col].sWidth); 29 | } 30 | return result; 31 | } 32 | 33 | function convertCell(cell) { 34 | if (cell == null) return ""; 35 | if (cell instanceof Array) { 36 | var result = []; 37 | for (var i = 0; i < cell.length; i++) { 38 | result.push(convertCell(cell[i])); 39 | } 40 | return "[" + result.join(", ") + "]"; 41 | } 42 | if (cell instanceof Object) { 43 | if (cell["_type"]) { 44 | return "(" + cell["_start"] + ")-[" + cell["_id"] + ":" + cell["_type"] + props(cell) + "]->(" + cell["_end"] + ")"; 45 | } else 46 | if (typeof(cell["_id"]) !== "undefined") { 47 | var labels = ""; 48 | if (cell["_labels"]) { 49 | labels = ":" + cell["_labels"].join(":"); 50 | } 51 | return "(" + cell["_id"] + labels + props(cell) + ")"; 52 | } 53 | return props(cell); 54 | } 55 | return cell; 56 | } 57 | 58 | function props(cell) { 59 | var props = []; 60 | for (key in cell) { 61 | if (cell.hasOwnProperty(key) && key[0] != '_') { 62 | props.push([key] + ":" + JSON.stringify(cell[key])); 63 | } 64 | } 65 | return props.length ? " {" + props.join(", ") + "}" : ""; 66 | } 67 | 68 | function renderResult(id, data) { 69 | if (!data || !data.length) return; 70 | var result = convertResult(data); 71 | var table = $('
').appendTo($("#" + id)); 72 | var large = result.data.length > 10; 73 | var dataTable = table.dataTable({ 74 | aoColumns: result.columns, 75 | bFilter: large, 76 | bInfo: large, 77 | bLengthChange: large, 78 | bPaginate: large, 79 | aaData: result.data, 80 | // bAutoWidth: true, 81 | aLengthMenu: [ 82 | [10, 25, 50, -1], 83 | [10, 25, 50, "All"] 84 | ], 85 | aaSorting: [], 86 | bSortable: true, 87 | oLanguage: { 88 | oPaginate: { 89 | sNext: " >> ", 90 | sPrevious: " << " 91 | } 92 | } 93 | }); 94 | } 95 | -------------------------------------------------------------------------------- /scripts/data.js: -------------------------------------------------------------------------------- 1 | var graph = { 2 | "nodes": [ 3 | { 4 | "caption": "Screen Actors Guild Award for Outstanding Performance by a Female Actor in a Miniseries or Television Movie", 5 | "type": "award", 6 | "id": 595472 7 | }, 8 | { 9 | "caption": "Children of the Corn III: Urban Harvest", 10 | "type": "movie", 11 | "id": 626470 12 | }, 13 | { 14 | "caption": "Sleepwalking", 15 | "type": "movie", 16 | "id": 795744 17 | }, 18 | { 19 | "caption": "That Thing You Do!", 20 | "type": "movie", 21 | "id": 692946 22 | }, 23 | { 24 | "caption": "Trapped", 25 | "type": "movie", 26 | "id": 689794 27 | }, 28 | { 29 | "caption": "Head in the Clouds", 30 | "type": "movie", 31 | "id": 709577 32 | }, 33 | { 34 | "caption": "Waking Up in Reno", 35 | "type": "movie", 36 | "id": 635905 37 | }, 38 | { 39 | "caption": "Battle in Seattle", 40 | "type": "movie", 41 | "id": 734583 42 | }, 43 | { 44 | "caption": "Mighty Joe Young", 45 | "type": "movie", 46 | "id": 662595 47 | }, 48 | { 49 | "caption": "Academy Award for Actress in a Leading Role", 50 | "type": "award", 51 | "id": 593781 52 | }, 53 | { 54 | "caption": "The Devil's Advocate", 55 | "type": "movie", 56 | "id": 740763 57 | }, 58 | { 59 | "caption": "Screen Actors Guild Award for Outstanding Performance by a Cast in a Motion Picture", 60 | "type": "award", 61 | "id": 595440 62 | }, 63 | { 64 | "caption": "Silver Bear for Best Actress", 65 | "type": "award", 66 | "id": 601507 67 | }, 68 | { 69 | "caption": "The Curse of the Jade Scorpion", 70 | "type": "movie", 71 | "id": 649461 72 | }, 73 | { 74 | "caption": "MTV Movie Award for Best Female Performance", 75 | "type": "award", 76 | "id": 595074 77 | }, 78 | { 79 | "caption": "15 Minutes", 80 | "type": "movie", 81 | "id": 634248 82 | }, 83 | { 84 | "caption": "The Burning Plain", 85 | "type": "movie", 86 | "id": 670704 87 | }, 88 | { 89 | "caption": "The Life and Death of Peter Sellers", 90 | "type": "movie", 91 | "id": 794982 92 | }, 93 | { 94 | "caption": "Prometheus", 95 | "type": "movie", 96 | "id": 608746 97 | }, 98 | { 99 | "caption": "Teen Choice Award for Choice Summer Movie Star: Female", 100 | "type": "award", 101 | "id": 599909 102 | }, 103 | { 104 | "caption": "Chicago Film Critics Association Award for Best Actress", 105 | "type": "award", 106 | "id": 623686 107 | }, 108 | { 109 | "caption": "Golden Globe Award for Best Supporting Actress - Series, Miniseries or Television Film", 110 | "type": "award", 111 | "id": 598027 112 | }, 113 | { 114 | "caption": "Golden Globe Award for Best Actress - Musical or Comedy Film", 115 | "type": "award", 116 | "id": 595206 117 | }, 118 | { 119 | "caption": "Mad Max: Fury Road", 120 | "type": "movie", 121 | "id": 804341 122 | }, 123 | { 124 | "caption": "In the Valley of Elah", 125 | "type": "movie", 126 | "id": 621675 127 | }, 128 | { 129 | "caption": "Screen Actors Guild Award for Outstanding Performance by a Female Actor in a Leading Role", 130 | "type": "award", 131 | "id": 593954 132 | }, 133 | { 134 | "caption": "Golden Raspberry Award for Worst Actress", 135 | "type": "award", 136 | "id": 594134 137 | }, 138 | { 139 | "caption": "East of Havana", 140 | "type": "movie", 141 | "id": 609415 142 | }, 143 | { 144 | "caption": "The Road", 145 | "type": "movie", 146 | "id": 627715 147 | }, 148 | { 149 | "caption": "Golden Globe Award for Best Actress - Drama Film", 150 | "type": "award", 151 | "id": 593776 152 | }, 153 | { 154 | "caption": "Charles Jacobus Theron", 155 | "type": "person", 156 | "id": 314008 157 | }, 158 | { 159 | "caption": "Jackson Theron", 160 | "type": "person", 161 | "id": 314009 162 | }, 163 | { 164 | "caption": "Primetime Emmy Award for Outstanding Supporting Actress in a Miniseries or a Movie", 165 | "type": "award", 166 | "id": 595684 167 | }, 168 | { 169 | "caption": "The Cider House Rules", 170 | "type": "movie", 171 | "id": 801237 172 | }, 173 | { 174 | "caption": "The Astronaut's Wife", 175 | "type": "movie", 176 | "id": 657006 177 | }, 178 | { 179 | "caption": "Broadcast Film Critics Association Award for Best Actress", 180 | "type": "award", 181 | "id": 601849 182 | }, 183 | { 184 | "caption": "Hancock", 185 | "type": "movie", 186 | "id": 652245 187 | }, 188 | { 189 | "caption": "Charlize Theron", 190 | "root": true, 191 | "id": 314003 192 | }, 193 | { 194 | "caption": "Stuart Townsend", 195 | "type": "person", 196 | "id": 314004 197 | }, 198 | { 199 | "caption": "Stephan Jenkins", 200 | "type": "person", 201 | "id": 314005 202 | }, 203 | { 204 | "caption": "Benoni, Gauteng", 205 | "type": "person", 206 | "id": 314006 207 | }, 208 | { 209 | "caption": "Gerda Jacoba Aletta Maritz", 210 | "type": "person", 211 | "id": 314007 212 | }, 213 | { 214 | "caption": "Æon Flux", 215 | "type": "movie", 216 | "id": 663286 217 | }, 218 | { 219 | "caption": "Snow White and the Huntsman", 220 | "type": "movie", 221 | "id": 599907 222 | }, 223 | { 224 | "caption": "Young Adult", 225 | "type": "movie", 226 | "id": 661733 227 | }, 228 | { 229 | "caption": "Reindeer Games", 230 | "type": "movie", 231 | "id": 761000 232 | }, 233 | { 234 | "caption": "Monster", 235 | "type": "movie", 236 | "id": 729778 237 | }, 238 | { 239 | "caption": "The Legend of Bagger Vance", 240 | "type": "movie", 241 | "id": 804616 242 | }, 243 | { 244 | "caption": "Teen Choice Award for Choice Hissy Fit: Film", 245 | "type": "award", 246 | "id": 599908 247 | }, 248 | { 249 | "caption": "The Yards", 250 | "type": "movie", 251 | "id": 781638 252 | }, 253 | { 254 | "caption": "MTV Movie Award for Best Kiss", 255 | "type": "award", 256 | "id": 595095 257 | }, 258 | { 259 | "caption": "Celebrity", 260 | "type": "movie", 261 | "id": 611629 262 | }, 263 | { 264 | "caption": "Astro Boy", 265 | "type": "movie", 266 | "id": 818608 267 | }, 268 | { 269 | "caption": "North Country", 270 | "type": "movie", 271 | "id": 784437 272 | }, 273 | { 274 | "caption": "2 Days in the Valley", 275 | "type": "movie", 276 | "id": 615556 277 | }, 278 | { 279 | "caption": "Satellite Award for Best Actress – Motion Picture", 280 | "type": "award", 281 | "id": 595704 282 | }, 283 | { 284 | "caption": "Trial and Error", 285 | "type": "movie", 286 | "id": 799574 287 | }, 288 | { 289 | "caption": "National Society of Film Critics Award for Best Actress", 290 | "type": "award", 291 | "id": 595702 292 | }, 293 | { 294 | "caption": "Independent Spirit Award for Best Female Lead", 295 | "type": "award", 296 | "id": 595703 297 | }, 298 | { 299 | "caption": "Two Eyes Staring", 300 | "type": "movie", 301 | "id": 788889 302 | }, 303 | { 304 | "caption": "Sweet November", 305 | "type": "movie", 306 | "id": 811358 307 | }, 308 | { 309 | "caption": "Teen Choice Movie Award: Villain", 310 | "type": "award", 311 | "id": 595082 312 | }, 313 | { 314 | "caption": "Satellite Award for Best Supporting Actress – Drama", 315 | "type": "award", 316 | "id": 602151 317 | }, 318 | { 319 | "caption": "San Francisco Film Critics Circle Award for Best Actress", 320 | "type": "award", 321 | "id": 669827 322 | }, 323 | { 324 | "caption": "Independent Spirit Award for Best First Feature", 325 | "type": "award", 326 | "id": 599387 327 | }, 328 | { 329 | "caption": "The Italian Job", 330 | "type": "movie", 331 | "id": 817380 332 | }, 333 | { 334 | "caption": "Hollywood Confidential", 335 | "type": "movie", 336 | "id": 711550 337 | }, 338 | { 339 | "caption": "Men of Honor", 340 | "type": "movie", 341 | "id": 682763 342 | }, 343 | { 344 | "caption": "BAFTA Award for Best Actress in a Leading Role", 345 | "type": "award", 346 | "id": 594478 347 | } 348 | ], 349 | "edges": [ 350 | { 351 | "source": 314003, 352 | "target": 621675, 353 | "caption": "ACTED_IN" 354 | }, 355 | { 356 | "source": 314003, 357 | "target": 818608, 358 | "caption": "ACTED_IN" 359 | }, 360 | { 361 | "source": 314003, 362 | "target": 601849, 363 | "caption": "NOMINATED" 364 | }, 365 | { 366 | "source": 314003, 367 | "target": 649461, 368 | "caption": "ACTED_IN" 369 | }, 370 | { 371 | "source": 314003, 372 | "target": 669827, 373 | "caption": "RECEIVED" 374 | }, 375 | { 376 | "source": 314003, 377 | "target": 608746, 378 | "caption": "ACTED_IN" 379 | }, 380 | { 381 | "source": 314003, 382 | "target": 593954, 383 | "caption": "RECEIVED" 384 | }, 385 | { 386 | "source": 314003, 387 | "target": 595702, 388 | "caption": "NOMINATED" 389 | }, 390 | { 391 | "source": 314003, 392 | "target": 601849, 393 | "caption": "RECEIVED" 394 | }, 395 | { 396 | "source": 314003, 397 | "target": 595095, 398 | "caption": "NOMINATED" 399 | }, 400 | { 401 | "source": 314003, 402 | "target": 729778, 403 | "caption": "ACTED_IN" 404 | }, 405 | { 406 | "source": 314003, 407 | "target": 595703, 408 | "caption": "NOMINATED" 409 | }, 410 | { 411 | "source": 314003, 412 | "target": 811358, 413 | "caption": "ACTED_IN" 414 | }, 415 | { 416 | "source": 314003, 417 | "target": 595472, 418 | "caption": "NOMINATED" 419 | }, 420 | { 421 | "source": 314003, 422 | "target": 661733, 423 | "caption": "PRODUCED" 424 | }, 425 | { 426 | "source": 314003, 427 | "target": 784437, 428 | "caption": "ACTED_IN" 429 | }, 430 | { 431 | "source": 314003, 432 | "target": 634248, 433 | "caption": "ACTED_IN" 434 | }, 435 | { 436 | "source": 314003, 437 | "target": 662595, 438 | "caption": "ACTED_IN" 439 | }, 440 | { 441 | "source": 314003, 442 | "target": 804616, 443 | "caption": "ACTED_IN" 444 | }, 445 | { 446 | "source": 314003, 447 | "target": 595703, 448 | "caption": "RECEIVED" 449 | }, 450 | { 451 | "source": 314003, 452 | "target": 626470, 453 | "caption": "ACTED_IN" 454 | }, 455 | { 456 | "source": 314003, 457 | "target": 599387, 458 | "caption": "RECEIVED" 459 | }, 460 | { 461 | "source": 314003, 462 | "target": 599908, 463 | "caption": "RECEIVED" 464 | }, 465 | { 466 | "source": 314003, 467 | "target": 682763, 468 | "caption": "ACTED_IN" 469 | }, 470 | { 471 | "source": 314003, 472 | "target": 595702, 473 | "caption": "RECEIVED" 474 | }, 475 | { 476 | "source": 314003, 477 | "target": 788889, 478 | "caption": "ACTED_IN" 479 | }, 480 | { 481 | "source": 314003, 482 | "target": 657006, 483 | "caption": "ACTED_IN" 484 | }, 485 | { 486 | "source": 314003, 487 | "target": 795744, 488 | "caption": "ACTED_IN" 489 | }, 490 | { 491 | "source": 314003, 492 | "target": 593781, 493 | "caption": "RECEIVED" 494 | }, 495 | { 496 | "source": 314003, 497 | "target": 594478, 498 | "caption": "NOMINATED" 499 | }, 500 | { 501 | "source": 314003, 502 | "target": 594134, 503 | "caption": "NOMINATED" 504 | }, 505 | { 506 | "source": 314003, 507 | "target": 595074, 508 | "caption": "NOMINATED" 509 | }, 510 | { 511 | "source": 314003, 512 | "target": 692946, 513 | "caption": "ACTED_IN" 514 | }, 515 | { 516 | "source": 314003, 517 | "target": 740763, 518 | "caption": "ACTED_IN" 519 | }, 520 | { 521 | "source": 314005, 522 | "target": 314003, 523 | "caption": "PARTNER_OF" 524 | }, 525 | { 526 | "source": 314003, 527 | "target": 711550, 528 | "caption": "ACTED_IN" 529 | }, 530 | { 531 | "source": 314003, 532 | "target": 595440, 533 | "caption": "NOMINATED" 534 | }, 535 | { 536 | "source": 314003, 537 | "target": 801237, 538 | "caption": "ACTED_IN" 539 | }, 540 | { 541 | "source": 314003, 542 | "target": 599907, 543 | "caption": "ACTED_IN" 544 | }, 545 | { 546 | "source": 314003, 547 | "target": 761000, 548 | "caption": "ACTED_IN" 549 | }, 550 | { 551 | "source": 314003, 552 | "target": 781638, 553 | "caption": "ACTED_IN" 554 | }, 555 | { 556 | "source": 314003, 557 | "target": 670704, 558 | "caption": "ACTED_IN" 559 | }, 560 | { 561 | "source": 314003, 562 | "target": 609415, 563 | "caption": "PRODUCED" 564 | }, 565 | { 566 | "source": 314003, 567 | "target": 314009, 568 | "caption": "PARENT_OF" 569 | }, 570 | { 571 | "source": 314003, 572 | "target": 652245, 573 | "caption": "ACTED_IN" 574 | }, 575 | { 576 | "source": 314003, 577 | "target": 661733, 578 | "caption": "ACTED_IN" 579 | }, 580 | { 581 | "source": 314003, 582 | "target": 602151, 583 | "caption": "NOMINATED" 584 | }, 585 | { 586 | "source": 314003, 587 | "target": 635905, 588 | "caption": "ACTED_IN" 589 | }, 590 | { 591 | "source": 314003, 592 | "target": 799574, 593 | "caption": "ACTED_IN" 594 | }, 595 | { 596 | "source": 314003, 597 | "target": 593781, 598 | "caption": "NOMINATED" 599 | }, 600 | { 601 | "source": 314003, 602 | "target": 817380, 603 | "caption": "ACTED_IN" 604 | }, 605 | { 606 | "source": 314003, 607 | "target": 611629, 608 | "caption": "ACTED_IN" 609 | }, 610 | { 611 | "source": 314003, 612 | "target": 729778, 613 | "caption": "PRODUCED" 614 | }, 615 | { 616 | "source": 314003, 617 | "target": 709577, 618 | "caption": "ACTED_IN" 619 | }, 620 | { 621 | "source": 314003, 622 | "target": 804341, 623 | "caption": "ACTED_IN" 624 | }, 625 | { 626 | "source": 314003, 627 | "target": 627715, 628 | "caption": "ACTED_IN" 629 | }, 630 | { 631 | "source": 314003, 632 | "target": 794982, 633 | "caption": "ACTED_IN" 634 | }, 635 | { 636 | "source": 314003, 637 | "target": 623686, 638 | "caption": "RECEIVED" 639 | }, 640 | { 641 | "source": 314003, 642 | "target": 595082, 643 | "caption": "NOMINATED" 644 | }, 645 | { 646 | "source": 314003, 647 | "target": 689794, 648 | "caption": "ACTED_IN" 649 | }, 650 | { 651 | "source": 314003, 652 | "target": 788889, 653 | "caption": "PRODUCED" 654 | }, 655 | { 656 | "source": 314007, 657 | "target": 314003, 658 | "caption": "PARENT_OF" 659 | }, 660 | { 661 | "source": 314003, 662 | "target": 593776, 663 | "caption": "NOMINATED" 664 | }, 665 | { 666 | "source": 314003, 667 | "target": 734583, 668 | "caption": "ACTED_IN" 669 | }, 670 | { 671 | "source": 314003, 672 | "target": 598027, 673 | "caption": "NOMINATED" 674 | }, 675 | { 676 | "source": 314003, 677 | "target": 601507, 678 | "caption": "RECEIVED" 679 | }, 680 | { 681 | "source": 314003, 682 | "target": 599909, 683 | "caption": "NOMINATED" 684 | }, 685 | { 686 | "source": 314003, 687 | "target": 314004, 688 | "caption": "PARTNER_OF" 689 | }, 690 | { 691 | "source": 314003, 692 | "target": 663286, 693 | "caption": "ACTED_IN" 694 | }, 695 | { 696 | "source": 314003, 697 | "target": 314006, 698 | "caption": "BORN_AT" 699 | }, 700 | { 701 | "source": 314003, 702 | "target": 615556, 703 | "caption": "ACTED_IN" 704 | }, 705 | { 706 | "source": 314004, 707 | "target": 314003, 708 | "caption": "PARTNER_OF" 709 | }, 710 | { 711 | "source": 314008, 712 | "target": 314003, 713 | "caption": "PARENT_OF" 714 | }, 715 | { 716 | "source": 314003, 717 | "target": 314005, 718 | "caption": "PARTNER_OF" 719 | }, 720 | { 721 | "source": 314003, 722 | "target": 795744, 723 | "caption": "PRODUCED" 724 | }, 725 | { 726 | "source": 314003, 727 | "target": 595704, 728 | "caption": "RECEIVED" 729 | }, 730 | { 731 | "source": 314003, 732 | "target": 670704, 733 | "caption": "EXEC_PRODUCED" 734 | }, 735 | { 736 | "source": 314003, 737 | "target": 593954, 738 | "caption": "NOMINATED" 739 | }, 740 | { 741 | "source": 314003, 742 | "target": 595206, 743 | "caption": "NOMINATED" 744 | }, 745 | { 746 | "source": 314003, 747 | "target": 593776, 748 | "caption": "RECEIVED" 749 | }, 750 | { 751 | "source": 314003, 752 | "target": 595704, 753 | "caption": "NOMINATED" 754 | }, 755 | { 756 | "source": 314003, 757 | "target": 595684, 758 | "caption": "NOMINATED" 759 | }, 760 | { 761 | "source": 314003, 762 | "target": 599387, 763 | "caption": "NOMINATED" 764 | } 765 | ] 766 | } -------------------------------------------------------------------------------- /scripts/jquery.dataTables.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * File: jquery.dataTables.min.js 3 | * Version: 1.9.4 4 | * Author: Allan Jardine (www.sprymedia.co.uk) 5 | * Info: www.datatables.net 6 | * 7 | * Copyright 2008-2012 Allan Jardine, all rights reserved. 8 | * 9 | * This source file is free software, under either the GPL v2 license or a 10 | * BSD style license, available at: 11 | * http://datatables.net/license_gpl2 12 | * http://datatables.net/license_bsd 13 | * 14 | * This source file is distributed in the hope that it will be useful, but 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 17 | */ 18 | (function(la,s,p){(function(i){if(typeof define==="function"&&define.amd)define(["jquery"],i);else jQuery&&!jQuery.fn.dataTable&&i(jQuery)})(function(i){var l=function(h){function n(a,b){var c=l.defaults.columns,d=a.aoColumns.length;b=i.extend({},l.models.oColumn,c,{sSortingClass:a.oClasses.sSortable,sSortingClassJUI:a.oClasses.sSortJUI,nTh:b?b:s.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.oDefaults:d});a.aoColumns.push(b);if(a.aoPreSearchCols[d]=== 19 | p||a.aoPreSearchCols[d]===null)a.aoPreSearchCols[d]=i.extend({},l.models.oSearch);else{b=a.aoPreSearchCols[d];if(b.bRegex===p)b.bRegex=true;if(b.bSmart===p)b.bSmart=true;if(b.bCaseInsensitive===p)b.bCaseInsensitive=true}q(a,d,null)}function q(a,b,c){var d=a.aoColumns[b];if(c!==p&&c!==null){if(c.mDataProp&&!c.mData)c.mData=c.mDataProp;if(c.sType!==p){d.sType=c.sType;d._bAutoType=false}i.extend(d,c);r(d,c,"sWidth","sWidthOrig");if(c.iDataSort!==p)d.aDataSort=[c.iDataSort];r(d,c,"aDataSort")}var e=d.mRender? 20 | ca(d.mRender):null,f=ca(d.mData);d.fnGetData=function(g,j){var k=f(g,j);if(d.mRender&&j&&j!=="")return e(k,j,g);return k};d.fnSetData=Ja(d.mData);if(!a.oFeatures.bSort)d.bSortable=false;if(!d.bSortable||i.inArray("asc",d.asSorting)==-1&&i.inArray("desc",d.asSorting)==-1){d.sSortingClass=a.oClasses.sSortableNone;d.sSortingClassJUI=""}else if(i.inArray("asc",d.asSorting)==-1&&i.inArray("desc",d.asSorting)==-1){d.sSortingClass=a.oClasses.sSortable;d.sSortingClassJUI=a.oClasses.sSortJUI}else if(i.inArray("asc", 21 | d.asSorting)!=-1&&i.inArray("desc",d.asSorting)==-1){d.sSortingClass=a.oClasses.sSortableAsc;d.sSortingClassJUI=a.oClasses.sSortJUIAscAllowed}else if(i.inArray("asc",d.asSorting)==-1&&i.inArray("desc",d.asSorting)!=-1){d.sSortingClass=a.oClasses.sSortableDesc;d.sSortingClassJUI=a.oClasses.sSortJUIDescAllowed}}function o(a){if(a.oFeatures.bAutoWidth===false)return false;ta(a);for(var b=0,c=a.aoColumns.length;b=0;e--){var m=b[e].aTargets;i.isArray(m)||O(a,1,"aTargets must be an array of targets, not a "+typeof m);f=0;for(g=m.length;f=0){for(;a.aoColumns.length<=m[f];)n(a);d(m[f],b[e])}else if(typeof m[f]==="number"&&m[f]<0)d(a.aoColumns.length+m[f],b[e]);else if(typeof m[f]=== 24 | "string"){j=0;for(k=a.aoColumns.length;jb&&a[d]--;c!=-1&&a.splice(c,1)}function da(a,b,c){var d=a.aoColumns[c];return d.fnRender({iDataRow:b,iDataColumn:c,oSettings:a, 33 | aData:a.aoData[b]._aData,mDataProp:d.mData},F(a,b,c,"display"))}function ua(a,b){var c=a.aoData[b],d;if(c.nTr===null){c.nTr=s.createElement("tr");c.nTr._DT_RowIndex=b;if(c._aData.DT_RowId)c.nTr.id=c._aData.DT_RowId;if(c._aData.DT_RowClass)c.nTr.className=c._aData.DT_RowClass;for(var e=0,f=a.aoColumns.length;e=0;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);j.push([])}d=0;for(e=g.length;d=a.fnRecordsDisplay()?0:a.iInitDisplayStart;a.iInitDisplayStart=-1;I(a)}if(a.bDeferLoading){a.bDeferLoading=false;a.iDraw++}else if(a.oFeatures.bServerSide){if(!a.bDestroying&&!La(a))return}else a.iDraw++;if(a.aiDisplay.length!==0){var g=a._iDisplayStart;d=a._iDisplayEnd;if(a.oFeatures.bServerSide){g=0;d=a.aoData.length}for(g=g;g")[0];a.nTable.parentNode.insertBefore(b,a.nTable);a.nTableWrapper=i('
')[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var c=a.nTableWrapper,d=a.sDom.split(""),e,f,g,j,k,m,u,x=0;x")[0];k=d[x+1];if(k=="'"||k=='"'){m="";for(u=2;d[x+u]!=k;){m+=d[x+u];u++}if(m=="H")m=a.oClasses.sJUIHeader;else if(m=="F")m=a.oClasses.sJUIFooter;if(m.indexOf(".")!=-1){k= 44 | m.split(".");j.id=k[0].substr(1,k[0].length-1);j.className=k[1]}else if(m.charAt(0)=="#")j.id=m.substr(1,m.length-1);else j.className=m;x+=u}c.appendChild(j);c=j}else if(g==">")c=c.parentNode;else if(g=="l"&&a.oFeatures.bPaginate&&a.oFeatures.bLengthChange){e=Na(a);f=1}else if(g=="f"&&a.oFeatures.bFilter){e=Oa(a);f=1}else if(g=="r"&&a.oFeatures.bProcessing){e=Pa(a);f=1}else if(g=="t"){e=Qa(a);f=1}else if(g=="i"&&a.oFeatures.bInfo){e=Ra(a);f=1}else if(g=="p"&&a.oFeatures.bPaginate){e=Sa(a);f=1}else if(l.ext.aoFeatures.length!== 45 | 0){j=l.ext.aoFeatures;u=0;for(k=j.length;u'):c===""?'':c+' ';var d=s.createElement("div");d.className=a.oClasses.sFilter;d.innerHTML="";if(!a.aanFeatures.f)d.id=a.sTableId+"_filter";c=i('input[type="text"]',d);d._DT_Input=c[0];c.val(b.sSearch.replace('"',"""));c.bind("keyup.DT",function(){for(var e=a.aanFeatures.f,f=this.value===""?"":this.value,g=0,j=e.length;g=0;d--){e=Ya(F(a,a.aiDisplay[d],c, 54 | "filter"),a.aoColumns[c].sType);if(!b.test(e)){a.aiDisplay.splice(d,1);g++}}}}function Va(a,b,c,d,e,f){d=Ca(b,d,e,f);e=a.oPreviousSearch;c||(c=0);if(l.ext.afnFiltering.length!==0)c=1;if(b.length<=0){a.aiDisplay.splice(0,a.aiDisplay.length);a.aiDisplay=a.aiDisplayMaster.slice()}else if(a.aiDisplay.length==a.aiDisplayMaster.length||e.sSearch.length>b.length||c==1||b.indexOf(e.sSearch)!==0){a.aiDisplay.splice(0,a.aiDisplay.length);Ba(a,1);for(b=0;b").html(a).text();return a.replace(/[\n\r]/g," ")}function Ca(a,b,c,d){if(c){a=b?a.split(" "): 56 | Ea(a).split(" ");a="^(?=.*?"+a.join(")(?=.*?")+").*$";return new RegExp(a,d?"i":"")}else{a=b?a:Ea(a);return new RegExp(a,d?"i":"")}}function Ya(a,b){if(typeof l.ext.ofnSearch[b]==="function")return l.ext.ofnSearch[b](a);else if(a===null)return"";else if(b=="html")return a.replace(/[\r\n]/g," ").replace(/<.*?>/g,"");else if(typeof a==="string")return a.replace(/[\r\n]/g," ");return a}function Ea(a){return a.replace(new RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"), 57 | "\\$1")}function Ra(a){var b=s.createElement("div");b.className=a.oClasses.sInfo;if(!a.aanFeatures.i){a.aoDrawCallback.push({fn:Za,sName:"information"});b.id=a.sTableId+"_info"}a.nTable.setAttribute("aria-describedby",a.sTableId+"_info");return b}function Za(a){if(!(!a.oFeatures.bInfo||a.aanFeatures.i.length===0)){var b=a.oLanguage,c=a._iDisplayStart+1,d=a.fnDisplayEnd(),e=a.fnRecordsTotal(),f=a.fnRecordsDisplay(),g;g=f===0?b.sInfoEmpty:b.sInfo;if(f!=e)g+=" "+b.sInfoFiltered;g+=b.sInfoPostFix;g=za(a, 58 | g);if(b.fnInfoCallback!==null)g=b.fnInfoCallback.call(a.oInstance,a,c,d,e,f,g);a=a.aanFeatures.i;b=0;for(c=a.length;b",c,d,e=a.aLengthMenu;if(e.length==2&&typeof e[0]==="object"&&typeof e[1]==="object"){c=0;for(d=e[0].length;c'+e[1][c]+""}else{c=0;for(d=e.length;c'+e[c]+""}b+=""; 62 | e=s.createElement("div");if(!a.aanFeatures.l)e.id=a.sTableId+"_length";e.className=a.oClasses.sLength;e.innerHTML="";i('select option[value="'+a._iDisplayLength+'"]',e).attr("selected",true);i("select",e).bind("change.DT",function(){var f=i(this).val(),g=a.aanFeatures.l;c=0;for(d=g.length;ca.aiDisplay.length||a._iDisplayLength==-1?a.aiDisplay.length:a._iDisplayStart+a._iDisplayLength}function Sa(a){if(a.oScroll.bInfinite)return null;var b=s.createElement("div");b.className=a.oClasses.sPaging+ 64 | a.sPaginationType;l.ext.oPagination[a.sPaginationType].fnInit(a,b,function(c){I(c);H(c)});a.aanFeatures.p||a.aoDrawCallback.push({fn:function(c){l.ext.oPagination[c.sPaginationType].fnUpdate(c,function(d){I(d);H(d)})},sName:"pagination"});return b}function Ga(a,b){var c=a._iDisplayStart;if(typeof b==="number"){a._iDisplayStart=b*a._iDisplayLength;if(a._iDisplayStart>a.fnRecordsDisplay())a._iDisplayStart=0}else if(b=="first")a._iDisplayStart=0;else if(b=="previous"){a._iDisplayStart=a._iDisplayLength>= 65 | 0?a._iDisplayStart-a._iDisplayLength:0;if(a._iDisplayStart<0)a._iDisplayStart=0}else if(b=="next")if(a._iDisplayLength>=0){if(a._iDisplayStart+a._iDisplayLength=0){b=parseInt((a.fnRecordsDisplay()-1)/a._iDisplayLength,10)+1;a._iDisplayStart=(b-1)*a._iDisplayLength}else a._iDisplayStart=0;else O(a,0,"Unknown paging action: "+b);i(a.oInstance).trigger("page",a);return c!=a._iDisplayStart} 66 | function Pa(a){var b=s.createElement("div");if(!a.aanFeatures.r)b.id=a.sTableId+"_processing";b.innerHTML=a.oLanguage.sProcessing;b.className=a.oClasses.sProcessing;a.nTable.parentNode.insertBefore(b,a.nTable);return b}function P(a,b){if(a.oFeatures.bProcessing)for(var c=a.aanFeatures.r,d=0,e=c.length;d0){d=d[0];if(d._captionSide==="top")j.appendChild(d);else d._captionSide==="bottom"&&u&&k.appendChild(d)}if(a.oScroll.sX!==""){c.style.width=t(a.oScroll.sX);e.style.width=t(a.oScroll.sX);if(u!==null)f.style.width=t(a.oScroll.sX);i(e).scroll(function(){c.scrollLeft=this.scrollLeft;if(u!==null)f.scrollLeft=this.scrollLeft})}if(a.oScroll.sY!=="")e.style.height=t(a.oScroll.sY);a.aoDrawCallback.push({fn:$a,sName:"scrolling"});a.oScroll.bInfinite&& 70 | i(e).scroll(function(){if(!a.bDrawing&&i(this).scrollTop()!==0)if(i(this).scrollTop()+i(this).height()>i(a.nTable).height()-a.oScroll.iLoadGap)if(a.fnDisplayEnd()d.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=t(i(a.nTable).outerWidth()- 73 | a.oScroll.iBarWidth)}else if(a.oScroll.sXInner!=="")a.nTable.style.width=t(a.oScroll.sXInner);else if(e==i(d).width()&&i(d).height()e-a.oScroll.iBarWidth)a.nTable.style.width=t(e)}else a.nTable.style.width=t(e);e=i(a.nTable).outerWidth();N(ja,j);N(function(z){y.push(t(i(z).width()))},j);N(function(z,Q){z.style.width=y[Q]},g);i(j).height(0);if(a.nTFoot!==null){N(ja,k);N(function(z){B.push(t(i(z).width()))}, 74 | k);N(function(z,Q){z.style.width=B[Q]},m);i(k).height(0)}N(function(z,Q){z.innerHTML="";z.style.width=y[Q]},j);a.nTFoot!==null&&N(function(z,Q){z.innerHTML="";z.style.width=B[Q]},k);if(i(a.nTable).outerWidth()d.offsetHeight||i(d).css("overflow-y")=="scroll"?e+a.oScroll.iBarWidth:e;if(L&&(d.scrollHeight>d.offsetHeight||i(d).css("overflow-y")=="scroll"))a.nTable.style.width=t(g-a.oScroll.iBarWidth);d.style.width=t(g);a.nScrollHead.style.width=t(g);if(a.nTFoot!==null)a.nScrollFoot.style.width= 75 | t(g);if(a.oScroll.sX==="")O(a,1,"The table cannot fit into the current element which will cause column misalignment. The table has been drawn at its minimum possible width.");else a.oScroll.sXInner!==""&&O(a,1,"The table cannot fit into the current element which will cause column misalignment. Increase the sScrollXInner value or remove it to allow automatic calculation")}else{d.style.width=t("100%");a.nScrollHead.style.width=t("100%");if(a.nTFoot!==null)a.nScrollFoot.style.width=t("100%")}if(a.oScroll.sY=== 76 | "")if(L)d.style.height=t(a.nTable.offsetHeight+a.oScroll.iBarWidth);if(a.oScroll.sY!==""&&a.oScroll.bCollapse){d.style.height=t(a.oScroll.sY);L=a.oScroll.sX!==""&&a.nTable.offsetWidth>d.offsetWidth?a.oScroll.iBarWidth:0;if(a.nTable.offsetHeightd.clientHeight||i(d).css("overflow-y")=="scroll";b.style.paddingRight=c?a.oScroll.iBarWidth+"px":"0px";if(a.nTFoot!== 77 | null){M.style.width=t(L);T.style.width=t(L);T.style.paddingRight=c?a.oScroll.iBarWidth+"px":"0px"}i(d).scroll();if(a.bSorted||a.bFiltered)d.scrollTop=0}function N(a,b,c){for(var d=0,e=0,f=b.length,g,j;etd",b);j=Z(a,f);for(f=d=0;f0)a.aoColumns[f].sWidth=t(g);d++}e=i(b).css("width"); 82 | a.nTable.style.width=e.indexOf("%")!==-1?e:t(i(b).outerWidth());b.parentNode.removeChild(b)}if(k)a.nTable.style.width=t(k)}function cb(a,b){if(a.oScroll.sX===""&&a.oScroll.sY!==""){i(b).width();b.style.width=t(i(b).outerWidth()-a.oScroll.iBarWidth)}else if(a.oScroll.sX!=="")b.style.width=t(i(b).outerWidth())}function bb(a,b){var c=db(a,b);if(c<0)return null;if(a.aoData[c].nTr===null){var d=s.createElement("td");d.innerHTML=F(a,c,b,"");return d}return W(a,c)[b]}function db(a,b){for(var c=-1,d=-1,e= 83 | 0;e/g,"");if(f.length>c){c=f.length;d=e}}return d}function t(a){if(a===null)return"0px";if(typeof a=="number"){if(a<0)return"0px";return a+"px"}var b=a.charCodeAt(a.length-1);if(b<48||b>57)return a;return a+"px"}function eb(){var a=s.createElement("p"),b=a.style;b.width="100%";b.height="200px";b.padding="0px";var c=s.createElement("div");b=c.style;b.position="absolute";b.top="0px";b.left="0px";b.visibility="hidden";b.width="200px"; 84 | b.height="150px";b.padding="0px";b.overflow="hidden";c.appendChild(a);s.body.appendChild(c);b=a.offsetWidth;c.style.overflow="scroll";a=a.offsetWidth;if(b==a)a=c.clientWidth;s.body.removeChild(c);return b-a}function $(a,b){var c,d,e,f,g,j,k=[],m=[],u=l.ext.oSort,x=a.aoData,y=a.aoColumns,B=a.oLanguage.oAria;if(!a.oFeatures.bServerSide&&(a.aaSorting.length!==0||a.aaSortingFixed!==null)){k=a.aaSortingFixed!==null?a.aaSortingFixed.concat(a.aaSorting):a.aaSorting.slice();for(c=0;c/g,"");b=y[c].nTh;b.removeAttribute("aria-sort");b.removeAttribute("aria-label"); 87 | if(y[c].bSortable)if(k.length>0&&k[0][0]==c){b.setAttribute("aria-sort",k[0][1]=="asc"?"ascending":"descending");b.setAttribute("aria-label",e+((y[c].asSorting[k[0][2]+1]?y[c].asSorting[k[0][2]+1]:y[c].asSorting[0])=="asc"?B.sSortAscending:B.sSortDescending))}else b.setAttribute("aria-label",e+(y[c].asSorting[0]=="asc"?B.sSortAscending:B.sSortDescending));else b.setAttribute("aria-label",e)}a.bSorted=true;i(a.oInstance).trigger("sort",a);if(a.oFeatures.bFilter)X(a,a.oPreviousSearch,1);else{a.aiDisplay= 88 | a.aiDisplayMaster.slice();a._iDisplayStart=0;I(a);H(a)}}function ya(a,b,c,d){fb(b,{},function(e){if(a.aoColumns[c].bSortable!==false){var f=function(){var g,j;if(e.shiftKey){for(var k=false,m=0;m0&&d.indexOf(k)==-1)a[b].className=d+" "+k}}}function Ha(a){if(!(!a.oFeatures.bStateSave||a.bDestroying)){var b,c;b=a.oScroll.bInfinite;var d={iCreate:(new Date).getTime(),iStart:b?0:a._iDisplayStart,iEnd:b?a._iDisplayLength:a._iDisplayEnd,iLength:a._iDisplayLength,aaSorting:i.extend(true,[],a.aaSorting),oSearch:i.extend(true,{},a.oPreviousSearch),aoSearchCols:i.extend(true,[],a.aoPreSearchCols),abVisCols:[]};b=0;for(c=a.aoColumns.length;b4096){for(var j=0,k=a.length;j4096;){if(f.length===0)return;d=f.pop();s.cookie=d.name+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ 96 | c.join("/")+"/"}}s.cookie=b}function mb(a){var b=la.location.pathname.split("/");a=a+"_"+b[b.length-1].replace(/[\/:]/g,"").toLowerCase()+"=";b=s.cookie.split(";");for(var c=0;c=0;f--)e.push(b[f].fn.apply(a.oInstance,d));c!==null&&i(a.oInstance).trigger(c,d);return e}function ib(a){var b=i('
')[0];s.body.appendChild(b); 100 | a.oBrowser.bScrollOversize=i("#DT_BrowserTest",b)[0].offsetWidth===100?true:false;s.body.removeChild(b)}function jb(a){return function(){var b=[C(this[l.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return l.ext.oApi[a].apply(this,b)}}var ga=/\[.*?\]$/,kb=la.JSON?JSON.stringify:function(a){var b=typeof a;if(b!=="object"||a===null){if(b==="string")a='"'+a+'"';return a+""}var c,d,e=[],f=i.isArray(a);for(c in a){d=a[c];b=typeof d;if(b==="string")d='"'+d+'"';else if(b==="object"&&d!== 101 | null)d=kb(d);e.push((f?"":'"'+c+'":')+d)}return(f?"[":"{")+e+(f?"]":"}")};this.$=function(a,b){var c,d=[],e;c=C(this[l.ext.iApiIndex]);var f=c.aoData,g=c.aiDisplay,j=c.aiDisplayMaster;b||(b={});b=i.extend({},{filter:"none",order:"current",page:"all"},b);if(b.page=="current"){b=c._iDisplayStart;for(c=c.fnDisplayEnd();b=d.fnRecordsDisplay()){d._iDisplayStart-=d._iDisplayLength;if(d._iDisplayStart<0)d._iDisplayStart=0}if(c===p||c){I(d);H(d)}return g};this.fnDestroy=function(a){var b=C(this[l.ext.iApiIndex]),c=b.nTableWrapper.parentNode,d=b.nTBody,e,f;a=a===p?false:a;b.bDestroying=true;K(b,"aoDestroyCallback","destroy",[b]);if(!a){e=0;for(f=b.aoColumns.length;e< 106 | f;e++)b.aoColumns[e].bVisible===false&&this.fnSetColumnVis(e,true)}i(b.nTableWrapper).find("*").andSelf().unbind(".DT");i("tbody>tr>td."+b.oClasses.sRowEmpty,b.nTable).parent().remove();if(b.nTable!=b.nTHead.parentNode){i(b.nTable).children("thead").remove();b.nTable.appendChild(b.nTHead)}if(b.nTFoot&&b.nTable!=b.nTFoot.parentNode){i(b.nTable).children("tfoot").remove();b.nTable.appendChild(b.nTFoot)}b.nTable.parentNode.removeChild(b.nTable);i(b.nTableWrapper).remove();b.aaSorting=[];b.aaSortingFixed= 107 | [];ba(b);i(fa(b)).removeClass(b.asStripeClasses.join(" "));i("th, td",b.nTHead).removeClass([b.oClasses.sSortable,b.oClasses.sSortableAsc,b.oClasses.sSortableDesc,b.oClasses.sSortableNone].join(" "));if(b.bJUI){i("th span."+b.oClasses.sSortIcon+", td span."+b.oClasses.sSortIcon,b.nTHead).remove();i("th, td",b.nTHead).each(function(){var g=i("div."+b.oClasses.sSortJUIWrapper,this),j=g.contents();i(this).append(j);g.remove()})}if(!a&&b.nTableReinsertBefore)c.insertBefore(b.nTable,b.nTableReinsertBefore); 108 | else a||c.appendChild(b.nTable);e=0;for(f=b.aoData.length;e=D(d);if(!m)for(e=a;et<"F"ip>'}else i.extend(g.oClasses,l.ext.oStdClasses); 125 | i(this).addClass(g.oClasses.sTable);if(g.oScroll.sX!==""||g.oScroll.sY!=="")g.oScroll.iBarWidth=eb();if(g.iInitDisplayStart===p){g.iInitDisplayStart=h.iDisplayStart;g._iDisplayStart=h.iDisplayStart}if(h.bStateSave){g.oFeatures.bStateSave=true;gb(g,h);J(g,"aoDrawCallback",Ha,"state_save")}if(h.iDeferLoading!==null){g.bDeferLoading=true;a=i.isArray(h.iDeferLoading);g._iRecordsDisplay=a?h.iDeferLoading[0]:h.iDeferLoading;g._iRecordsTotal=a?h.iDeferLoading[1]:h.iDeferLoading}if(h.aaData!==null)f=true; 126 | if(h.oLanguage.sUrl!==""){g.oLanguage.sUrl=h.oLanguage.sUrl;i.getJSON(g.oLanguage.sUrl,null,function(k){Fa(k);i.extend(true,g.oLanguage,h.oLanguage,k);ra(g)});e=true}else i.extend(true,g.oLanguage,h.oLanguage);if(h.asStripeClasses===null)g.asStripeClasses=[g.oClasses.sStripeOdd,g.oClasses.sStripeEven];b=g.asStripeClasses.length;g.asDestroyStripes=[];if(b){c=false;d=i(this).children("tbody").children("tr:lt("+b+")");for(a=0;a=g.aoColumns.length)g.aaSorting[a][0]= 128 | 0;var j=g.aoColumns[g.aaSorting[a][0]];if(g.aaSorting[a][2]===p)g.aaSorting[a][2]=0;if(h.aaSorting===p&&g.saved_aaSorting===p)g.aaSorting[a][1]=j.asSorting[0];c=0;for(d=j.asSorting.length;c0&&(g.oScroll.sX!==""||g.oScroll.sY!=="")){b=[s.createElement("tfoot")];this.appendChild(b[0])}if(b.length>0){g.nTFoot=b[0];ha(g.aoFooter,g.nTFoot)}if(f)for(a=0;a=parseInt(v,10)};l.fnIsDataTable=function(h){for(var n=l.settings,q=0;q'+o.sPrevious+''+o.sNext+"":'';i(n).append(o);var w=i("a",n);o=w[0];w=w[1];h.oApi._fnBindAction(o,{action:"previous"},v);h.oApi._fnBindAction(w,{action:"next"},v); 150 | if(!h.aanFeatures.p){n.id=h.sTableId+"_paginate";o.id=h.sTableId+"_previous";w.id=h.sTableId+"_next";o.setAttribute("aria-controls",h.sTableId);w.setAttribute("aria-controls",h.sTableId)}},fnUpdate:function(h){if(h.aanFeatures.p)for(var n=h.oClasses,q=h.aanFeatures.p,o,v=0,w=q.length;v'+o.sFirst+''+o.sPrevious+''+o.sNext+''+o.sLast+"");var D=i("a",n);o=D[0];v=D[1];var A=D[2];D=D[3];h.oApi._fnBindAction(o,{action:"first"},w);h.oApi._fnBindAction(v,{action:"previous"},w);h.oApi._fnBindAction(A,{action:"next"},w);h.oApi._fnBindAction(D,{action:"last"},w);if(!h.aanFeatures.p){n.id=h.sTableId+"_paginate";o.id=h.sTableId+"_first";v.id=h.sTableId+"_previous";A.id=h.sTableId+"_next";D.id=h.sTableId+"_last"}},fnUpdate:function(h,n){if(h.aanFeatures.p){var q=l.ext.oPagination.iFullNumbersShowPages,o=Math.floor(q/2),v= 153 | Math.ceil(h.fnRecordsDisplay()/h._iDisplayLength),w=Math.ceil(h._iDisplayStart/h._iDisplayLength)+1,D="",A,G=h.oClasses,E,Y=h.aanFeatures.p,ma=function(R){h.oApi._fnBindAction(this,{page:R+A-1},function(ea){h.oApi._fnPageChange(h,ea.data.page);n(h);ea.preventDefault()})};if(h._iDisplayLength===-1)w=o=A=1;else if(v=v-o){A=v-q+1;o=v}else{A=w-Math.ceil(q/2)+1;o=A+q-1}for(q=A;q<=o;q++)D+=w!==q?''+h.fnFormatNumber(q)+ 154 | "":''+h.fnFormatNumber(q)+"";q=0;for(o=Y.length;qn?1:0},"string-desc":function(h,n){return hn?-1:0},"html-pre":function(h){return h.replace(/<.*?>/g,"").toLowerCase()},"html-asc":function(h,n){return hn?1:0},"html-desc":function(h,n){return hn?-1:0},"date-pre":function(h){h=Date.parse(h);if(isNaN(h)||h==="")h=Date.parse("01/01/1970 00:00:00"); 156 | return h},"date-asc":function(h,n){return h-n},"date-desc":function(h,n){return n-h},"numeric-pre":function(h){return h=="-"||h===""?0:h*1},"numeric-asc":function(h,n){return h-n},"numeric-desc":function(h,n){return n-h}});i.extend(l.ext.aTypes,[function(h){if(typeof h==="number")return"numeric";else if(typeof h!=="string")return null;var n,q=false;n=h.charAt(0);if("0123456789-".indexOf(n)==-1)return null;for(var o=1;o")!=-1)return"html";return null}]);i.fn.DataTable=l;i.fn.dataTable=l;i.fn.dataTableSettings=l.settings;i.fn.dataTableExt=l.ext})})(window,document); 158 | -------------------------------------------------------------------------------- /scripts/neo4d3.js: -------------------------------------------------------------------------------- 1 | function Neo(urlSource) { 2 | function txUrl() { 3 | var connection = urlSource(); 4 | var url = (connection.url || "http://localhost:7474").replace(/\/db\/data.*/,""); 5 | return url + "/db/data/transaction/commit"; 6 | } 7 | var me = { 8 | executeQuery: function(query, params, cb) { 9 | var connection = urlSource(); 10 | var auth = ((connection.user || "") == "") ? "" : "Basic " + btoa(connection.user + ":" + connection.pass); 11 | $.ajax(txUrl(), { 12 | type: "POST", 13 | data: JSON.stringify({ 14 | statements: [{ 15 | statement: query, 16 | parameters: params || {}, 17 | resultDataContents: ["row", "graph"] 18 | }] 19 | }), 20 | contentType: "application/json", 21 | error: function(err) { 22 | cb(err); 23 | }, 24 | beforeSend: function (xhr) { 25 | if (auth && auth.length) xhr.setRequestHeader ("Authorization", auth); 26 | }, 27 | success: function(res) { 28 | if (res.errors.length > 0) { 29 | cb(res.errors); 30 | } else { 31 | var cols = res.results[0].columns; 32 | var rows = res.results[0].data.map(function(row) { 33 | var r = {}; 34 | cols.forEach(function(col, index) { 35 | r[col] = row.row[index]; 36 | }); 37 | return r; 38 | }); 39 | var nodes = []; 40 | var rels = []; 41 | var labels = []; 42 | function findNode(nodes, id) { 43 | for (var i=0;i 0; 51 | if (!found) { 52 | //n.props=n.properties; 53 | for(var p in n.properties||{}) { n[p]=n.properties[p];delete n.properties[p];} 54 | delete n.properties; 55 | nodes.push(n); 56 | labels=labels.concat(n.labels.filter(function(l) { labels.indexOf(l) == -1 })) 57 | } 58 | }); 59 | rels = rels.concat(row.graph.relationships.map( 60 | function(r) { 61 | return { id: r.id, start:r.startNode, end:r.endNode, type:r.type } } 62 | )); 63 | }); 64 | cb(null,{table:rows,graph:{nodes:nodes, links:rels},labels:labels}); 65 | } 66 | } 67 | }); 68 | } 69 | }; 70 | return me; 71 | } 72 | -------------------------------------------------------------------------------- /scripts/neod3-visualization.js: -------------------------------------------------------------------------------- 1 | function Neod3Renderer() { 2 | 3 | var styleContents = 4 | "node {\ 5 | diameter: 40px;\ 6 | color: #DFE1E3;\ 7 | border-color: #D4D6D7;\ 8 | border-width: 2px;\ 9 | text-color-internal: #000000;\ 10 | text-color-external: #000000;\ 11 | caption: '{name}';\ 12 | font-size: 12px;\ 13 | }\ 14 | relationship {\ 15 | color: #4356C0;\ 16 | shaft-width: 3px;\ 17 | font-size: 9px;\ 18 | padding: 3px;\ 19 | text-color-external: #000000;\ 20 | text-color-internal: #FFFFFF;\ 21 | }\n"; 22 | 23 | var skip = ["id", "start", "end", "source", "target", "labels", "type", "selected","properties"]; 24 | var prio_props = ["name", "title", "tag", "username", "lastname","caption"]; 25 | 26 | var serializer = null; 27 | 28 | var $downloadSvgLink = $(' Download SVG').hide().click(function () { 29 | $downloadSvgLink.hide(); 30 | }); 31 | var downloadSvgLink = $downloadSvgLink[0]; 32 | var blobSupport = 'Blob' in window; 33 | var URLSupport = 'URL' in window && 'createObjectURL' in window.URL; 34 | var msBlobSupport = typeof window.navigator.msSaveOrOpenBlob !== 'undefined'; 35 | var svgStyling = ''; 36 | var stylingUrl = window.location.hostname === 'www.neo4j.org' ? 'http://gist.neo4j.org/css/neod3' : 'styles/neod3'; 37 | if (window.isInternetExplorer) { 38 | stylingUrl += '-ie.css'; 39 | } else { 40 | stylingUrl += '.css'; 41 | } 42 | 43 | var existingStyles = {}; 44 | var currentColor = 1; 45 | 46 | function dummyFunc() { 47 | } 48 | 49 | function render(id, $container, visualization) { 50 | function extract_props(pc) { 51 | var p = {}; 52 | for (var key in pc) { 53 | if (!pc.hasOwnProperty(key) || skip.indexOf(key) != -1) continue; 54 | p[key] = pc[key]; 55 | } 56 | return p; 57 | } 58 | 59 | function node_styles(nodes) { 60 | function label(n) { 61 | var labels = n["labels"]; 62 | if (labels && labels.length) { 63 | return labels[labels.length - 1]; 64 | } 65 | return ""; 66 | } 67 | 68 | var style = {}; 69 | for (var i = 0; i < nodes.length; i++) { 70 | var props= nodes[i].properties = extract_props(nodes[i]); 71 | var keys = Object.keys(props); 72 | if (label(nodes[i]) !== "" && keys.length > 0) { 73 | var selected_keys = prio_props.filter(function (k) { 74 | return keys.indexOf(k) !== -1 75 | }); 76 | selected_keys = selected_keys.concat(keys).concat(['id']); 77 | var selector = "node." + label(nodes[i]); 78 | var selectedKey = selected_keys[0]; 79 | if (typeof(props[selectedKey]) === "string" && props[selectedKey].length > 30) { 80 | props[selectedKey] = props[selectedKey].substring(0,30)+" ..."; 81 | } 82 | style[selector] = style[selector] || selectedKey; 83 | } 84 | } 85 | return style; 86 | } 87 | function style_sheet(styles, styleContents) { 88 | function format(key) { 89 | var item=styles[key]; 90 | return item.selector + 91 | " {caption: '{" + item.caption + 92 | "}'; color: " + item.color + 93 | "; border-color: " + item['border-color'] + 94 | "; text-color-internal: " + item['text-color-internal'] + 95 | "; text-color-external: " + item['text-color-external'] + 96 | "; }" 97 | } 98 | return styleContents + Object.keys(styles).map(format).join("\n"); 99 | } 100 | function create_styles(styleCaptions, styles) { 101 | var colors = neo.style.defaults.colors; 102 | for (var selector in styleCaptions) { 103 | if (!(selector in styles)) { 104 | var color = colors[currentColor]; 105 | currentColor = (currentColor + 1) % colors.length; 106 | var textColor = window.isInternetExplorer ? '#000000' : color['text-color-internal']; 107 | var style = {selector:selector, caption:styleCaptions[selector], color:color.color, 108 | "border-color":color['border-color'], "text-color-internal":textColor,"text-color-external": textColor } 109 | styles[selector] = style; 110 | } 111 | } 112 | return styles; 113 | } 114 | 115 | function applyZoom() { 116 | renderer.select(".nodes").attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); 117 | renderer.select(".relationships").attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); 118 | } 119 | 120 | function enableZoomHandlers() { 121 | renderer.on("wheel.zoom",zoomHandlers.wheel); 122 | renderer.on("mousewheel.zoom",zoomHandlers.mousewheel); 123 | renderer.on("mousedown.zoom",zoomHandlers.mousedown); 124 | renderer.on("DOMMouseScroll.zoom",zoomHandlers.DOMMouseScroll); 125 | renderer.on("touchstart.zoom",zoomHandlers.touchstart); 126 | renderer.on("touchmove.zoom",zoomHandlers.touchmove); 127 | renderer.on("touchend.zoom",zoomHandlers.touchend); 128 | } 129 | 130 | function disableZoomHandlers() { 131 | renderer.on("wheel.zoom",null); 132 | renderer.on("mousewheel.zoom",null); 133 | renderer.on("mousedown.zoom", null); 134 | renderer.on("DOMMouseScroll.zoom", null); 135 | renderer.on("touchstart.zoom",null); 136 | renderer.on("touchmove.zoom",null); 137 | renderer.on("touchend.zoom",null); 138 | } 139 | 140 | function legend(svg, styles) { 141 | var keys = Object.keys(styles).sort(); 142 | var circles = svg.selectAll('circle.legend').data(keys); 143 | var r=20; 144 | circles.enter().append('circle').classed('legend', true).attr({ 145 | cx: 2*r, 146 | r : r 147 | }); 148 | circles.attr({ 149 | cy: function(node) { 150 | return (keys.indexOf(node)+1)*2.2*r; 151 | }, 152 | fill: function(node) { 153 | return styles[node]['color']; 154 | }, 155 | stroke: function(node) { 156 | return styles[node]['border-color']; 157 | }, 158 | 'stroke-width': function(node) { 159 | return "2px"; 160 | } 161 | }); 162 | var text = svg.selectAll('text.legend').data(keys); 163 | text.enter().append('text').classed('legend',true).attr({ 164 | 'text-anchor': 'left', 165 | 'font-weight': 'bold', 166 | 'stroke-width' : '0', 167 | 'stroke-color' : 'black', 168 | 'fill' : 'black', 169 | 'x' : 3.2*r, 170 | 'font-size' : "12px" 171 | }); 172 | text.text(function(node) { 173 | var label = styles[node].selector; 174 | return label ? label.substring(5) : ""; 175 | }).attr('y', function(node) { 176 | return (keys.indexOf(node)+1)*2.2*r+6; 177 | }) 178 | /* 179 | .attr('stroke', function(node) { 180 | return styles[node]['color']; 181 | }) 182 | .attr('fill', function(node) { 183 | return styles[node]['text-color-internal']; 184 | }); 185 | */ 186 | return circles.exit().remove(); 187 | } 188 | function keyHandler() { 189 | if (d3.event.altKey || d3.event.shiftKey) { 190 | enableZoomHandlers(); 191 | } 192 | else { 193 | disableZoomHandlers(); 194 | } 195 | } 196 | 197 | var links = visualization.links; 198 | var nodes = visualization.nodes; 199 | for (var i = 0; i < links.length; i++) { 200 | links[i].source = links[i].start; 201 | links[i].target = links[i].end; 202 | // links[i].properties = props(links[i]); 203 | } 204 | var nodeStyles = node_styles(nodes); 205 | create_styles(nodeStyles, existingStyles); 206 | var styleSheet = style_sheet(existingStyles, styleContents); 207 | var graphModel = neo.graphModel() 208 | .nodes(nodes) 209 | .relationships(links); 210 | var graphView = neo.graphView() 211 | .style(styleSheet) 212 | .width($container.width()).height($container.height()).on('nodeClicked', dummyFunc).on('relationshipClicked', dummyFunc).on('nodeDblClicked', dummyFunc); 213 | var svg = d3.select("#" + id).append("svg"); 214 | var renderer = svg.data([graphModel]); 215 | legend(svg,existingStyles); 216 | var zoomHandlers = {}; 217 | var zoomBehavior = d3.behavior.zoom().on("zoom", applyZoom).scaleExtent([0.2, 8]); 218 | 219 | renderer.call(graphView); 220 | renderer.call(zoomBehavior); 221 | 222 | zoomHandlers.wheel = renderer.on("wheel.zoom"); 223 | zoomHandlers.mousewheel = renderer.on("mousewheel.zoom"); 224 | zoomHandlers.mousedown = renderer.on("mousedown.zoom"); 225 | zoomHandlers.DOMMouseScroll = renderer.on("DOMMouseScroll.zoom"); 226 | zoomHandlers.touchstart = renderer.on("touchstart.zoom"); 227 | zoomHandlers.touchmove = renderer.on("touchmove.zoom") 228 | zoomHandlers.touchend = renderer.on("touchend.zoom"); 229 | disableZoomHandlers(); 230 | 231 | d3.select('body').on("keydown", keyHandler).on("keyup", keyHandler); 232 | 233 | function refresh() { 234 | graphView.height($container.height()); 235 | graphView.width($container.width()); 236 | renderer.call(graphView); 237 | } 238 | 239 | function saveToSvg() { 240 | var svgElement = $('#' + id).children('svg').first()[0]; 241 | var xml = serializeSvg(svgElement, $container); 242 | if (!msBlobSupport && downloadSvgLink.href !== '#') { 243 | window.URL.revokeObjectURL(downloadSvgLink.href); 244 | } 245 | var blob = new window.Blob([xml], { 246 | 'type': 'image/svg+xml' 247 | }); 248 | var fileName = id + '.svg'; 249 | if (!msBlobSupport) { 250 | downloadSvgLink.href = window.URL.createObjectURL(blob); 251 | $downloadSvgLink.appendTo($container).show(); 252 | $downloadSvgLink.attr('download', fileName); 253 | } else { 254 | window.navigator.msSaveOrOpenBlob(blob, fileName); 255 | } 256 | } 257 | 258 | function getFunctions() { 259 | var funcs = {}; 260 | if (blobSupport && (URLSupport || msBlobSupport)) { 261 | funcs['icon-download-alt'] = {'title': 'Save as SVG', 'func':saveToSvg}; 262 | } 263 | return funcs; 264 | } 265 | 266 | return { 267 | 'subscriptions': { 268 | 'expand': refresh, 269 | 'contract': refresh, 270 | 'sizeChange': refresh 271 | }, 272 | 'actions': getFunctions() 273 | }; 274 | } 275 | 276 | function serializeSvg(element, $container) { 277 | if (serializer === null) { 278 | if (typeof window.XMLSerializer !== 'undefined') { 279 | var xmlSerializer = new XMLSerializer(); 280 | serializer = function (emnt) { 281 | return xmlSerializer.serializeToString(emnt); 282 | }; 283 | } else { 284 | serializer = function (emnt) { 285 | return '' + $(emnt).html() + ''; 286 | } 287 | } 288 | } 289 | var svg = serializer(element); 290 | svg = svg.replace(''; 297 | $(svgStyling).appendTo('head'); 298 | }); 299 | 300 | return {'render': render}; 301 | } 302 | -------------------------------------------------------------------------------- /scripts/neod3.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (function(){ 3 | var __hasProp = {}.hasOwnProperty; 4 | 5 | window.neo = {}; 6 | 7 | neo.models = {}; 8 | 9 | neo.renderers = { 10 | node: [], 11 | relationship: [] 12 | }; 13 | 14 | neo.utils = { 15 | copy: function(src) { 16 | return JSON.parse(JSON.stringify(src)); 17 | }, 18 | extend: function(dest, src) { 19 | var k, v; 20 | if (!neo.utils.isObject(dest) && neo.utils.isObject(src)) { 21 | return; 22 | } 23 | for (k in src) { 24 | if (!__hasProp.call(src, k)) continue; 25 | v = src[k]; 26 | dest[k] = v; 27 | } 28 | return dest; 29 | }, 30 | isArray: Array.isArray || function(obj) { 31 | return Object.prototype.toString.call(obj) === '[object Array]'; 32 | }, 33 | isObject: function(obj) { 34 | return Object(obj) === obj; 35 | } 36 | }; 37 | 38 | var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 39 | __hasProp = {}.hasOwnProperty, 40 | __slice = [].slice; 41 | 42 | neo.models.Graph = (function() { 43 | function Graph(cypher) { 44 | this.removeRelationships = __bind(this.removeRelationships, this); 45 | this.removeNodes = __bind(this.removeNodes, this); 46 | this.findRelationship = __bind(this.findRelationship, this); 47 | this.findNode = __bind(this.findNode, this); 48 | this.addRelationships = __bind(this.addRelationships, this); 49 | this.addNodes = __bind(this.addNodes, this); 50 | this.nodeMap = {}; 51 | this.relationshipMap = {}; 52 | if (cypher) { 53 | this.addNodes(cypher.nodes); 54 | this.addRelationships(cypher.relationships); 55 | } 56 | } 57 | 58 | Graph.prototype.nodes = function() { 59 | var key, value, _ref, _results; 60 | _ref = this.nodeMap; 61 | _results = []; 62 | for (key in _ref) { 63 | if (!__hasProp.call(_ref, key)) continue; 64 | value = _ref[key]; 65 | _results.push(value); 66 | } 67 | return _results; 68 | }; 69 | 70 | Graph.prototype.relationships = function() { 71 | var key, value, _ref, _results; 72 | _ref = this.relationshipMap; 73 | _results = []; 74 | for (key in _ref) { 75 | if (!__hasProp.call(_ref, key)) continue; 76 | value = _ref[key]; 77 | _results.push(value); 78 | } 79 | return _results; 80 | }; 81 | 82 | Graph.prototype.addNodes = function(item) { 83 | var items, node, _base, _i, _len, _name; 84 | items = !neo.utils.isArray(item) ? [item] : item; 85 | for (_i = 0, _len = items.length; _i < _len; _i++) { 86 | item = items[_i]; 87 | node = !(item instanceof neo.models.Node) ? new neo.models.Node(item.id, item.labels, item.properties) : item; 88 | (_base = this.nodeMap)[_name = item.id] || (_base[_name] = node); 89 | } 90 | return this; 91 | }; 92 | Graph.prototype.addRelationships = function(item) { 93 | var items, source, target, _i, _len; 94 | items = !neo.utils.isArray(item) ? [item] : item; 95 | for (_i = 0, _len = items.length; _i < _len; _i++) { 96 | item = items[_i]; 97 | source = this.nodeMap[item.source] || (function() { 98 | throw "Invalid source"; 99 | })(); 100 | target = this.nodeMap[item.target] || (function() { 101 | throw "Invalid target"; 102 | })(); 103 | this.relationshipMap[item.id] = new neo.models.Relationship(item.id, source, target, item.type, item.properties); 104 | } 105 | return this; 106 | }; 107 | 108 | Graph.prototype.findNode = function(id) { 109 | return this.nodeMap[id]; 110 | }; 111 | 112 | Graph.prototype.findRelationship = function(id) { 113 | return this.relationshipMap[id]; 114 | }; 115 | 116 | Graph.prototype.merge = function(result) { 117 | this.addNodes(result.nodes); 118 | this.addRelationships(result.relationships); 119 | return this; 120 | }; 121 | 122 | Graph.prototype.removeNodes = function() { 123 | var id, rId, rel, rels, remove, _i, _len; 124 | remove = 1 <= arguments.length ? __slice.call(arguments, 0) : []; 125 | if (arguments.length === 0) { 126 | this.nodeMap = {}; 127 | this.relationshipMap = {}; 128 | return this; 129 | } 130 | remove = neo.utils.isArray(remove[0]) ? remove[0] : remove; 131 | for (_i = 0, _len = remove.length; _i < _len; _i++) { 132 | id = remove[_i]; 133 | rels = (function() { 134 | var _ref, _results; 135 | _ref = this.relationshipMap; 136 | _results = []; 137 | for (rId in _ref) { 138 | if (!__hasProp.call(_ref, rId)) continue; 139 | rel = _ref[rId]; 140 | if (rel.source.id === id || rel.target.id === id) { 141 | _results.push(rel.id); 142 | } 143 | } 144 | return _results; 145 | }).call(this); 146 | this.removeRelationships(rels); 147 | delete this.nodeMap[id]; 148 | } 149 | return this; 150 | }; 151 | 152 | Graph.prototype.removeRelationships = function() { 153 | var id, remove, _i, _len; 154 | remove = 1 <= arguments.length ? __slice.call(arguments, 0) : []; 155 | if (arguments.length === 0) { 156 | this.relationshipMap = {}; 157 | return this; 158 | } 159 | remove = neo.utils.isArray(remove[0]) ? remove[0] : remove; 160 | for (_i = 0, _len = remove.length; _i < _len; _i++) { 161 | id = remove[_i]; 162 | delete this.relationshipMap[id]; 163 | } 164 | return this; 165 | }; 166 | 167 | return Graph; 168 | 169 | })(); 170 | 171 | var NeoD3Geometry; 172 | 173 | NeoD3Geometry = (function() { 174 | var addShortenedNextWord, fitCaptionIntoCircle, noEmptyLines, square; 175 | 176 | square = function(distance) { 177 | return distance * distance; 178 | }; 179 | 180 | function NeoD3Geometry(style) { 181 | this.style = style; 182 | } 183 | 184 | addShortenedNextWord = function(line, word, measure) { 185 | var _results; 186 | _results = []; 187 | while (!(word.length <= 2)) { 188 | word = word.substr(0, word.length - 2) + '\u2026'; 189 | if (measure(word) < line.remainingWidth) { 190 | line.text += " " + word; 191 | break; 192 | } else { 193 | _results.push(void 0); 194 | } 195 | } 196 | return _results; 197 | }; 198 | 199 | noEmptyLines = function(lines) { 200 | var line, _i, _len; 201 | for (_i = 0, _len = lines.length; _i < _len; _i++) { 202 | line = lines[_i]; 203 | if (line.text.length === 0) { 204 | return false; 205 | } 206 | } 207 | return true; 208 | }; 209 | 210 | NeoD3Geometry.prototype.formatNodeCaptions = function(nodes) { 211 | var captionText, i, lines, node, template, words, _i, _j, _len, _ref, _results; 212 | var style = this.style; 213 | _results = []; 214 | for (_i = 0, _len = nodes.length; _i < _len; _i++) { 215 | node = nodes[_i]; 216 | template = style.forNode(node).get("caption"); 217 | captionText = style.interpolate(template, node.id, node.propertyMap); 218 | words = captionText.split(" "); 219 | lines = []; 220 | for (i = _j = 0, _ref = words.length - 1; 0 <= _ref ? _j <= _ref : _j >= _ref; i = 0 <= _ref ? ++_j : --_j) { 221 | lines.push({ 222 | node: node, 223 | text: words[i], 224 | baseline: (1 + i - words.length / 2) * 10 225 | }); 226 | } 227 | _results.push(node.caption = lines); 228 | } 229 | return _results; 230 | }; 231 | 232 | fitCaptionIntoCircle = function(node, style) { 233 | var candidateLines, candidateWords, captionText, consumedWords, emptyLine, fitOnFixedNumberOfLines, fontFamily, fontSize, lineCount, lineHeight, lines, maxLines, measure, template, words, _i, _ref, _ref1; 234 | template = style.forNode(node).get("caption"); 235 | captionText = style.interpolate(template, node.id, node.propertyMap); 236 | fontFamily = 'sans-serif'; 237 | fontSize = parseFloat(style.forNode(node).get('font-size')); 238 | lineHeight = fontSize; 239 | measure = function(text) { 240 | return neo.utils.measureText(text, fontFamily, fontSize); 241 | }; 242 | words = captionText.split(" "); 243 | emptyLine = function(lineCount, iLine) { 244 | var baseline, constainingHeight, lineWidth; 245 | baseline = (1 + iLine - lineCount / 2) * lineHeight; 246 | constainingHeight = iLine < lineCount / 2 ? baseline - lineHeight : baseline; 247 | lineWidth = Math.sqrt(square(node.radius) - square(constainingHeight)) * 2; 248 | return { 249 | node: node, 250 | text: '', 251 | baseline: baseline, 252 | remainingWidth: lineWidth 253 | }; 254 | }; 255 | fitOnFixedNumberOfLines = function(lineCount) { 256 | var iLine, iWord, line, lines, _i, _ref; 257 | lines = []; 258 | iWord = 0; 259 | for (iLine = _i = 0, _ref = lineCount - 1; 0 <= _ref ? _i <= _ref : _i >= _ref; iLine = 0 <= _ref ? ++_i : --_i) { 260 | line = emptyLine(lineCount, iLine); 261 | while (iWord < words.length && measure(" " + words[iWord]) < line.remainingWidth) { 262 | line.text += " " + words[iWord]; 263 | line.remainingWidth -= measure(" " + words[iWord]); 264 | iWord++; 265 | } 266 | lines.push(line); 267 | } 268 | if (iWord < words.length) { 269 | addShortenedNextWord(lines[lineCount - 1], words[iWord], measure); 270 | } 271 | return [lines, iWord]; 272 | }; 273 | consumedWords = 0; 274 | maxLines = node.radius * 2 / fontSize; 275 | lines = [emptyLine(1, 0)]; 276 | for (lineCount = _i = 1; 1 <= maxLines ? _i <= maxLines : _i >= maxLines; lineCount = 1 <= maxLines ? ++_i : --_i) { 277 | _ref = fitOnFixedNumberOfLines(lineCount), candidateLines = _ref[0], candidateWords = _ref[1]; 278 | if (noEmptyLines(candidateLines)) { 279 | _ref1 = [candidateLines, candidateWords], lines = _ref1[0], consumedWords = _ref1[1]; 280 | } 281 | if (consumedWords >= words.length) { 282 | return lines; 283 | } 284 | } 285 | return lines; 286 | }; 287 | 288 | // NeoD3Geometry.prototype.formatNodeCaptions = function(nodes) { 289 | // var node, _i, _len, _results; 290 | // _results = []; 291 | // for (_i = 0, _len = nodes.length; _i < _len; _i++) { 292 | // node = nodes[_i]; 293 | // _results.push(node.caption = fitCaptionIntoCircle(node, this.style)); 294 | // } 295 | // return _results; 296 | // }; 297 | 298 | NeoD3Geometry.prototype.measureRelationshipCaption = function(relationship, caption) { 299 | var fontFamily, fontSize, padding; 300 | fontFamily = 'sans-serif'; 301 | fontSize = parseFloat(this.style.forRelationship(relationship).get('font-size')); 302 | padding = parseFloat(this.style.forRelationship(relationship).get('padding')); 303 | return neo.utils.measureText(caption, fontFamily, fontSize) + padding * 2; 304 | }; 305 | 306 | NeoD3Geometry.prototype.captionFitsInsideArrowShaftWidth = function(relationship) { 307 | return parseFloat(this.style.forRelationship(relationship).get('shaft-width')) > parseFloat(this.style.forRelationship(relationship).get('font-size')); 308 | }; 309 | 310 | NeoD3Geometry.prototype.measureRelationshipCaptions = function(relationships) { 311 | var relationship, _i, _len, _results; 312 | _results = []; 313 | for (_i = 0, _len = relationships.length; _i < _len; _i++) { 314 | relationship = relationships[_i]; 315 | relationship.captionLength = this.measureRelationshipCaption(relationship, relationship.type); 316 | _results.push(relationship.captionLayout = this.captionFitsInsideArrowShaftWidth(relationship) ? "internal" : "external"); 317 | } 318 | return _results; 319 | }; 320 | 321 | NeoD3Geometry.prototype.shortenCaption = function(relationship, caption, targetWidth) { 322 | var shortCaption, width; 323 | shortCaption = caption; 324 | while (true) { 325 | if (shortCaption.length <= 2) { 326 | return ['', 0]; 327 | } 328 | shortCaption = shortCaption.substr(0, shortCaption.length - 2) + '\u2026'; 329 | width = this.measureRelationshipCaption(relationship, shortCaption); 330 | if (width < targetWidth) { 331 | return [shortCaption, width]; 332 | } 333 | } 334 | }; 335 | 336 | NeoD3Geometry.prototype.layoutRelationships = function(relationships) { 337 | var alongPath, dx, dy, endBreak, headHeight, headRadius, length, relationship, shaftLength, shaftRadius, startBreak, _i, _len, _ref, _results; 338 | _results = []; 339 | for (_i = 0, _len = relationships.length; _i < _len; _i++) { 340 | relationship = relationships[_i]; 341 | dx = relationship.target.x - relationship.source.x; 342 | dy = relationship.target.y - relationship.source.y; 343 | length = Math.sqrt(square(dx) + square(dy)); 344 | relationship.arrowLength = length - relationship.source.radius - relationship.target.radius; 345 | alongPath = function(from, distance) { 346 | return { 347 | x: from.x + dx * distance / length, 348 | y: from.y + dy * distance / length 349 | }; 350 | }; 351 | shaftRadius = (parseFloat(this.style.forRelationship(relationship).get('shaft-width')) / 2) || 2; 352 | headRadius = shaftRadius + 3; 353 | headHeight = headRadius * 2; 354 | shaftLength = relationship.arrowLength - headHeight; 355 | relationship.startPoint = alongPath(relationship.source, relationship.source.radius); 356 | relationship.endPoint = alongPath(relationship.target, -relationship.target.radius); 357 | relationship.midShaftPoint = alongPath(relationship.startPoint, shaftLength / 2); 358 | relationship.angle = Math.atan2(dy, dx) / Math.PI * 180; 359 | relationship.textAngle = relationship.angle; 360 | if (relationship.angle < -90 || relationship.angle > 90) { 361 | relationship.textAngle += 180; 362 | } 363 | _ref = shaftLength > relationship.captionLength ? [relationship.type, relationship.captionLength] : this.shortenCaption(relationship, relationship.type, shaftLength), relationship.shortCaption = _ref[0], relationship.shortCaptionLength = _ref[1]; 364 | if (relationship.captionLayout === "external") { 365 | startBreak = (shaftLength - relationship.shortCaptionLength) / 2; 366 | endBreak = shaftLength - startBreak; 367 | _results.push(relationship.arrowOutline = ['M', 0, shaftRadius, 'L', startBreak, shaftRadius, 'L', startBreak, -shaftRadius, 'L', 0, -shaftRadius, 'Z', 'M', endBreak, shaftRadius, 'L', shaftLength, shaftRadius, 'L', shaftLength, headRadius, 'L', relationship.arrowLength, 0, 'L', shaftLength, -headRadius, 'L', shaftLength, -shaftRadius, 'L', endBreak, -shaftRadius, 'Z'].join(' ')); 368 | } else { 369 | _results.push(relationship.arrowOutline = ['M', 0, shaftRadius, 'L', shaftLength, shaftRadius, 'L', shaftLength, headRadius, 'L', relationship.arrowLength, 0, 'L', shaftLength, -headRadius, 'L', shaftLength, -shaftRadius, 'L', 0, -shaftRadius, 'Z'].join(' ')); 370 | } 371 | } 372 | return _results; 373 | }; 374 | 375 | NeoD3Geometry.prototype.setNodeRadii = function(nodes) { 376 | var node, _i, _len, _results; 377 | _results = []; 378 | for (_i = 0, _len = nodes.length; _i < _len; _i++) { 379 | node = nodes[_i]; 380 | _results.push(node.radius = parseFloat(this.style.forNode(node).get("diameter")) / 2); 381 | } 382 | return _results; 383 | }; 384 | 385 | NeoD3Geometry.prototype.onGraphChange = function(graph) { 386 | this.setNodeRadii(graph.nodes()); 387 | this.formatNodeCaptions(graph.nodes()); 388 | return this.measureRelationshipCaptions(graph.relationships()); 389 | }; 390 | 391 | NeoD3Geometry.prototype.onTick = function(graph) { 392 | return this.layoutRelationships(graph.relationships()); 393 | }; 394 | 395 | return NeoD3Geometry; 396 | 397 | })(); 398 | 399 | var __slice = [].slice; 400 | 401 | neo.graphModel = function() { 402 | var graph, model; 403 | graph = new neo.models.Graph(); 404 | model = function() {}; 405 | model.callbacks = {}; 406 | model.trigger = function() { 407 | var args, callback, event, _i, _len, _ref, _results; 408 | event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 409 | event = 'updated'; 410 | if (model.callbacks[event]) { 411 | _ref = model.callbacks[event]; 412 | _results = []; 413 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 414 | callback = _ref[_i]; 415 | _results.push(callback.apply(null, args)); 416 | } 417 | return _results; 418 | } 419 | }; 420 | model.nodes = function(items) { 421 | if (items == null) { 422 | return graph.nodes(); 423 | } 424 | graph.removeNodes().addNodes(items); 425 | model.trigger('nodesAdded'); 426 | return model; 427 | }; 428 | model.nodes.add = function(items) { 429 | if (items != null) { 430 | graph.addNodes(items); 431 | model.trigger('nodesAdded'); 432 | } 433 | return model; 434 | }; 435 | model.nodes.find = graph.findNode; 436 | model.nodes.remove = function() { 437 | graph.removeNodes.apply(null, arguments); 438 | model.trigger('nodesRemoved'); 439 | return model; 440 | }; 441 | model.relationships = function(items) { 442 | if (items == null) { 443 | return graph.relationships(); 444 | } 445 | graph.removeRelationships().addRelationships(items); 446 | model.trigger('relationshipsAdded'); 447 | return model; 448 | }; 449 | model.relationships.add = function(items) { 450 | if (items != null) { 451 | graph.addRelationships(items); 452 | model.trigger('relationshipsAdded'); 453 | } 454 | return model; 455 | }; 456 | model.relationships.find = graph.findRelationship; 457 | model.relationships.remove = function() { 458 | graph.removeRelationships.apply(null, arguments); 459 | model.trigger('relationshipsRemoved'); 460 | return model; 461 | }; 462 | model.on = function(event, callback) { 463 | var _base; 464 | ((_base = model.callbacks)[event] != null ? _base[event] : _base[event] = []).push(callback); 465 | return model; 466 | }; 467 | return model; 468 | }; 469 | 470 | var __slice = [].slice; 471 | 472 | neo.graphView = function() { 473 | var callbacks, chart, layout, style, trigger, viz; 474 | layout = neo.layout.force(); 475 | style = neo.style(); 476 | viz = null; 477 | callbacks = {}; 478 | trigger = function() { 479 | var args, callback, event, _i, _len, _ref, _results; 480 | event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 481 | _ref = callbacks[event]; 482 | _results = []; 483 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 484 | callback = _ref[_i]; 485 | _results.push(callback.apply(null, args)); 486 | } 487 | return _results; 488 | }; 489 | chart = function(selection) { 490 | selection.each(function(graphModel) { 491 | if (!viz) { 492 | viz = neo.viz(this, graphModel, layout, style); 493 | graphModel.on('updated', function() { 494 | return viz.update(); 495 | }); 496 | viz.trigger = trigger; 497 | } 498 | return viz.update(); 499 | }); 500 | }; 501 | chart.on = function(event, callback) { 502 | (callbacks[event] != null ? callbacks[event] : callbacks[event] = []).push(callback); 503 | return chart; 504 | }; 505 | chart.layout = function(value) { 506 | if (!arguments.length) { 507 | return layout; 508 | } 509 | layout = value; 510 | return chart; 511 | }; 512 | chart.style = function(value) { 513 | if (!arguments.length) { 514 | return style.toSheet(); 515 | } 516 | style.importGrass(value); 517 | return chart; 518 | }; 519 | chart.width = function(value) { 520 | if (!arguments.length) { 521 | return viz.width; 522 | } 523 | return chart; 524 | }; 525 | chart.height = function(value) { 526 | if (!arguments.length) { 527 | return viz.height; 528 | } 529 | return chart; 530 | }; 531 | chart.update = function() { 532 | viz.update(); 533 | return chart; 534 | }; 535 | return chart; 536 | }; 537 | 538 | neo.layout = (function() { 539 | var _layout; 540 | _layout = {}; 541 | _layout.force = function() { 542 | var _force; 543 | _force = {}; 544 | _force.init = function(render) { 545 | var accelerateLayout, d3force, forceLayout, linkDistance; 546 | forceLayout = {}; 547 | linkDistance = 60; 548 | d3force = d3.layout.force().linkDistance(linkDistance).charge(-1000).gravity(0.3); 549 | accelerateLayout = function() { 550 | var d3Tick, maxAnimationFramesPerSecond, maxComputeTime, maxStepsPerTick, now; 551 | maxStepsPerTick = 100; 552 | maxAnimationFramesPerSecond = 60; 553 | maxComputeTime = 1000 / maxAnimationFramesPerSecond; 554 | now = window.performance ? function() { 555 | return window.performance.now(); 556 | } : function() { 557 | return Date.now(); 558 | }; 559 | d3Tick = d3force.tick; 560 | return d3force.tick = (function(_this) { 561 | return function() { 562 | var startTick, step; 563 | startTick = now(); 564 | step = maxStepsPerTick; 565 | while (step-- && now() - startTick < maxComputeTime) { 566 | if (d3Tick()) { 567 | maxStepsPerTick = 2; 568 | return true; 569 | } 570 | } 571 | render(); 572 | return false; 573 | }; 574 | })(this); 575 | }; 576 | accelerateLayout(); 577 | forceLayout.update = function(graph, size) { 578 | var center, nodes, radius, relationships; 579 | nodes = graph.nodes(); 580 | relationships = graph.relationships(); 581 | radius = nodes.length * linkDistance / (Math.PI * 2); 582 | center = { 583 | x: size[0] / 2, 584 | y: size[1] / 2 585 | }; 586 | neo.utils.circularLayout(nodes, center, radius); 587 | return d3force.nodes(nodes).links(relationships).size(size).start(); 588 | }; 589 | forceLayout.drag = d3force.drag; 590 | return forceLayout; 591 | }; 592 | return _force; 593 | }; 594 | return _layout; 595 | })(); 596 | 597 | var __hasProp = {}.hasOwnProperty; 598 | 599 | neo.models.Node = (function() { 600 | function Node(id, labels, properties) { 601 | var key, value; 602 | this.id = id; 603 | this.labels = labels; 604 | this.propertyMap = properties; 605 | this.propertyList = (function() { 606 | var _results; 607 | _results = []; 608 | for (key in properties) { 609 | if (!__hasProp.call(properties, key)) continue; 610 | value = properties[key]; 611 | _results.push({ 612 | key: key, 613 | value: value 614 | }); 615 | } 616 | return _results; 617 | })(); 618 | } 619 | 620 | Node.prototype.toJSON = function() { 621 | return this.propertyMap; 622 | }; 623 | 624 | Node.prototype.isNode = true; 625 | 626 | Node.prototype.isRelationship = false; 627 | 628 | return Node; 629 | 630 | })(); 631 | 632 | var __hasProp = {}.hasOwnProperty; 633 | 634 | neo.models.Relationship = (function() { 635 | function Relationship(id, source, target, type, properties) { 636 | var key, value; 637 | this.id = id; 638 | this.source = source; 639 | this.target = target; 640 | this.type = type; 641 | this.propertyMap = properties; 642 | this.propertyList = (function() { 643 | var _ref, _results; 644 | _ref = this.propertyMap; 645 | _results = []; 646 | for (key in _ref) { 647 | if (!__hasProp.call(_ref, key)) continue; 648 | value = _ref[key]; 649 | _results.push({ 650 | key: key, 651 | value: value 652 | }); 653 | } 654 | return _results; 655 | }).call(this); 656 | } 657 | 658 | Relationship.prototype.toJSON = function() { 659 | return this.propertyMap; 660 | }; 661 | 662 | Relationship.prototype.isNode = false; 663 | 664 | Relationship.prototype.isRelationship = true; 665 | 666 | return Relationship; 667 | 668 | })(); 669 | 670 | neo.Renderer = (function() { 671 | function Renderer(opts) { 672 | if (opts == null) { 673 | opts = {}; 674 | } 675 | neo.utils.extend(this, opts); 676 | if (this.onGraphChange == null) { 677 | this.onGraphChange = function() {}; 678 | } 679 | if (this.onTick == null) { 680 | this.onTick = function() {}; 681 | } 682 | } 683 | 684 | return Renderer; 685 | 686 | })(); 687 | 688 | neo.style = (function() { 689 | var GraphStyle, Selector, StyleElement, StyleRule, _style; 690 | _style = function(storage) { 691 | return new GraphStyle(storage); 692 | }; 693 | _style.defaults = { 694 | autoColor: true, 695 | colors: [ 696 | { 697 | color: '#DFE1E3', 698 | 'border-color': '#D4D6D7', 699 | 'text-color-internal': '#000000' 700 | }, { 701 | color: '#F25A29', 702 | 'border-color': '#DC4717', 703 | 'text-color-internal': '#FFFFFF' 704 | }, { 705 | color: '#AD62CE', 706 | 'border-color': '#9453B1', 707 | 'text-color-internal': '#FFFFFF' 708 | }, { 709 | color: '#30B6AF', 710 | 'border-color': '#46A39E', 711 | 'text-color-internal': '#FFFFFF' 712 | }, { 713 | color: '#FCC940', 714 | 'border-color': '#F3BA25', 715 | 'text-color-internal': '#000000' 716 | }, { 717 | color: '#4356C0', 718 | 'border-color': '#3445A2', 719 | 'text-color-internal': '#FFFFFF' 720 | }, { 721 | color: '#FF6C7C', 722 | 'border-color': '#EB5D6C', 723 | 'text-color-internal': '#FFFFFF' 724 | }, { 725 | color: '#a2cf81', 726 | 'border-color': '#9bbd82', 727 | 'text-color-internal': '#000000' 728 | }, { 729 | color: '#f79235', 730 | 'border-color': '#e68f40', 731 | 'text-color-internal': '#000000' 732 | }, { 733 | color: '#785cc7', 734 | 'border-color': '#625096', 735 | 'text-color-internal': '#FFFFFF' 736 | }, { 737 | color: '#d05e7c', 738 | 'border-color': '#b05b72', 739 | 'text-color-internal': '#FFFFFF' 740 | }, { 741 | color: '#3986b7', 742 | 'border-color': '#3a7499', 743 | 'text-color-internal': '#FFFFFF' 744 | } 745 | ], 746 | style: { 747 | 'node': { 748 | 'diameter': '40px', 749 | 'color': '#DFE1E3', 750 | 'border-color': '#D4D6D7', 751 | 'border-width': '2px', 752 | 'text-color-internal': '#000000', 753 | 'caption': '{id}', 754 | 'font-size': '10px' 755 | }, 756 | 'relationship': { 757 | 'color': '#D4D6D7', 758 | 'shaft-width': '1px', 759 | 'font-size': '8px', 760 | 'padding': '3px', 761 | 'text-color-external': '#000000', 762 | 'text-color-internal': '#FFFFFF' 763 | } 764 | }, 765 | sizes: [ 766 | { 767 | diameter: '10px' 768 | }, { 769 | diameter: '20px' 770 | }, { 771 | diameter: '30px' 772 | }, { 773 | diameter: '50px' 774 | }, { 775 | diameter: '80px' 776 | } 777 | ], 778 | arrayWidths: [ 779 | { 780 | 'shaft-width': '1px' 781 | }, { 782 | 'shaft-width': '2px' 783 | }, { 784 | 'shaft-width': '3px' 785 | }, { 786 | 'shaft-width': '5px' 787 | }, { 788 | 'shaft-width': '8px' 789 | }, { 790 | 'shaft-width': '13px' 791 | }, { 792 | 'shaft-width': '25px' 793 | }, { 794 | 'shaft-width': '38px' 795 | } 796 | ] 797 | }; 798 | Selector = (function() { 799 | function Selector(selector) { 800 | var _ref; 801 | _ref = selector.indexOf('.') > 0 ? selector.split('.') : [selector, void 0], this.tag = _ref[0], this.klass = _ref[1]; 802 | } 803 | 804 | Selector.prototype.toString = function() { 805 | var str; 806 | str = this.tag; 807 | if (this.klass != null) { 808 | str += "." + this.klass; 809 | } 810 | return str; 811 | }; 812 | 813 | return Selector; 814 | 815 | })(); 816 | StyleRule = (function() { 817 | function StyleRule(selector, props) { 818 | this.selector = selector; 819 | this.props = props; 820 | } 821 | 822 | StyleRule.prototype.matches = function(selector) { 823 | if (this.selector.tag === selector.tag) { 824 | if (this.selector.klass === selector.klass || !this.selector.klass) { 825 | return true; 826 | } 827 | } 828 | return false; 829 | }; 830 | 831 | StyleRule.prototype.matchesExact = function(selector) { 832 | return this.selector.tag === selector.tag && this.selector.klass === selector.klass; 833 | }; 834 | 835 | return StyleRule; 836 | 837 | })(); 838 | StyleElement = (function() { 839 | function StyleElement(selector, data) { 840 | this.data = data; 841 | this.selector = selector; 842 | this.props = {}; 843 | } 844 | 845 | StyleElement.prototype.applyRules = function(rules) { 846 | var rule, _i, _j, _len, _len1; 847 | for (_i = 0, _len = rules.length; _i < _len; _i++) { 848 | rule = rules[_i]; 849 | if (!(rule.matches(this.selector))) { 850 | continue; 851 | } 852 | neo.utils.extend(this.props, rule.props); 853 | break; 854 | } 855 | for (_j = 0, _len1 = rules.length; _j < _len1; _j++) { 856 | rule = rules[_j]; 857 | if (!(rule.matchesExact(this.selector))) { 858 | continue; 859 | } 860 | neo.utils.extend(this.props, rule.props); 861 | break; 862 | } 863 | return this; 864 | }; 865 | 866 | StyleElement.prototype.get = function(attr) { 867 | return this.props[attr] || ''; 868 | }; 869 | 870 | return StyleElement; 871 | 872 | })(); 873 | GraphStyle = (function() { 874 | function GraphStyle(storage) { 875 | this.storage = storage; 876 | this.rules = []; 877 | this.loadRules(); 878 | } 879 | 880 | GraphStyle.prototype.selector = function(item) { 881 | if (item.isNode) { 882 | return this.nodeSelector(item); 883 | } else if (item.isRelationship) { 884 | return this.relationshipSelector(item); 885 | } 886 | }; 887 | 888 | GraphStyle.prototype.calculateStyle = function(selector, data) { 889 | return new StyleElement(selector, data).applyRules(this.rules); 890 | }; 891 | 892 | GraphStyle.prototype.forEntity = function(item) { 893 | return this.calculateStyle(this.selector(item), item); 894 | }; 895 | 896 | GraphStyle.prototype.forNode = function(node) { 897 | var selector, _ref; 898 | if (node == null) { 899 | node = {}; 900 | } 901 | selector = this.nodeSelector(node); 902 | if (((_ref = node.labels) != null ? _ref.length : void 0) > 0) { 903 | this.setDefaultStyling(selector); 904 | } 905 | return this.calculateStyle(selector, node); 906 | }; 907 | 908 | GraphStyle.prototype.forRelationship = function(rel) { 909 | return this.calculateStyle(this.relationshipSelector(rel), rel); 910 | }; 911 | 912 | GraphStyle.prototype.findAvailableDefaultColor = function() { 913 | var defaultColor, rule, usedColors, _i, _j, _len, _len1, _ref, _ref1; 914 | usedColors = {}; 915 | _ref = this.rules; 916 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 917 | rule = _ref[_i]; 918 | if (rule.props.color != null) { 919 | usedColors[rule.props.color] = true; 920 | } 921 | } 922 | _ref1 = _style.defaults.colors; 923 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 924 | defaultColor = _ref1[_j]; 925 | if (usedColors[defaultColor.color] == null) { 926 | return neo.utils.copy(defaultColor); 927 | } 928 | } 929 | return neo.utils.copy(_style.defaults.colors[0]); 930 | }; 931 | 932 | GraphStyle.prototype.setDefaultStyling = function(selector) { 933 | var rule; 934 | rule = this.findRule(selector); 935 | if (_style.defaults.autoColor && (rule == null)) { 936 | rule = new StyleRule(selector, this.findAvailableDefaultColor()); 937 | this.rules.push(rule); 938 | return this.persist(); 939 | } 940 | }; 941 | 942 | GraphStyle.prototype.change = function(item, props) { 943 | var rule, selector; 944 | selector = this.selector(item); 945 | rule = this.findRule(selector); 946 | if (rule == null) { 947 | rule = new StyleRule(selector, {}); 948 | this.rules.push(rule); 949 | } 950 | neo.utils.extend(rule.props, props); 951 | this.persist(); 952 | return rule; 953 | }; 954 | 955 | GraphStyle.prototype.destroyRule = function(rule) { 956 | var idx; 957 | idx = this.rules.indexOf(rule); 958 | if (idx != null) { 959 | this.rules.splice(idx, 1); 960 | } 961 | return this.persist(); 962 | }; 963 | 964 | GraphStyle.prototype.findRule = function(selector) { 965 | var r, rule, _i, _len, _ref; 966 | _ref = this.rules; 967 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 968 | r = _ref[_i]; 969 | if (r.matchesExact(selector)) { 970 | rule = r; 971 | } 972 | } 973 | return rule; 974 | }; 975 | 976 | GraphStyle.prototype.nodeSelector = function(node) { 977 | var selector, _ref; 978 | if (node == null) { 979 | node = {}; 980 | } 981 | selector = 'node'; 982 | if (((_ref = node.labels) != null ? _ref.length : void 0) > 0) { 983 | selector += "." + node.labels[0]; 984 | } 985 | return new Selector(selector); 986 | }; 987 | 988 | GraphStyle.prototype.relationshipSelector = function(rel) { 989 | var selector; 990 | if (rel == null) { 991 | rel = {}; 992 | } 993 | selector = 'relationship'; 994 | if (rel.type != null) { 995 | selector += "." + rel.type; 996 | } 997 | return new Selector(selector); 998 | }; 999 | 1000 | GraphStyle.prototype.importGrass = function(string) { 1001 | var e, rules; 1002 | try { 1003 | rules = this.parse(string); 1004 | this.loadRules(rules); 1005 | return this.persist(); 1006 | } catch (_error) { 1007 | e = _error; 1008 | } 1009 | }; 1010 | 1011 | GraphStyle.prototype.loadRules = function(data) { 1012 | var props, rule; 1013 | if (!neo.utils.isObject(data)) { 1014 | data = _style.defaults.style; 1015 | } 1016 | this.rules.length = 0; 1017 | for (rule in data) { 1018 | props = data[rule]; 1019 | this.rules.push(new StyleRule(new Selector(rule), neo.utils.copy(props))); 1020 | } 1021 | return this; 1022 | }; 1023 | 1024 | GraphStyle.prototype.parse = function(string) { 1025 | var c, chars, insideProps, insideString, k, key, keyword, prop, props, rules, skipThis, v, val, _i, _j, _len, _len1, _ref, _ref1; 1026 | chars = string.split(''); 1027 | insideString = false; 1028 | insideProps = false; 1029 | keyword = ""; 1030 | props = ""; 1031 | rules = {}; 1032 | for (_i = 0, _len = chars.length; _i < _len; _i++) { 1033 | c = chars[_i]; 1034 | skipThis = true; 1035 | switch (c) { 1036 | case "{": 1037 | if (!insideString) { 1038 | insideProps = true; 1039 | } else { 1040 | skipThis = false; 1041 | } 1042 | break; 1043 | case "}": 1044 | if (!insideString) { 1045 | insideProps = false; 1046 | rules[keyword] = props; 1047 | keyword = ""; 1048 | props = ""; 1049 | } else { 1050 | skipThis = false; 1051 | } 1052 | break; 1053 | case "'": 1054 | case "\"": 1055 | insideString ^= true; 1056 | break; 1057 | default: 1058 | skipThis = false; 1059 | } 1060 | if (skipThis) { 1061 | continue; 1062 | } 1063 | if (insideProps) { 1064 | props += c; 1065 | } else { 1066 | if (!c.match(/[\s\n]/)) { 1067 | keyword += c; 1068 | } 1069 | } 1070 | } 1071 | for (k in rules) { 1072 | v = rules[k]; 1073 | rules[k] = {}; 1074 | _ref = v.split(';'); 1075 | for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { 1076 | prop = _ref[_j]; 1077 | _ref1 = prop.split(':'), key = _ref1[0], val = _ref1[1]; 1078 | if (!(key && val)) { 1079 | continue; 1080 | } 1081 | rules[k][key != null ? key.trim() : void 0] = val != null ? val.trim() : void 0; 1082 | } 1083 | } 1084 | return rules; 1085 | }; 1086 | 1087 | GraphStyle.prototype.persist = function() { 1088 | var _ref; 1089 | return (_ref = this.storage) != null ? _ref.add('grass', JSON.stringify(this.toSheet())) : void 0; 1090 | }; 1091 | 1092 | GraphStyle.prototype.resetToDefault = function() { 1093 | this.loadRules(); 1094 | return this.persist(); 1095 | }; 1096 | 1097 | GraphStyle.prototype.toSheet = function() { 1098 | var rule, sheet, _i, _len, _ref; 1099 | sheet = {}; 1100 | _ref = this.rules; 1101 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1102 | rule = _ref[_i]; 1103 | sheet[rule.selector.toString()] = rule.props; 1104 | } 1105 | return sheet; 1106 | }; 1107 | 1108 | GraphStyle.prototype.toString = function() { 1109 | var k, r, str, v, _i, _len, _ref, _ref1; 1110 | str = ""; 1111 | _ref = this.rules; 1112 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1113 | r = _ref[_i]; 1114 | str += r.selector.toString() + " {\n"; 1115 | _ref1 = r.props; 1116 | for (k in _ref1) { 1117 | v = _ref1[k]; 1118 | if (k === "caption") { 1119 | v = "'" + v + "'"; 1120 | } 1121 | str += " " + k + ": " + v + ";\n"; 1122 | } 1123 | str += "}\n\n"; 1124 | } 1125 | return str; 1126 | }; 1127 | 1128 | GraphStyle.prototype.nextDefaultColor = 0; 1129 | 1130 | GraphStyle.prototype.defaultColors = function() { 1131 | return neo.utils.copy(_style.defaults.colors); 1132 | }; 1133 | 1134 | GraphStyle.prototype.interpolate = function(str, id, properties) { 1135 | return str.replace(/\{([^{}]*)\}/g, function(a, b) { 1136 | var r; 1137 | r = properties[b] || id; 1138 | if (typeof r === 'string' || typeof r === 'number') { 1139 | return r; 1140 | } else { 1141 | return a; 1142 | } 1143 | }); 1144 | }; 1145 | 1146 | return GraphStyle; 1147 | 1148 | })(); 1149 | return _style; 1150 | })(); 1151 | 1152 | var __slice = [].slice; 1153 | 1154 | neo.viz = function(el, graph, layout, style) { 1155 | var clickHandler, force, geometry, onNodeClick, onNodeDblClick, onRelationshipClick, render, viz; 1156 | viz = { 1157 | style: style 1158 | }; 1159 | el = d3.select(el); 1160 | geometry = new NeoD3Geometry(style); 1161 | viz.trigger = function() { 1162 | var args, event; 1163 | event = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 1164 | }; 1165 | onNodeClick = (function(_this) { 1166 | return function(node) { 1167 | return viz.trigger('nodeClicked', node); 1168 | }; 1169 | })(this); 1170 | onNodeDblClick = (function(_this) { 1171 | return function(node) { 1172 | return viz.trigger('nodeDblClicked', node); 1173 | }; 1174 | })(this); 1175 | onRelationshipClick = (function(_this) { 1176 | return function(relationship) { 1177 | return viz.trigger('relationshipClicked', relationship); 1178 | }; 1179 | })(this); 1180 | render = function() { 1181 | var nodeGroups, relationshipGroups, renderer, _i, _j, _len, _len1, _ref, _ref1, _results; 1182 | geometry.onTick(graph); 1183 | nodeGroups = el.selectAll("g.node").attr("transform", function(node) { 1184 | return "translate(" + node.x + "," + node.y + ")"; 1185 | }); 1186 | _ref = neo.renderers.node; 1187 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1188 | renderer = _ref[_i]; 1189 | nodeGroups.call(renderer.onTick, viz); 1190 | } 1191 | relationshipGroups = el.selectAll("g.relationship"); 1192 | _ref1 = neo.renderers.relationship; 1193 | _results = []; 1194 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 1195 | renderer = _ref1[_j]; 1196 | _results.push(relationshipGroups.call(renderer.onTick, viz)); 1197 | } 1198 | return _results; 1199 | }; 1200 | force = layout.init(render); 1201 | viz.update = function() { 1202 | var height, layers, nodeGroups, nodes, relationshipGroups, relationships, renderer, width, _i, _j, _len, _len1, _ref, _ref1; 1203 | if (!graph) { 1204 | return; 1205 | } 1206 | height = (function() { 1207 | try { 1208 | return parseInt(el.style('height').replace('px', '')); 1209 | } catch (_error) {} 1210 | })(); 1211 | width = (function() { 1212 | try { 1213 | return parseInt(el.style('width').replace('px', '')); 1214 | } catch (_error) {} 1215 | })(); 1216 | layers = el.selectAll("g.layer").data(["relationships", "nodes"]); 1217 | layers.enter().append("g").attr("class", function(d) { 1218 | return "layer " + d; 1219 | }); 1220 | nodes = graph.nodes(); 1221 | relationships = graph.relationships(); 1222 | relationshipGroups = el.select("g.layer.relationships").selectAll("g.relationship").data(relationships, function(d) { 1223 | return d.id; 1224 | }); 1225 | relationshipGroups.enter().append("g").attr("class", "relationship").on("click", onRelationshipClick); 1226 | geometry.onGraphChange(graph); 1227 | _ref = neo.renderers.relationship; 1228 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 1229 | renderer = _ref[_i]; 1230 | relationshipGroups.call(renderer.onGraphChange, viz); 1231 | } 1232 | relationshipGroups.exit().remove(); 1233 | nodeGroups = el.select("g.layer.nodes").selectAll("g.node").data(nodes, function(d) { 1234 | return d.id; 1235 | }); 1236 | nodeGroups.enter().append("g").attr("class", "node").call(force.drag).call(clickHandler); 1237 | _ref1 = neo.renderers.node; 1238 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { 1239 | renderer = _ref1[_j]; 1240 | nodeGroups.call(renderer.onGraphChange, viz); 1241 | } 1242 | nodeGroups.exit().remove(); 1243 | return force.update(graph, [width, height]); 1244 | }; 1245 | clickHandler = neo.utils.clickHandler(); 1246 | clickHandler.on('click', onNodeClick); 1247 | clickHandler.on('dblclick', onNodeDblClick); 1248 | return viz; 1249 | }; 1250 | 1251 | neo.utils.circularLayout = function(nodes, center, radius) { 1252 | var i, n, unlocatedNodes, _i, _len, _results; 1253 | unlocatedNodes = nodes.filter(function(node) { 1254 | return !((node.x != null) && (node.y != null)); 1255 | }); 1256 | _results = []; 1257 | for (i = _i = 0, _len = unlocatedNodes.length; _i < _len; i = ++_i) { 1258 | n = unlocatedNodes[i]; 1259 | n.x = center.x + radius * Math.sin(2 * Math.PI * i / unlocatedNodes.length); 1260 | _results.push(n.y = center.y + radius * Math.cos(2 * Math.PI * i / unlocatedNodes.length)); 1261 | } 1262 | return _results; 1263 | }; 1264 | 1265 | neo.utils.distributeCircular = function(arrowAngles, minSeparation) { 1266 | var angle, center, expand, i, key, length, list, rawAngle, result, run, runLength, runsOfTooDenseArrows, tooDense, wrapAngle, wrapIndex, _i, _j, _k, _len, _ref, _ref1, _ref2, _ref3; 1267 | list = []; 1268 | _ref = arrowAngles.floating; 1269 | for (key in _ref) { 1270 | angle = _ref[key]; 1271 | list.push({ 1272 | key: key, 1273 | angle: angle 1274 | }); 1275 | } 1276 | list.sort(function(a, b) { 1277 | return a.angle - b.angle; 1278 | }); 1279 | runsOfTooDenseArrows = []; 1280 | length = function(startIndex, endIndex) { 1281 | if (startIndex < endIndex) { 1282 | return endIndex - startIndex + 1; 1283 | } else { 1284 | return endIndex + list.length - startIndex + 1; 1285 | } 1286 | }; 1287 | angle = function(startIndex, endIndex) { 1288 | if (startIndex < endIndex) { 1289 | return list[endIndex].angle - list[startIndex].angle; 1290 | } else { 1291 | return 360 - (list[startIndex].angle - list[endIndex].angle); 1292 | } 1293 | }; 1294 | tooDense = function(startIndex, endIndex) { 1295 | return angle(startIndex, endIndex) < length(startIndex, endIndex) * minSeparation; 1296 | }; 1297 | wrapIndex = function(index) { 1298 | if (index === -1) { 1299 | return list.length - 1; 1300 | } else if (index >= list.length) { 1301 | return index - list.length; 1302 | } else { 1303 | return index; 1304 | } 1305 | }; 1306 | wrapAngle = function(angle) { 1307 | if (angle >= 360) { 1308 | return angle - 360; 1309 | } else { 1310 | return angle; 1311 | } 1312 | }; 1313 | expand = function(startIndex, endIndex) { 1314 | if (length(startIndex, endIndex) < list.length) { 1315 | if (tooDense(startIndex, wrapIndex(endIndex + 1))) { 1316 | return expand(startIndex, wrapIndex(endIndex + 1)); 1317 | } 1318 | if (tooDense(wrapIndex(startIndex - 1), endIndex)) { 1319 | return expand(wrapIndex(startIndex - 1), endIndex); 1320 | } 1321 | } 1322 | return runsOfTooDenseArrows.push({ 1323 | start: startIndex, 1324 | end: endIndex 1325 | }); 1326 | }; 1327 | for (i = _i = 0, _ref1 = list.length - 2; 0 <= _ref1 ? _i <= _ref1 : _i >= _ref1; i = 0 <= _ref1 ? ++_i : --_i) { 1328 | if (tooDense(i, i + 1)) { 1329 | expand(i, i + 1); 1330 | } 1331 | } 1332 | result = {}; 1333 | for (_j = 0, _len = runsOfTooDenseArrows.length; _j < _len; _j++) { 1334 | run = runsOfTooDenseArrows[_j]; 1335 | center = list[run.start].angle + angle(run.start, run.end) / 2; 1336 | runLength = length(run.start, run.end); 1337 | for (i = _k = 0, _ref2 = runLength - 1; 0 <= _ref2 ? _k <= _ref2 : _k >= _ref2; i = 0 <= _ref2 ? ++_k : --_k) { 1338 | rawAngle = center + (i - (runLength - 1) / 2) * minSeparation; 1339 | result[list[wrapIndex(run.start + i)].key] = wrapAngle(rawAngle); 1340 | } 1341 | } 1342 | _ref3 = arrowAngles.floating; 1343 | for (key in _ref3) { 1344 | angle = _ref3[key]; 1345 | if (!result[key]) { 1346 | result[key] = arrowAngles.floating[key]; 1347 | } 1348 | } 1349 | return result; 1350 | }; 1351 | 1352 | neo.utils.clickHandler = function() { 1353 | var cc, event; 1354 | cc = function(selection) { 1355 | var dist, down, last, tolerance, wait; 1356 | dist = function(a, b) { 1357 | return Math.sqrt(Math.pow(a[0] - b[0], 2), Math.pow(a[1] - b[1], 2)); 1358 | }; 1359 | down = void 0; 1360 | tolerance = 5; 1361 | last = void 0; 1362 | wait = null; 1363 | selection.on("mousedown", function() { 1364 | d3.event.target.__data__.fixed = true; 1365 | down = d3.mouse(document.body); 1366 | return last = +new Date(); 1367 | }); 1368 | return selection.on("mouseup", function() { 1369 | if (dist(down, d3.mouse(document.body)) > tolerance) { 1370 | 1371 | } else { 1372 | if (wait) { 1373 | window.clearTimeout(wait); 1374 | wait = null; 1375 | return event.dblclick(d3.event.target.__data__); 1376 | } else { 1377 | return wait = window.setTimeout((function(e) { 1378 | return function() { 1379 | event.click(e.target.__data__); 1380 | return wait = null; 1381 | }; 1382 | })(d3.event), 250); 1383 | } 1384 | } 1385 | }); 1386 | }; 1387 | event = d3.dispatch("click", "dblclick"); 1388 | return d3.rebind(cc, event, "on"); 1389 | }; 1390 | 1391 | 1392 | 1393 | neo.utils.measureText = (function() { 1394 | var cache, measureUsingCanvas; 1395 | measureUsingCanvas = function(text, font) { 1396 | var canvas, canvasSelection, context; 1397 | canvasSelection = d3.select('canvas#textMeasurementCanvas').data([this]); 1398 | canvasSelection.enter().append('canvas').attr('id', 'textMeasurementCanvas').style('display', 'none'); 1399 | canvas = canvasSelection.node(); 1400 | context = canvas.getContext('2d'); 1401 | context.font = font; 1402 | return context.measureText(text).width; 1403 | }; 1404 | cache = (function() { 1405 | var cacheSize, list, map; 1406 | cacheSize = 10000; 1407 | map = {}; 1408 | list = []; 1409 | return function(key, calc) { 1410 | var cached, result; 1411 | cached = map[key]; 1412 | if (cached) { 1413 | return cached; 1414 | } else { 1415 | result = calc(); 1416 | if (list.length > cacheSize) { 1417 | delete map[list.splice(0, 1)]; 1418 | list.push(key); 1419 | } 1420 | return map[key] = result; 1421 | } 1422 | }; 1423 | })(); 1424 | return function(text, fontFamily, fontSize) { 1425 | var font; 1426 | font = 'normal normal normal ' + fontSize + 'px/normal ' + fontFamily; 1427 | return cache(text + font, function() { 1428 | return measureUsingCanvas(text, font); 1429 | }); 1430 | }; 1431 | })(); 1432 | 1433 | (function() { 1434 | var arrowPath, nodeCaption, nodeOutline, nodeOverlay, noop, relationshipOverlay, relationshipType; 1435 | noop = function() {}; 1436 | nodeOutline = new neo.Renderer({ 1437 | onGraphChange: function(selection, viz) { 1438 | var circles; 1439 | circles = selection.selectAll('circle.outline').data(function(node) { 1440 | return [node]; 1441 | }); 1442 | circles.enter().append('circle').classed('outline', true).attr({ 1443 | cx: 0, 1444 | cy: 0 1445 | }); 1446 | circles.attr({ 1447 | r: function(node) { 1448 | return node.radius; 1449 | }, 1450 | fill: function(node) { 1451 | return viz.style.forNode(node).get('color'); 1452 | }, 1453 | stroke: function(node) { 1454 | return viz.style.forNode(node).get('border-color'); 1455 | }, 1456 | 'stroke-width': function(node) { 1457 | return viz.style.forNode(node).get('border-width'); 1458 | } 1459 | }); 1460 | return circles.exit().remove(); 1461 | }, 1462 | onTick: noop 1463 | }); 1464 | nodeCaption = new neo.Renderer({ 1465 | onGraphChange: function(selection, viz) { 1466 | var text; 1467 | text = selection.selectAll('text').data(function(node) { 1468 | return node.caption; 1469 | }); 1470 | text.enter().append('text').attr({ 1471 | 'text-anchor': 'middle', 1472 | 'font-weight': 'bold', 1473 | 'stroke': '#FFFFFF', 1474 | 'stroke-width' : '0' 1475 | }); 1476 | text.text(function(line) { 1477 | return line.text; 1478 | }).attr('y', function(line) { 1479 | return line.baseline; 1480 | }).attr('font-size', function(line) { 1481 | return viz.style.forNode(line.node).get('font-size'); 1482 | }).attr('stroke', function(line) { 1483 | return viz.style.forNode(line.node).get('color'); 1484 | }).attr('fill', function(line) { 1485 | return viz.style.forNode(line.node).get('text-color-internal'); 1486 | }); 1487 | return text.exit().remove(); 1488 | }, 1489 | onTick: noop 1490 | }); 1491 | nodeOverlay = new neo.Renderer({ 1492 | onGraphChange: function(selection) { 1493 | var circles; 1494 | circles = selection.selectAll('circle.overlay').data(function(node) { 1495 | if (node.selected) { 1496 | return [node]; 1497 | } else { 1498 | return []; 1499 | } 1500 | }); 1501 | circles.enter().insert('circle', '.outline').classed('ring', true).classed('overlay', true).attr({ 1502 | cx: 0, 1503 | cy: 0, 1504 | fill: '#f5F6F6', 1505 | stroke: 'rgba(151, 151, 151, 0.2)', 1506 | 'stroke-width': '3px' 1507 | }); 1508 | circles.attr({ 1509 | r: function(node) { 1510 | return node.radius + 6; 1511 | } 1512 | }); 1513 | return circles.exit().remove(); 1514 | }, 1515 | onTick: noop 1516 | }); 1517 | arrowPath = new neo.Renderer({ 1518 | onGraphChange: function(selection, viz) { 1519 | var paths; 1520 | paths = selection.selectAll('path').data(function(rel) { 1521 | return [rel]; 1522 | }); 1523 | paths.enter().append('path'); 1524 | paths.attr('fill', function(rel) { 1525 | return viz.style.forRelationship(rel).get('color'); 1526 | }).attr('stroke', 'none'); 1527 | return paths.exit().remove(); 1528 | }, 1529 | onTick: function(selection) { 1530 | return selection.selectAll('path').attr('d', function(d) { 1531 | return d.arrowOutline; 1532 | }).attr('transform', function(d) { 1533 | return isNaN(d.startPoint.x) || isNaN(d.startPoint.y) ? null : "translate(" + d.startPoint.x + " " + d.startPoint.y + ") rotate(" + d.angle + ")"; 1534 | }); 1535 | } 1536 | }); 1537 | relationshipType = new neo.Renderer({ 1538 | onGraphChange: function(selection, viz) { 1539 | var texts; 1540 | texts = selection.selectAll("text").data(function(rel) { 1541 | return [rel]; 1542 | }); 1543 | texts.enter().append("text").attr({ 1544 | "text-anchor": "middle" 1545 | }); 1546 | texts.attr('font-size', function(rel) { 1547 | return viz.style.forRelationship(rel).get('font-size'); 1548 | }).attr('fill', function(rel) { 1549 | return viz.style.forRelationship(rel).get('text-color-' + rel.captionLayout); 1550 | }); 1551 | return texts.exit().remove(); 1552 | }, 1553 | onTick: function(selection, viz) { 1554 | return selection.selectAll('text').attr('x', function(rel) { 1555 | return isNaN(rel.midShaftPoint.x) ? null : rel.midShaftPoint.x; 1556 | }).attr('y', function(rel) { 1557 | return isNaN(rel.midShaftPoint.y) ? null : rel.midShaftPoint.y + parseFloat(viz.style.forRelationship(rel).get('font-size')) / 2 - 1; 1558 | }).attr('transform', function(rel) { 1559 | return isNaN(rel.midShaftPoint.x) || isNaN(rel.midShaftPoint.y) ? null : "rotate(" + rel.textAngle + " " + rel.midShaftPoint.x + " " + rel.midShaftPoint.y + ")"; 1560 | }).text(function(rel) { 1561 | return rel.shortCaption; 1562 | }); 1563 | } 1564 | }); 1565 | relationshipOverlay = new neo.Renderer({ 1566 | onGraphChange: function(selection) { 1567 | var band, rects; 1568 | rects = selection.selectAll("rect").data(function(rel) { 1569 | return [rel]; 1570 | }); 1571 | band = 20; 1572 | rects.enter().append('rect').classed('overlay', true).attr('fill', 'yellow').attr('x', 0).attr('y', -band / 2).attr('height', band); 1573 | rects.attr('opacity', function(rel) { 1574 | if (rel.selected) { 1575 | return 0.3; 1576 | } else { 1577 | return 0; 1578 | } 1579 | }); 1580 | return rects.exit().remove(); 1581 | }, 1582 | onTick: function(selection) { 1583 | return selection.selectAll('rect').attr('width', function(d) { 1584 | if (d.arrowLength > 0) { 1585 | return d.arrowLength; 1586 | } else { 1587 | return 0; 1588 | } 1589 | }).attr('transform', function(d) { 1590 | return isNaN(d.startPoint.x) || isNaN(d.startPoint.y) ? null : "translate(" + d.startPoint.x + " " + d.startPoint.y + ") rotate(" + d.angle + ")"; 1591 | }); 1592 | } 1593 | }); 1594 | neo.renderers.node.push(nodeOutline); 1595 | neo.renderers.node.push(nodeCaption); 1596 | neo.renderers.node.push(nodeOverlay); 1597 | neo.renderers.relationship.push(arrowPath); 1598 | neo.renderers.relationship.push(relationshipType); 1599 | return neo.renderers.relationship.push(relationshipOverlay); 1600 | })(); 1601 | 1602 | }()); -------------------------------------------------------------------------------- /scripts/sweet-alert.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){function n(t){var n=y(),o=n.querySelector("h2"),r=n.querySelector("p"),a=n.querySelector("button.cancel"),c=n.querySelector("button.confirm");if(o.innerHTML=w(t.title).split("\n").join("
"),r.innerHTML=w(t.text||"").split("\n").join("
"),t.text&&S(r),C(n.querySelectorAll(".icon")),t.type){for(var l=!1,s=0;sr;r++)o=parseInt(e.substr(2*r,2),16),o=Math.round(Math.min(Math.max(0,o+o*t),255)).toString(16),n+=("00"+o).substr(o.length);return n}function r(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}function a(e){var t=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return t?parseInt(t[1],16)+", "+parseInt(t[2],16)+", "+parseInt(t[3],16):null}function i(e,t){var n=a(t);e.style.boxShadow="0 0 2px rgba("+n+", 0.8), inset 0 0 0 1px rgba(0, 0, 0, 0.05)"}function c(){var e=y();T(p(),10),S(e),v(e,"showSweetAlert"),b(e,"hideSweetAlert"),I=t.activeElement;var n=e.querySelector("button.confirm");n.focus(),setTimeout(function(){v(e,"visible")},500);var o=e.getAttribute("data-timer");"null"!==o&&""!==o&&(e.timeout=setTimeout(function(){l()},o))}function l(){var n=y();E(p(),5),E(n,5),b(n,"showSweetAlert"),v(n,"hideSweetAlert"),b(n,"visible");var o=n.querySelector(".icon.success");b(o,"animate"),b(o.querySelector(".tip"),"animateSuccessTip"),b(o.querySelector(".long"),"animateSuccessLong");var r=n.querySelector(".icon.error");b(r,"animateErrorIcon"),b(r.querySelector(".x-mark"),"animateXMark");var a=n.querySelector(".icon.warning");b(a,"pulseWarning"),b(a.querySelector(".body"),"pulseWarningIns"),b(a.querySelector(".dot"),"pulseWarningIns"),e.onkeydown=M,t.onclick=A,I&&I.focus(),z=void 0,clearTimeout(n.timeout)}function s(){var e=y();e.style.marginTop=B(y())}var u=".sweet-alert",f=".sweet-overlay",d=["error","warning","info","success"],m={title:"",text:"",type:null,allowOutsideClick:!1,showCancelButton:!1,closeOnConfirm:!0,closeOnCancel:!0,confirmButtonText:"OK",confirmButtonColor:"#AEDEF4",cancelButtonText:"Cancel",imageUrl:null,imageSize:null,timer:null},y=function(){return t.querySelector(u)},p=function(){return t.querySelector(f)},g=function(e,t){return new RegExp(" "+t+" ").test(" "+e.className+" ")},v=function(e,t){g(e,t)||(e.className+=" "+t)},b=function(e,t){var n=" "+e.className.replace(/[\t\r\n]/g," ")+" ";if(g(e,t)){for(;n.indexOf(" "+t+" ")>=0;)n=n.replace(" "+t+" "," ");e.className=n.replace(/^\s+|\s+$/g,"")}},w=function(e){var n=t.createElement("div");return n.appendChild(t.createTextNode(e)),n.innerHTML},h=function(e){e.style.opacity="",e.style.display="block"},S=function(e){if(e&&!e.length)return h(e);for(var t=0;t0?setTimeout(o,t):e.style.display="none"};o()},q=function(n){if(MouseEvent){var o=new MouseEvent("click",{view:e,bubbles:!1,cancelable:!0});n.dispatchEvent(o)}else if(t.createEvent){var r=t.createEvent("MouseEvents");r.initEvent("click",!1,!1),n.dispatchEvent(r)}else t.createEventObject?n.fireEvent("onclick"):"function"==typeof n.onclick&&n.onclick()},O=function(t){"function"==typeof t.stopPropagation?(t.stopPropagation(),t.preventDefault()):e.event&&e.event.hasOwnProperty("cancelBubble")&&(e.event.cancelBubble=!0)},I,A,M,z;e.sweetAlertInitialize=function(){var e='

Title

Text

',n=t.createElement("div");n.innerHTML=e,t.body.appendChild(n)},e.sweetAlert=e.swal=function(){function a(t){var n=t||e.event,o=n.keyCode||n.which;if(-1!==[9,13,32,27].indexOf(o)){for(var r=n.target||n.srcElement,a=-1,c=0;c