├── .gitignore ├── LICENSE ├── README.md ├── bin └── build.js ├── index.html ├── index.js ├── jit ├── Examples │ ├── AreaChart │ │ ├── example1.html │ │ └── example1.js │ ├── BarChart │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ └── example2.js │ ├── ForceDirected │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ └── example2.js │ ├── Hypertree │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ └── example3.js │ ├── Icicle │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ └── example2.js │ ├── Other │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ └── example3.js │ ├── PieChart │ │ ├── example1.html │ │ └── example1.js │ ├── RGraph │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ ├── example3.js │ │ ├── example4.html │ │ └── example4.js │ ├── Spacetree │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ ├── example3.js │ │ ├── example4.html │ │ ├── example4.js │ │ ├── example5.html │ │ └── example5.js │ ├── Sunburst │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ └── example2.js │ ├── Treemap │ │ ├── example1.html │ │ ├── example1.js │ │ ├── example2.html │ │ ├── example2.js │ │ ├── example3.html │ │ └── example3.js │ └── css │ │ ├── AreaChart.css │ │ ├── BarChart.css │ │ ├── ForceDirected.css │ │ ├── ForceDirected3D.css │ │ ├── HeatMap.css │ │ ├── Hypertree.css │ │ ├── Icicle.css │ │ ├── Other.css │ │ ├── PieChart.css │ │ ├── RGraph.css │ │ ├── Spacetree.css │ │ ├── Sunburst.css │ │ ├── TimeGraph.css │ │ ├── Treemap.css │ │ ├── base.css │ │ ├── col1.png │ │ ├── col2.png │ │ └── gradient.png ├── Extras │ └── excanvas.js ├── jit-yc.js └── jit.js ├── package.json ├── src └── js │ ├── block.js │ ├── char.js │ ├── document.js │ ├── tree-ux.js │ ├── util.js │ ├── word.js │ └── words.js ├── style.css └── test └── unit ├── block.spec.js ├── char.spec.js ├── util.spec.js └── word.spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | /coverage -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright yabwe 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/yabwe/words 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules directory are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # words 2 | A humble yet ambitious attempt to build a WYSIWYG editor, backed by JSON, without relying on document.execCommand 3 | 4 | ## Getting Started 5 | 6 | **1)** Pull down the repo 7 | 8 | **2)** Pull down the dependencies 9 | 10 | ``` 11 | npm install 12 | ``` 13 | 14 | **3)** Start the server 15 | 16 | ``` 17 | npm start 18 | ``` 19 | 20 | **4)** Load the page 21 | 22 | ``` 23 | http://localhost:8088 24 | ``` 25 | 26 | **5)** Dance, everybody dance 27 | 28 | ## Bundling JS 29 | 30 | ``` 31 | npm run build 32 | ``` 33 | 34 | ## Running Unit Tests 35 | 36 | ``` 37 | npm test 38 | ``` 39 | 40 | ## Running Code Coverage 41 | 42 | ``` 43 | npm run coverage 44 | ``` 45 | 46 | ## Current JSON Tree Structure 47 | 48 | The current strategy is to represent the state of the editor text via a tree object in JSON 49 | 50 | ### Document 51 | 52 | A **Document** is the top level object and the root of the tree. It represents all of the text within the editor. 53 | 54 | **Document** objects have 2 main properties: 55 | 56 | 1. **blocks** 57 | * Array of all the **Block** objects which are its children. These are loosely tied to block elements in that blocks are always separated by new lines. 58 | 2. **chars** 59 | * Array of all the **Char** objects within the entire editor. These are the leaf-nodes of the data tree. 60 | 61 | ### Block 62 | 63 | A **Block** is an object which represents a chunk of text which is separated by other chunks of text by new lines. 64 | 65 | **Block** objects have 2 main properties: 66 | 67 | 1. **words** 68 | * Array of all the **Word** objects which are its children. 69 | 2. **parent** 70 | * A reference to its parent **Document** object. 71 | 72 | ### Word 73 | 74 | A **Word** is an object which represents a chunk of text which is separated by other chunks of text by spaces (within the same **Block**). All words will contain their ending character, which will either be: 75 | * A Space (' ') 76 | * A Newline ('\n') 77 | * An Empty String ('') 78 | * The last word in the **Document** will have this empty string as a terminator 79 | 80 | **Word** objects have 2 main properties: 81 | 82 | 1. **chars** 83 | * Array of all the **Char** objects which are its children. 84 | 2. **parent** 85 | * A reference to its parent **Block** object. 86 | 87 | ### Char 88 | 89 | A **Char** is an object which represents a single character of text. Currently, this will be where all formatting information will be stored (ie bold, italic, blockquote, etc.). 90 | 91 | **Char** objects will represent every single character within a **Document**. This includes spaces, newlines, and the empty character terminator. 92 | 93 | **Char** objects have 3 main properties: 94 | 95 | 1. **char** 96 | * The character this represents 97 | 2. **props** 98 | * Key-Value pair representing a formatting property and whether it is applied (ie bold, italic, blockquote) 99 | 3. **parent** 100 | * A reference to its parent **Word** object. 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /bin/build.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | 4 | var browserify = require('browserify'); 5 | 6 | var fileName = process.env.npm_package_name + '.js'; 7 | var srcDir = './src/js'; 8 | var distDir = './dist'; 9 | 10 | var files = [path.join(srcDir, fileName)]; 11 | 12 | if (!fs.existsSync(distDir)){ 13 | fs.mkdirSync(distDir); 14 | } 15 | 16 | var b = browserify(files); 17 | 18 | b.bundle().pipe(fs.createWriteStream(path.join(distDir, fileName))); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | words | demo 6 | 7 | 8 | 9 | 10 |
11 |

Words

12 |
13 | 14 | 15 | 16 | 28 |
29 |

Words are fun

30 |
31 |
32 |
33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var connect = require('connect'); 2 | var serveStatic = require('serve-static'); 3 | connect().use(serveStatic(__dirname)).listen(8088); -------------------------------------------------------------------------------- /jit/Examples/AreaChart/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AreaChart - Area Chart Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 | 25 | 26 | 27 |
28 |

29 | Area Chart Example 30 |

31 | 32 | A static Area Chart example with gradients that displays tooltips when hovering the stacks.

33 | Left-click a Stack to apply a filter to it.

34 | Right-click to restore all stacks.

35 | Click the Update button to update the JSON data. 36 | 37 |
38 | 39 | Update Data 40 | Remove Filter 41 | 42 | 43 | 44 | 45 |
See the Example Code
46 |
47 | 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 |
59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /jit/Examples/AreaChart/example1.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | var json = { 32 | 'label': ['label A', 'label B', 'label C', 'label D'], 33 | 'values': [ 34 | { 35 | 'label': 'date A', 36 | 'values': [20, 40, 15, 5] 37 | }, 38 | { 39 | 'label': 'date B', 40 | 'values': [30, 10, 45, 10] 41 | }, 42 | { 43 | 'label': 'date E', 44 | 'values': [38, 20, 35, 17] 45 | }, 46 | { 47 | 'label': 'date F', 48 | 'values': [58, 10, 35, 32] 49 | }, 50 | { 51 | 'label': 'date D', 52 | 'values': [55, 60, 34, 38] 53 | }, 54 | { 55 | 'label': 'date C', 56 | 'values': [26, 40, 25, 40] 57 | }] 58 | 59 | }; 60 | var json2 = { 61 | 'values': [ 62 | { 63 | 'label': 'date A', 64 | 'values': [10, 40, 15, 7] 65 | }, 66 | { 67 | 'label': 'date B', 68 | 'values': [30, 40, 45, 9] 69 | }, 70 | { 71 | 'label': 'date D', 72 | 'values': [55, 30, 34, 26] 73 | }, 74 | { 75 | 'label': 'date C', 76 | 'values': [26, 40, 85, 28] 77 | }] 78 | 79 | }; 80 | //end 81 | var infovis = document.getElementById('infovis'); 82 | //init AreaChart 83 | var areaChart = new $jit.AreaChart({ 84 | //id of the visualization container 85 | injectInto: 'infovis', 86 | //add animations 87 | animate: true, 88 | //separation offsets 89 | Margin: { 90 | top: 5, 91 | left: 5, 92 | right: 5, 93 | bottom: 5 94 | }, 95 | labelOffset: 10, 96 | //whether to display sums 97 | showAggregates: true, 98 | //whether to display labels at all 99 | showLabels: true, 100 | //could also be 'stacked' 101 | type: useGradients? 'stacked:gradient' : 'stacked', 102 | //label styling 103 | Label: { 104 | type: labelType, //can be 'Native' or 'HTML' 105 | size: 13, 106 | family: 'Arial', 107 | color: 'white' 108 | }, 109 | //enable tips 110 | Tips: { 111 | enable: true, 112 | onShow: function(tip, elem) { 113 | tip.innerHTML = "" + elem.name + ": " + elem.value; 114 | } 115 | }, 116 | //add left and right click handlers 117 | filterOnClick: true, 118 | restoreOnRightClick:true 119 | }); 120 | //load JSON data. 121 | areaChart.loadJSON(json); 122 | //end 123 | var list = $jit.id('id-list'), 124 | button = $jit.id('update'), 125 | restoreButton = $jit.id('restore'); 126 | //update json on click 127 | $jit.util.addEvent(button, 'click', function() { 128 | var util = $jit.util; 129 | if(util.hasClass(button, 'gray')) return; 130 | util.removeClass(button, 'white'); 131 | util.addClass(button, 'gray'); 132 | areaChart.updateJSON(json2); 133 | }); 134 | //restore graph on click 135 | $jit.util.addEvent(restoreButton, 'click', function() { 136 | areaChart.restore(); 137 | }); 138 | //dynamically add legend to list 139 | var legend = areaChart.getLegend(), 140 | listItems = []; 141 | for(var name in legend) { 142 | listItems.push('
 
' + name); 144 | } 145 | list.innerHTML = '
  • ' + listItems.join('
  • ') + '
  • '; 146 | } 147 | -------------------------------------------------------------------------------- /jit/Examples/BarChart/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BarChart - Bar Chart Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Bar Chart Example 30 |

    31 | 32 | A static vertical Bar Chart example with gradients. The Bar Chart displays tooltips when hovering the stacks.

    33 | Click the Update button to update the JSON data. 34 | 35 |
    36 | 37 | Update Data 38 | 39 | 40 |
    See the Example Code
    41 |
    42 | 43 |
    44 |
    45 |
    46 | 47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 |
    55 | 56 | 57 | -------------------------------------------------------------------------------- /jit/Examples/BarChart/example1.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | var json = { 32 | 'label': ['label A', 'label B', 'label C', 'label D'], 33 | 'values': [ 34 | { 35 | 'label': 'date A', 36 | 'values': [20, 40, 15, 5] 37 | }, 38 | { 39 | 'label': 'date B', 40 | 'values': [30, 10, 45, 10] 41 | }, 42 | { 43 | 'label': 'date E', 44 | 'values': [38, 20, 35, 17] 45 | }, 46 | { 47 | 'label': 'date F', 48 | 'values': [58, 10, 35, 32] 49 | }, 50 | { 51 | 'label': 'date D', 52 | 'values': [55, 60, 34, 38] 53 | }, 54 | { 55 | 'label': 'date C', 56 | 'values': [26, 40, 25, 40] 57 | }] 58 | 59 | }; 60 | //end 61 | var json2 = { 62 | 'values': [ 63 | { 64 | 'label': 'date A', 65 | 'values': [10, 40, 15, 7] 66 | }, 67 | { 68 | 'label': 'date B', 69 | 'values': [30, 40, 45, 9] 70 | }, 71 | { 72 | 'label': 'date D', 73 | 'values': [55, 30, 34, 26] 74 | }, 75 | { 76 | 'label': 'date C', 77 | 'values': [26, 40, 85, 28] 78 | }] 79 | 80 | }; 81 | //init BarChart 82 | var barChart = new $jit.BarChart({ 83 | //id of the visualization container 84 | injectInto: 'infovis', 85 | //whether to add animations 86 | animate: true, 87 | //horizontal or vertical barcharts 88 | orientation: 'vertical', 89 | //bars separation 90 | barsOffset: 20, 91 | //visualization offset 92 | Margin: { 93 | top:5, 94 | left: 5, 95 | right: 5, 96 | bottom:5 97 | }, 98 | //labels offset position 99 | labelOffset: 5, 100 | //bars style 101 | type: useGradients? 'stacked:gradient' : 'stacked', 102 | //whether to show the aggregation of the values 103 | showAggregates:true, 104 | //whether to show the labels for the bars 105 | showLabels:true, 106 | //labels style 107 | Label: { 108 | type: labelType, //Native or HTML 109 | size: 13, 110 | family: 'Arial', 111 | color: 'white' 112 | }, 113 | //add tooltips 114 | Tips: { 115 | enable: true, 116 | onShow: function(tip, elem) { 117 | tip.innerHTML = "" + elem.name + ": " + elem.value; 118 | } 119 | } 120 | }); 121 | //load JSON data. 122 | barChart.loadJSON(json); 123 | //end 124 | var list = $jit.id('id-list'), 125 | button = $jit.id('update'), 126 | orn = $jit.id('switch-orientation'); 127 | //update json on click 'Update Data' 128 | $jit.util.addEvent(button, 'click', function() { 129 | var util = $jit.util; 130 | if(util.hasClass(button, 'gray')) return; 131 | util.removeClass(button, 'white'); 132 | util.addClass(button, 'gray'); 133 | barChart.updateJSON(json2); 134 | }); 135 | //dynamically add legend to list 136 | var legend = barChart.getLegend(), 137 | listItems = []; 138 | for(var name in legend) { 139 | listItems.push('
     
    ' + name); 141 | } 142 | list.innerHTML = '
  • ' + listItems.join('
  • ') + '
  • '; 143 | } 144 | -------------------------------------------------------------------------------- /jit/Examples/BarChart/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BarChart - Bar Chart Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Bar Chart Example 30 |

    31 | 32 | A static horizontal Bar Chart example without gradients. The Bar Chart displays tooltips when hovering the stacks.

    33 | Click the Update button to update the JSON data. 34 | 35 |
    36 | 37 | Update Data 38 | 39 | 40 |
    See the Example Code
    41 |
    42 | 43 |
    44 |
    45 |
    46 | 47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 |
    55 | 56 | 57 | -------------------------------------------------------------------------------- /jit/Examples/BarChart/example2.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | var json = { 32 | 'label': ['label A', 'label B', 'label C', 'label D'], 33 | 'values': [ 34 | { 35 | 'label': 'date A', 36 | 'values': [20, 40, 15, 5] 37 | }, 38 | { 39 | 'label': 'date B', 40 | 'values': [30, 10, 45, 10] 41 | }, 42 | { 43 | 'label': 'date E', 44 | 'values': [38, 20, 35, 17] 45 | }, 46 | { 47 | 'label': 'date F', 48 | 'values': [58, 10, 35, 32] 49 | }, 50 | { 51 | 'label': 'date D', 52 | 'values': [55, 60, 34, 38] 53 | }, 54 | { 55 | 'label': 'date C', 56 | 'values': [26, 40, 25, 40] 57 | }] 58 | 59 | }; 60 | //end 61 | var json2 = { 62 | 'values': [ 63 | { 64 | 'label': 'date A', 65 | 'values': [10, 40, 15, 7] 66 | }, 67 | { 68 | 'label': 'date B', 69 | 'values': [30, 40, 45, 9] 70 | }, 71 | { 72 | 'label': 'date D', 73 | 'values': [55, 30, 34, 26] 74 | }, 75 | { 76 | 'label': 'date C', 77 | 'values': [26, 40, 85, 28] 78 | }] 79 | 80 | }; 81 | //init BarChart 82 | var barChart = new $jit.BarChart({ 83 | //id of the visualization container 84 | injectInto: 'infovis', 85 | //whether to add animations 86 | animate: true, 87 | //horizontal or vertical barcharts 88 | orientation: 'horizontal', 89 | //bars separation 90 | barsOffset: 0.5, 91 | //visualization offset 92 | Margin: { 93 | top: 5, 94 | left: 5, 95 | right: 5, 96 | bottom: 5 97 | }, 98 | //labels offset position 99 | labelOffset:5, 100 | //bars style 101 | type:'stacked', 102 | //whether to show the aggregation of the values 103 | showAggregates:true, 104 | //whether to show the labels for the bars 105 | showLabels:true, 106 | //label styles 107 | Label: { 108 | type: labelType, //Native or HTML 109 | size: 13, 110 | family: 'Arial', 111 | color: 'white' 112 | }, 113 | //tooltip options 114 | Tips: { 115 | enable: true, 116 | onShow: function(tip, elem) { 117 | tip.innerHTML = "" + elem.name + ": " + elem.value; 118 | } 119 | } 120 | }); 121 | //load JSON data. 122 | barChart.loadJSON(json); 123 | //end 124 | var list = $jit.id('id-list'), 125 | button = $jit.id('update'); 126 | //update json on click 'Update Data' 127 | $jit.util.addEvent(button, 'click', function() { 128 | var util = $jit.util; 129 | if(util.hasClass(button, 'gray')) return; 130 | util.removeClass(button, 'white'); 131 | util.addClass(button, 'gray'); 132 | barChart.updateJSON(json2); 133 | }); 134 | //dynamically add legend to list 135 | var legend = barChart.getLegend(), 136 | listItems = []; 137 | for(var name in legend) { 138 | listItems.push('
     
    ' + name); 140 | } 141 | list.innerHTML = '
  • ' + listItems.join('
  • ') + '
  • '; 142 | } 143 | -------------------------------------------------------------------------------- /jit/Examples/ForceDirected/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ForceDirected - Force Directed Static Graph 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Force Directed Static Graph 30 |

    31 | 32 | A static JSON Graph structure is used as input for this visualization.

    33 | You can zoom and pan the visualization by scrolling and dragging.

    34 | You can change node positions by dragging the nodes around.

    35 | The clicked node's connections are displayed in a relations list in the right column.

    36 | The JSON static data is customized to provide different node types, colors and widths. 37 | 38 |
    39 | 40 |
    41 | 42 | 43 |
    See the Example Code
    44 |
    45 | 46 |
    47 |
    48 |
    49 | 50 |
    51 | 52 |
    53 | 54 |
    55 | 56 |
    57 |
    58 | 59 | 60 | -------------------------------------------------------------------------------- /jit/Examples/ForceDirected/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ForceDirected - Graph Operations 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Graph Operations 30 |

    31 | 32 | In this (advanced) example a static graph is fed into the visualization.

    33 | Custom Animations are triggered when clicking on a node's label or when deleting a node.

    34 | Click on a node's label to select a node and its connections.

    35 | Click on the 'x' link to delete a node.

    36 | You can drag nodes around and zoom and pan, just like you did in the previous 37 | example. 38 | 39 | 40 |
    41 | 42 |
    43 | 44 | 45 |
    See the Example Code
    46 |
    47 | 48 |
    49 |
    50 |
    51 | 52 |
    53 | 54 |
    55 | 56 |
    57 | 58 |
    59 |
    60 | 61 | 62 | -------------------------------------------------------------------------------- /jit/Examples/Hypertree/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hypertree - Tree Animation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Tree Animation 30 |

    31 | 32 | A static JSON Tree structure is used as input for this animation.

    33 | Clicking on a node should move the tree and center that node.

    34 | The centered node's children are displayed in a relations list in the right column. 35 | 36 |
    37 | 38 |
    39 | 40 | 41 |
    See the Example Code
    42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |
    49 | 50 |
    51 | 52 |
    53 | 54 |
    55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /jit/Examples/Hypertree/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hypertree - Weighted Graph Animation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Weighted Graph Animation 30 |

    31 | 32 | A static JSON graph structure is used for this animation.

    33 | For each JSON node the "$type" and "$dim" parameters set the type of node to be plotted and its dimensions.

    34 | Line weights are added programmatically, onBeforePlotLine.

    35 | A Back transition is used instead of the linear transition for the animation. 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Hypertree/example2.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | //By defining properties with the dollar sign ($) 32 | //in nodes and edges we can override the global configuration 33 | //properties for nodes and edges. 34 | //In this case we use "$type" and "$dim" properties to override 35 | //the type of the node to be plotted and its dimension. 36 | var json = [{ 37 | "id": "node0", 38 | "name": "node0 name", 39 | "data": { 40 | "$dim": 16.759175934208628, 41 | "some other key": "some other value" 42 | }, 43 | "adjacencies": [{ 44 | "nodeTo": "node1", 45 | "data": { 46 | "weight": 3 47 | } 48 | }, { 49 | "nodeTo": "node2", 50 | "data": { 51 | "weight": 3 52 | } 53 | }, { 54 | "nodeTo": "node3", 55 | "data": { 56 | "weight": 3 57 | } 58 | }, { 59 | "nodeTo": "node4", 60 | "data": { 61 | "weight": 1 62 | } 63 | }, { 64 | "nodeTo": "node5", 65 | "data": { 66 | "weight": 1 67 | } 68 | }] 69 | }, { 70 | "id": "node1", 71 | "name": "node1 name", 72 | "data": { 73 | "$dim": 13.077119090372014, 74 | "$type": "square", 75 | "some other key": "some other value" 76 | }, 77 | "adjacencies": [{ 78 | "nodeTo": "node0", 79 | "data": { 80 | "weight": 3 81 | } 82 | }, { 83 | "nodeTo": "node2", 84 | "data": { 85 | "weight": 1 86 | } 87 | }, { 88 | "nodeTo": "node3", 89 | "data": { 90 | "weight": 3 91 | } 92 | }, { 93 | "nodeTo": "node4", 94 | "data": { 95 | "weight": 1 96 | } 97 | }, { 98 | "nodeTo": "node5", 99 | "data": { 100 | "weight": 1 101 | } 102 | }] 103 | }, { 104 | "id": "node2", 105 | "name": "node2 name", 106 | "data": { 107 | "$dim": 24.937383149648717, 108 | "$type": "triangle", 109 | "some other key": "some other value" 110 | }, 111 | "adjacencies": [{ 112 | "nodeTo": "node0", 113 | "data": { 114 | "weight": 3 115 | } 116 | }, { 117 | "nodeTo": "node1", 118 | "data": { 119 | "weight": 1 120 | } 121 | }, { 122 | "nodeTo": "node3", 123 | "data": { 124 | "weight": 3 125 | } 126 | }, { 127 | "nodeTo": "node4", 128 | "data": { 129 | "weight": 3 130 | } 131 | }, { 132 | "nodeTo": "node5", 133 | "data": { 134 | "weight": 1 135 | } 136 | }] 137 | }, { 138 | "id": "node3", 139 | "name": "node3 name", 140 | "data": { 141 | "$dim": 10.53272740718869, 142 | "some other key": "some other value" 143 | }, 144 | "adjacencies": [{ 145 | "nodeTo": "node0", 146 | "data": { 147 | "weight": 3 148 | } 149 | }, { 150 | "nodeTo": "node1", 151 | "data": { 152 | "weight": 3 153 | } 154 | }, { 155 | "nodeTo": "node2", 156 | "data": { 157 | "weight": 3 158 | } 159 | }, { 160 | "nodeTo": "node4", 161 | "data": { 162 | "weight": 1 163 | } 164 | }, { 165 | "nodeTo": "node5", 166 | "data": { 167 | "weight": 3 168 | } 169 | }] 170 | }, { 171 | "id": "node4", 172 | "name": "node4 name", 173 | "data": { 174 | "$dim": 5.3754347037767345, 175 | "$type":"triangle", 176 | "some other key": "some other value" 177 | }, 178 | "adjacencies": [{ 179 | "nodeTo": "node0", 180 | "data": { 181 | "weight": 1 182 | } 183 | }, { 184 | "nodeTo": "node1", 185 | "data": { 186 | "weight": 1 187 | } 188 | }, { 189 | "nodeTo": "node2", 190 | "data": { 191 | "weight": 3 192 | } 193 | }, { 194 | "nodeTo": "node3", 195 | "data": { 196 | "weight": 1 197 | } 198 | }, { 199 | "nodeTo": "node5", 200 | "data": { 201 | "weight": 3 202 | } 203 | }] 204 | }, { 205 | "id": "node5", 206 | "name": "node5 name", 207 | "data": { 208 | "$dim": 32.26403873194912, 209 | "$type": "star", 210 | "some other key": "some other value" 211 | }, 212 | "adjacencies": [{ 213 | "nodeTo": "node0", 214 | "data": { 215 | "weight": 1 216 | } 217 | }, { 218 | "nodeTo": "node1", 219 | "data": { 220 | "weight": 1 221 | } 222 | }, { 223 | "nodeTo": "node2", 224 | "data": { 225 | "weight": 1 226 | } 227 | }, { 228 | "nodeTo": "node3", 229 | "data": { 230 | "weight": 3 231 | } 232 | }, { 233 | "nodeTo": "node4", 234 | "data": { 235 | "weight": 3 236 | } 237 | }] 238 | }]; 239 | //end 240 | //init Hypertree 241 | var ht = new $jit.Hypertree({ 242 | //id of the visualization container 243 | injectInto: 'infovis', 244 | //By setting overridable=true, 245 | //Node and Edge global properties can be 246 | //overriden for each node/edge. 247 | Node: { 248 | overridable: true, 249 | 'transform': false, 250 | color: "#f00" 251 | }, 252 | 253 | Edge: { 254 | overridable: true, 255 | color: "#088" 256 | }, 257 | //calculate nodes offset 258 | offset: 0.2, 259 | //Change the animation transition type 260 | transition: $jit.Trans.Back.easeOut, 261 | //animation duration (in milliseconds) 262 | duration:1000, 263 | 264 | //This method is called right before plotting an 265 | //edge. This method is useful for adding individual 266 | //styles to edges. 267 | onBeforePlotLine: function(adj){ 268 | //Set random lineWidth for edges. 269 | if (!adj.data.$lineWidth) 270 | adj.data.$lineWidth = Math.random() * 7 + 1; 271 | }, 272 | 273 | onBeforeCompute: function(node){ 274 | Log.write("centering"); 275 | }, 276 | //Attach event handlers on label creation. 277 | onCreateLabel: function(domElement, node){ 278 | domElement.innerHTML = node.name; 279 | domElement.style.cursor = "pointer"; 280 | domElement.onclick = function () { 281 | ht.onClick(node.id, { 282 | hideLabels: false, 283 | onComplete: function() { 284 | ht.controller.onComplete(); 285 | } 286 | }); 287 | }; 288 | }, 289 | //This method is called when moving/placing a label. 290 | //You can add some positioning offsets to the labels here. 291 | onPlaceLabel: function(domElement, node){ 292 | var width = domElement.offsetWidth; 293 | var intX = parseInt(domElement.style.left); 294 | intX -= width / 2; 295 | domElement.style.left = intX + 'px'; 296 | }, 297 | 298 | onComplete: function(){ 299 | Log.write("done"); 300 | 301 | //Make the relations list shown in the right column. 302 | var node = ht.graph.getClosestNodeToOrigin("current"); 303 | var html = "

    " + node.name + "

    Connections:"; 304 | html += ""; 310 | $jit.id('inner-details').innerHTML = html; 311 | } 312 | }); 313 | //load JSON graph. 314 | ht.loadJSON(json, 2); 315 | //compute positions and plot 316 | ht.refresh(); 317 | //end 318 | ht.controller.onBeforeCompute(ht.graph.getNode(ht.root)); 319 | ht.controller.onComplete(); 320 | } 321 | -------------------------------------------------------------------------------- /jit/Examples/Hypertree/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hypertree - Graph Operations 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Graph Operations 30 |

    31 | 32 | You can do the following operations with the Hypertree

    33 | 1.- Removing subtrees or nodes

    34 | 2.- Removing edges

    35 | 3.- Adding another graph, also called sum

    36 | 4.- Morphing (or transforming) the graph into another one
    37 | 38 |
    39 | 40 |
    41 | 42 | 43 |
    See the Example Code
    44 |
    45 | 46 |
    47 |
    48 |
    49 | 50 |
    51 | 52 |

    Global Options

    53 | 54 | 55 | 56 | 59 | 65 | 66 | 67 | 70 | 77 | 78 | 79 | 82 | 85 | 86 | 87 | 90 | 91 |
    57 | duration: 58 | 60 | 64 |
    68 | fps: 69 | 71 | 76 |
    80 | hide labels: 81 | 83 | 84 |
    88 | 89 |
    92 | 93 |

    1.- Remove Nodes

    94 | 95 | 96 | 97 | 100 | 108 | 109 | 110 | 113 | 116 | 117 |
    98 | type: 99 | 101 | 107 |
    111 | 112 | 114 | 115 |
    118 | 119 |

    2.- Remove Edges

    120 | 121 | 122 | 123 | 126 | 134 | 135 | 136 | 139 | 142 | 143 |
    124 | type: 125 | 127 | 133 |
    137 | 138 | 140 | 141 |
    144 | 145 |

    3.- Add Graph (Sum)

    146 | 147 | 148 | 149 | 152 | 159 | 160 | 161 | 164 | 167 | 168 |
    150 | type: 151 | 153 | 158 |
    162 | 163 | 165 | 166 |
    169 | 170 |

    4.- Morph

    171 | 172 | 173 | 174 | 177 | 183 | 184 | 185 | 188 | 191 | 192 |
    175 | type: 176 | 178 | 182 |
    186 | 187 | 189 | 190 |
    193 | 194 |
    195 | 196 |
    197 |
    198 | 199 | 200 | -------------------------------------------------------------------------------- /jit/Examples/Icicle/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Icicle - Icicle Tree with static JSON data 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 |
    26 | 27 |

    28 | Icicle Tree with static JSON data 29 |

    30 | 31 |

    Some static JSON tree data is fed to this visualization.

    32 |

    33 | Left click to set a node as root for the visualization. 34 |

    35 |

    36 | Right click to set the parent node as root for the visualization. 37 |

    38 | 39 | 40 |
    41 | 42 | 46 |
    47 |
    48 | 49 | 57 |
    58 |
    59 |
    60 | 61 | Go to Parent 62 | 63 |
    64 | 65 | 66 |
    See the Example Code
    67 |
    68 | 69 |
    70 |
    71 |
    72 | 73 |
    74 | 75 |
    76 | 77 |
    78 | 79 |
    80 |
    81 | 82 | 83 | -------------------------------------------------------------------------------- /jit/Examples/Icicle/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Icicle - Icicle tree with limited levels shown 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 |
    26 | 27 |

    28 | Icicle tree with limited levels shown 29 |

    30 | 31 |

    A static JSON tree representing a file system tree is loaded into 32 | an Icicle Tree.

    33 |

    34 | Left click to set a node as root for the visualization. 35 |

    36 |

    37 | Right click to set the parent node as root for the visualization. 38 |

    39 | 40 | 41 |
    42 | 43 | 47 |
    48 |
    49 | 50 | 58 |
    59 |
    60 |
    61 | 62 | Go to Parent 63 | 64 |
    65 | 66 | 67 |
    See the Example Code
    68 |
    69 | 70 |
    71 |
    72 |
    73 | 74 |
    75 | 76 |
    77 | 78 |
    79 | 80 |
    81 |
    82 | 83 | 84 | -------------------------------------------------------------------------------- /jit/Examples/Other/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Other - Implementing Node Types 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Implementing Node Types 30 |

    31 | 32 | In this example some custom node types are created for rendering pie charts with the RGraph.

    33 | Multiple instances of the RGraph are created using these node types. (top)

    34 | The SpaceTree is loaded with some custom data that individually changes nodes dimensions, making a bar chart (bottom). 35 | 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Other/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Other - Composing Visualizations 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Composing Visualizations 30 |

    31 | 32 | In this example a RGraph is composed with another RGraph (for node rendering).

    33 | The RGraph used for node rendering implements a custom node type defined in the "Implementing Node Types" example.

    34 | This example shows that many visualizations can be composed to create new visualizations. 35 | 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Other/example2.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init() { 30 | //init data 31 | var json = { 32 | 'id': 'root', 33 | 'name': 'RGraph( RGraph )', 34 | 'data': { 35 | '$type': 'none' 36 | }, 37 | 'children':[ 38 | { 39 | 'id':'pie10', 40 | 'name': 'pie1', 41 | 'data': { 42 | '$angularWidth': 20, 43 | '$color': '#f55' 44 | }, 45 | 'children': [ 46 | { 47 | 'id':'pie100', 48 | 'name': 'pc1', 49 | 'data': { 50 | '$angularWidth': 20, 51 | '$color': '#55f' 52 | }, 53 | 'children': [] 54 | 55 | }, 56 | { 57 | 'id':'pie101', 58 | 'name': 'pc2', 59 | 'data': { 60 | '$angularWidth': 70, 61 | '$color': '#66f' 62 | }, 63 | 'children': [] 64 | 65 | }, 66 | { 67 | 'id':'pie102', 68 | 'name': 'pc3', 69 | 'data': { 70 | '$angularWidth': 10, 71 | '$color': '#77f' 72 | }, 73 | 'children': [] 74 | 75 | } 76 | ] 77 | }, 78 | { 79 | 'id':'pie20', 80 | 'name': 'pie2', 81 | 'data': { 82 | '$angularWidth': 40, 83 | '$color': '#f77' 84 | }, 85 | 'children': [ 86 | { 87 | 'id':'pie200', 88 | 'name': 'pc1', 89 | 'data': { 90 | '$angularWidth': 40, 91 | '$color': '#88f' 92 | }, 93 | 'children': [] 94 | 95 | }, 96 | { 97 | 'id':'pie201', 98 | 'name': 'pc2', 99 | 'data': { 100 | '$angularWidth': 60, 101 | '$color': '#99f' 102 | }, 103 | 'children': [] 104 | 105 | } 106 | ] 107 | }, 108 | { 109 | 'id':'pie30', 110 | 'name': 'pie3', 111 | 'data': { 112 | '$angularWidth': 10, 113 | '$color': '#f99' 114 | }, 115 | 'children': [ 116 | { 117 | 'id':'pie300', 118 | 'name': 'pc1', 119 | 'data': { 120 | '$angularWidth': 100, 121 | '$color': '#aaf' 122 | }, 123 | 'children': [] 124 | 125 | } 126 | ] 127 | } 128 | ] 129 | }; 130 | var jsonpie = { 131 | 'id': 'root', 132 | 'name': 'RGraph based Pie Chart', 133 | 'data': { 134 | '$type': 'none' 135 | }, 136 | 'children':[ 137 | { 138 | 'id':'pie1', 139 | 'name': 'pie1', 140 | 'data': { 141 | '$angularWidth': 20, 142 | '$color': '#f55' 143 | }, 144 | 'children': [] 145 | }, 146 | { 147 | 'id':'pie2', 148 | 'name': 'pie2', 149 | 'data': { 150 | '$angularWidth': 40, 151 | '$color': '#f77' 152 | }, 153 | 'children': [] 154 | }, 155 | { 156 | 'id':'pie3', 157 | 'name': 'pie3', 158 | 'data': { 159 | '$angularWidth': 10, 160 | '$color': '#f99' 161 | }, 162 | 'children': [] 163 | }, 164 | { 165 | 'id':'pie4', 166 | 'name': 'pie4', 167 | 'data': { 168 | '$angularWidth': 30, 169 | '$color': '#fbb' 170 | }, 171 | 'children': [] 172 | } 173 | ] 174 | }; 175 | //end 176 | 177 | //init nodetypes 178 | //Here we implement custom node rendering types for the RGraph 179 | //Using this feature requires some javascript and canvas experience. 180 | $jit.RGraph.Plot.NodeTypes.implement({ 181 | //This node type is used for plotting pie-chart slices as nodes 182 | 'nodepie': { 183 | 'render': function(node, canvas) { 184 | var span = node.angleSpan, begin = span.begin, end = span.end; 185 | var polarNode = node.pos.getp(true); 186 | var polar = new $jit.Polar(polarNode.rho, begin); 187 | var p1coord = polar.getc(true); 188 | polar.theta = end; 189 | var p2coord = polar.getc(true); 190 | 191 | var ctx = canvas.getCtx(); 192 | ctx.beginPath(); 193 | ctx.moveTo(0, 0); 194 | ctx.lineTo(p1coord.x, p1coord.y); 195 | ctx.moveTo(0, 0); 196 | ctx.lineTo(p2coord.x, p2coord.y); 197 | ctx.moveTo(0, 0); 198 | ctx.arc(0, 0, polarNode.rho, begin, end, false); 199 | ctx.fill(); 200 | } 201 | }, 202 | //Create a new node type that renders an entire RGraph visualization 203 | //as node 204 | 'piechart': { 205 | 'render': function(node, canvas, animating) { 206 | var ctx = canvas.getCtx(), pos = node.pos.getc(true); 207 | ctx.save(); 208 | ctx.translate(pos.x, pos.y); 209 | pie.plot(); 210 | ctx.restore(); 211 | } 212 | } 213 | }); 214 | //end 215 | 216 | //init pie 217 | //This RGraph instance will be used as the node for 218 | //another RGraph instance. 219 | var pie = new $jit.RGraph({ 220 | 'injectInto': 'infovis', 221 | //Optional: create a background canvas and plot 222 | //concentric circles in it. 223 | 'background': { 224 | CanvasStyles: { 225 | strokeStyle: '#555' 226 | } 227 | }, 228 | //Add node/edge styles and set 229 | //overridable=true if you want your 230 | //styles to be individually overriden 231 | Node: { 232 | 'overridable': true, 233 | 'type':'nodepie' 234 | }, 235 | Edge: { 236 | 'type':'none' 237 | }, 238 | //Parent-children distance 239 | levelDistance: 30, 240 | //Don't create labels in this visualization 241 | withLabels: false, 242 | //Don't clear the entire canvas when plotting 243 | //this visualization 244 | clearCanvas: false 245 | }); 246 | //load graph. 247 | pie.loadJSON(jsonpie); 248 | pie.compute(); 249 | //end 250 | 251 | //init rgraph 252 | var rgraph = new $jit.RGraph({ 253 | useCanvas: pie.canvas, 254 | //Add node/edge styles and set 255 | //overridable=true if you want your 256 | //styles to be individually overriden 257 | Node: { 258 | //set the RGraph rendering function 259 | //as node type 260 | 'type': 'piechart' 261 | }, 262 | Edge: { 263 | color: '#772277' 264 | }, 265 | //Parent-children distance 266 | levelDistance: 100, 267 | //Duration 268 | duration: 1500, 269 | //Add styles to node labels on label creation 270 | onCreateLabel: function(domElement, node){ 271 | domElement.innerHTML = node.name; 272 | var style = domElement.style; 273 | style.fontSize = "0.8em"; 274 | style.color = "#fff"; 275 | style.cursor = "pointer"; 276 | domElement.onclick = function() { 277 | rgraph.onClick(node.id, { 278 | hideLabels: false 279 | }); 280 | }; 281 | }, 282 | 283 | onPlaceLabel: function(domElement, node){ 284 | var style = domElement.style; 285 | var left = parseInt(style.left); 286 | var w = domElement.offsetWidth; 287 | style.left = (left - w / 2) + 'px'; 288 | style.display = ''; 289 | } 290 | }); 291 | //load graph. 292 | rgraph.loadJSON(json); 293 | rgraph.refresh(); 294 | //end 295 | } 296 | -------------------------------------------------------------------------------- /jit/Examples/Other/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Other - Composing Visualizations 2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Composing Visualizations 2 30 |

    31 | 32 | In this example a SpaceTree is composed with a RGraph (for node rendering).

    33 | The RGraph used for node rendering implements a custom node type defined in the "Implementing Node Types" example.

    34 | This example shows that many visualizations can be composed to create new visualizations. 35 | 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Other/example3.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init() { 30 | //init data 31 | var json = { 32 | 'id': 'root', 33 | 'name': 'root', 34 | 'data': { 35 | //'$type': 'none' 36 | }, 37 | 'children':[ 38 | { 39 | 'id':'pie10', 40 | 'name': 'pie1', 41 | 'data': { 42 | '$angularWidth': 20, 43 | '$color': '#f55' 44 | }, 45 | 'children': [ 46 | { 47 | 'id':'pie100', 48 | 'name': 'pc1', 49 | 'data': { 50 | '$angularWidth': 20, 51 | '$color': '#55f' 52 | }, 53 | 'children': [] 54 | 55 | }, 56 | { 57 | 'id':'pie101', 58 | 'name': 'pc2', 59 | 'data': { 60 | '$angularWidth': 70, 61 | '$color': '#66f' 62 | }, 63 | 'children': [] 64 | 65 | }, 66 | { 67 | 'id':'pie102', 68 | 'name': 'pc3', 69 | 'data': { 70 | '$angularWidth': 10, 71 | '$color': '#77f' 72 | }, 73 | 'children': [] 74 | 75 | } 76 | ] 77 | }, 78 | { 79 | 'id':'pie20', 80 | 'name': 'pie2', 81 | 'data': { 82 | '$angularWidth': 40, 83 | '$color': '#f77' 84 | }, 85 | 'children': [ 86 | { 87 | 'id':'pie200', 88 | 'name': 'pc1', 89 | 'data': { 90 | '$angularWidth': 40, 91 | '$color': '#88f' 92 | }, 93 | 'children': [] 94 | 95 | }, 96 | { 97 | 'id':'pie201', 98 | 'name': 'pc2', 99 | 'data': { 100 | '$angularWidth': 60, 101 | '$color': '#99f' 102 | }, 103 | 'children': [] 104 | 105 | } 106 | ] 107 | }, 108 | { 109 | 'id':'pie30', 110 | 'name': 'pie3', 111 | 'data': { 112 | '$angularWidth': 10, 113 | '$color': '#f99' 114 | }, 115 | 'children': [ 116 | { 117 | 'id':'pie300', 118 | 'name': 'pc1', 119 | 'data': { 120 | '$angularWidth': 100, 121 | '$color': '#aaf' 122 | }, 123 | 'children': [] 124 | 125 | } 126 | ] 127 | } 128 | ] 129 | }; 130 | var jsonpie = { 131 | 'id': 'root', 132 | 'name': 'RGraph based Pie Chart', 133 | 'data': { 134 | '$type': 'none' 135 | }, 136 | 'children':[ 137 | { 138 | 'id':'pie1', 139 | 'name': 'pie1', 140 | 'data': { 141 | '$angularWidth': 20, 142 | '$color': '#55f' 143 | }, 144 | 'children': [] 145 | }, 146 | { 147 | 'id':'pie2', 148 | 'name': 'pie2', 149 | 'data': { 150 | '$angularWidth': 40, 151 | '$color': '#77f' 152 | }, 153 | 'children': [] 154 | }, 155 | { 156 | 'id':'pie3', 157 | 'name': 'pie3', 158 | 'data': { 159 | '$angularWidth': 10, 160 | '$color': '#99f' 161 | }, 162 | 'children': [] 163 | }, 164 | { 165 | 'id':'pie4', 166 | 'name': 'pie4', 167 | 'data': { 168 | '$angularWidth': 30, 169 | '$color': '#bbf' 170 | }, 171 | 'children': [] 172 | } 173 | ] 174 | }; 175 | //end 176 | 177 | //init nodetypes 178 | //Here we implement custom node rendering types for the RGraph 179 | //Using this feature requires some javascript and canvas experience. 180 | $jit.RGraph.Plot.NodeTypes.implement({ 181 | //This node type is used for plotting pie-chart slices as nodes 182 | 'shortnodepie': { 183 | 'render': function(node, canvas) { 184 | var ldist = this.config.levelDistance; 185 | var span = node.angleSpan, begin = span.begin, end = span.end; 186 | var polarNode = node.pos.getp(true); 187 | 188 | var polar = new $jit.Polar(polarNode.rho, begin); 189 | var p1coord = polar.getc(true); 190 | 191 | polar.theta = end; 192 | var p2coord = polar.getc(true); 193 | 194 | polar.rho += ldist; 195 | var p3coord = polar.getc(true); 196 | 197 | polar.theta = begin; 198 | var p4coord = polar.getc(true); 199 | 200 | 201 | var ctx = canvas.getCtx(); 202 | ctx.beginPath(); 203 | ctx.moveTo(p1coord.x, p1coord.y); 204 | ctx.lineTo(p4coord.x, p4coord.y); 205 | ctx.moveTo(0, 0); 206 | ctx.arc(0, 0, polarNode.rho, begin, end, false); 207 | 208 | ctx.moveTo(p2coord.x, p2coord.y); 209 | ctx.lineTo(p3coord.x, p3coord.y); 210 | ctx.moveTo(0, 0); 211 | ctx.arc(0, 0, polarNode.rho + ldist, end, begin, true); 212 | 213 | ctx.fill(); 214 | } 215 | } 216 | }); 217 | 218 | $jit.ST.Plot.NodeTypes.implement({ 219 | //Create a new node type that renders an entire RGraph visualization 220 | 'piechart': { 221 | 'render': function(node, canvas, animating) { 222 | var ctx = canvas.getCtx(), pos = node.pos.getc(true); 223 | ctx.save(); 224 | ctx.translate(pos.x, pos.y); 225 | pie.plot(); 226 | ctx.restore(); 227 | } 228 | } 229 | }); 230 | //end 231 | 232 | //init pie 233 | var pie = new $jit.RGraph({ 234 | 'injectInto': 'infovis', 235 | //Add node/edge styles and set 236 | //overridable=true if you want your 237 | //styles to be individually overriden 238 | Node: { 239 | 'overridable': true, 240 | 'type':'shortnodepie' 241 | }, 242 | Edge: { 243 | 'type':'none' 244 | }, 245 | //Parent-children distance 246 | levelDistance: 15, 247 | //Don't create labels for this visualization 248 | withLabels: false, 249 | //Don't clear the canvas when plotting 250 | clearCanvas: false 251 | }); 252 | //load graph. 253 | pie.loadJSON(jsonpie); 254 | pie.compute(); 255 | //end 256 | 257 | //init st 258 | var st = new $jit.ST({ 259 | useCanvas: pie.canvas, 260 | orientation: 'bottom', 261 | //Add node/edge styles 262 | Node: { 263 | type: 'piechart', 264 | width: 60, 265 | height: 60 266 | }, 267 | Edge: { 268 | color: '#999', 269 | type: 'quadratic:begin' 270 | }, 271 | //Parent-children distance 272 | levelDistance: 60, 273 | 274 | //Add styles to node labels on label creation 275 | onCreateLabel: function(domElement, node){ 276 | //add some styles to the node label 277 | var style = domElement.style; 278 | domElement.id = node.id; 279 | style.color = '#fff'; 280 | style.fontSize = '0.8em'; 281 | style.textAlign = 'center'; 282 | style.width = "60px"; 283 | style.height = "24px"; 284 | style.paddingTop = "22px"; 285 | style.cursor = 'pointer'; 286 | domElement.innerHTML = node.name; 287 | domElement.onclick = function() { 288 | st.onClick(node.id, { 289 | Move: { 290 | offsetY: -90 291 | } 292 | }); 293 | }; 294 | } 295 | }); 296 | //load json data 297 | st.loadJSON(json); 298 | //compute node positions and layout 299 | st.compute(); 300 | //optional: make a translation of the tree 301 | st.geom.translate(new $jit.Complex(0, 200), "start"); 302 | //Emulate a click on the root node. 303 | st.onClick(st.root, { 304 | Move: { 305 | offsetY: -90 306 | } 307 | }); 308 | //end 309 | } 310 | -------------------------------------------------------------------------------- /jit/Examples/PieChart/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PieChart - Pie Chart Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Pie Chart Example 30 |

    31 | 32 | A static Pie Chart example with gradients that displays tooltips when hovering the stacks.

    33 | Click the Update button to update the JSON data. 34 | 35 |
    36 | 37 | Update Data 38 | 39 | 40 | 41 |
    See the Example Code
    42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |
    49 | 50 |
    51 | 52 |
    53 | 54 |
    55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /jit/Examples/PieChart/example1.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | var json = { 32 | 'label': ['label A', 'label B', 'label C', 'label D'], 33 | 'values': [ 34 | { 35 | 'label': 'date A', 36 | 'values': [20, 40, 15, 5] 37 | }, 38 | { 39 | 'label': 'date B', 40 | 'values': [30, 10, 45, 10] 41 | }, 42 | { 43 | 'label': 'date E', 44 | 'values': [38, 20, 35, 17] 45 | }, 46 | { 47 | 'label': 'date F', 48 | 'values': [58, 10, 35, 32] 49 | }, 50 | { 51 | 'label': 'date D', 52 | 'values': [55, 60, 34, 38] 53 | }, 54 | { 55 | 'label': 'date C', 56 | 'values': [26, 40, 25, 40] 57 | }] 58 | 59 | }; 60 | //end 61 | var json2 = { 62 | 'values': [ 63 | { 64 | 'label': 'date A', 65 | 'values': [10, 40, 15, 7] 66 | }, 67 | { 68 | 'label': 'date B', 69 | 'values': [30, 40, 45, 9] 70 | }, 71 | { 72 | 'label': 'date D', 73 | 'values': [55, 30, 34, 26] 74 | }, 75 | { 76 | 'label': 'date C', 77 | 'values': [26, 40, 85, 28] 78 | }] 79 | 80 | }; 81 | //init PieChart 82 | var pieChart = new $jit.PieChart({ 83 | //id of the visualization container 84 | injectInto: 'infovis', 85 | //whether to add animations 86 | animate: true, 87 | //offsets 88 | offset: 30, 89 | sliceOffset: 0, 90 | labelOffset: 20, 91 | //slice style 92 | type: useGradients? 'stacked:gradient' : 'stacked', 93 | //whether to show the labels for the slices 94 | showLabels:true, 95 | //resize labels according to 96 | //pie slices values set 7px as 97 | //min label size 98 | resizeLabels: 7, 99 | //label styling 100 | Label: { 101 | type: labelType, //Native or HTML 102 | size: 20, 103 | family: 'Arial', 104 | color: 'white' 105 | }, 106 | //enable tips 107 | Tips: { 108 | enable: true, 109 | onShow: function(tip, elem) { 110 | tip.innerHTML = "" + elem.name + ": " + elem.value; 111 | } 112 | } 113 | }); 114 | //load JSON data. 115 | pieChart.loadJSON(json); 116 | //end 117 | var list = $jit.id('id-list'), 118 | button = $jit.id('update'); 119 | //update json on click 120 | $jit.util.addEvent(button, 'click', function() { 121 | var util = $jit.util; 122 | if(util.hasClass(button, 'gray')) return; 123 | util.removeClass(button, 'white'); 124 | util.addClass(button, 'gray'); 125 | pieChart.updateJSON(json2); 126 | }); 127 | //dynamically add legend to list 128 | var legend = pieChart.getLegend(), 129 | listItems = []; 130 | for(var name in legend) { 131 | listItems.push('
     
    ' + name); 133 | } 134 | list.innerHTML = '
  • ' + listItems.join('
  • ') + '
  • '; 135 | } 136 | -------------------------------------------------------------------------------- /jit/Examples/RGraph/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RGraph - Tree Animation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Tree Animation 30 |

    31 | 32 | A static JSON Tree structure is used as input for this visualization.

    33 | Click on a node to move the tree and center that node.

    34 | The centered node's children are displayed in a relations list in the right column.

    35 | Use the mouse wheel to zoom and drag and drop the canvas to pan. 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/RGraph/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RGraph - Weighted Graph Animation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Weighted Graph Animation 30 |

    31 | 32 | A static JSON graph structure is used for this animation.

    33 | For each JSON node/edge the properties prefixed with the dollar sign ($) set the type of node/edge to be plotted, its style and its dimensions.

    34 | Line weights are added programmatically, onBeforePlotLine.

    35 | An Elastic transition is used instead of the linear transition for the animation. 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/RGraph/example2.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | //If a node in this JSON structure 32 | //has the "$type" or "$dim" parameters 33 | //defined it will override the "type" and 34 | //"dim" parameters globally defined in the 35 | //RGraph constructor. 36 | var json = [{ 37 | "id": "node0", 38 | "name": "node0 name", 39 | "data": { 40 | "$dim": 16.759175934208628, 41 | "some other key": "some other value" 42 | }, 43 | "adjacencies": [{ 44 | "nodeTo": "node1", 45 | "data": { 46 | "weight": 3 47 | } 48 | }, { 49 | "nodeTo": "node2", 50 | "data": { 51 | "weight": 3 52 | } 53 | }, { 54 | "nodeTo": "node3", 55 | "data": { 56 | "weight": 3 57 | } 58 | }, { 59 | "nodeTo": "node4", 60 | "data": { 61 | "$type":"arrow", 62 | "$color":"#dd99dd", 63 | "$dim":25, 64 | "weight": 1 65 | } 66 | }, { 67 | "nodeTo": "node5", 68 | "data": { 69 | "weight": 1 70 | } 71 | }] 72 | }, { 73 | "id": "node1", 74 | "name": "node1 name", 75 | "data": { 76 | "$dim": 13.077119090372014, 77 | "$type": "square", 78 | "some other key": "some other value" 79 | }, 80 | "adjacencies": [{ 81 | "nodeTo": "node0", 82 | "data": { 83 | "weight": 3 84 | } 85 | }, { 86 | "nodeTo": "node2", 87 | "data": { 88 | "weight": 1 89 | } 90 | }, { 91 | "nodeTo": "node3", 92 | "data": { 93 | "weight": 3 94 | } 95 | }, { 96 | "nodeTo": "node4", 97 | "data": { 98 | "weight": 1 99 | } 100 | }, { 101 | "nodeTo": "node5", 102 | "data": { 103 | "weight": 1 104 | } 105 | }] 106 | }, { 107 | "id": "node2", 108 | "name": "node2 name", 109 | "data": { 110 | "$dim": 24.937383149648717, 111 | "$type": "triangle", 112 | "some other key": "some other value" 113 | }, 114 | "adjacencies": [{ 115 | "nodeTo": "node0", 116 | "data": { 117 | "weight": 3 118 | } 119 | }, { 120 | "nodeTo": "node1", 121 | "data": { 122 | "weight": 1 123 | } 124 | }, { 125 | "nodeTo": "node3", 126 | "data": { 127 | "weight": 3 128 | } 129 | }, { 130 | "nodeTo": "node4", 131 | "data": { 132 | "weight": 3 133 | } 134 | }, { 135 | "nodeTo": "node5", 136 | "data": { 137 | "weight": 1 138 | } 139 | }] 140 | }, { 141 | "id": "node3", 142 | "name": "node3 name", 143 | "data": { 144 | "$dim": 10.53272740718869, 145 | "some other key": "some other value" 146 | }, 147 | "adjacencies": [{ 148 | "nodeTo": "node0", 149 | "data": { 150 | "weight": 3 151 | } 152 | }, { 153 | "nodeTo": "node1", 154 | "data": { 155 | "weight": 3 156 | } 157 | }, { 158 | "nodeTo": "node2", 159 | "data": { 160 | "weight": 3 161 | } 162 | }, { 163 | "nodeTo": "node4", 164 | "data": { 165 | "$type":"arrow", 166 | "$direction": ["node4", "node3"], 167 | "$dim":25, 168 | "$color":"#dd99dd", 169 | "weight": 1 170 | } 171 | }, { 172 | "nodeTo": "node5", 173 | "data": { 174 | "weight": 3 175 | } 176 | }] 177 | }, { 178 | "id": "node4", 179 | "name": "node4 name", 180 | "data": { 181 | "$dim": 5.3754347037767345, 182 | "$type":"triangle", 183 | "some other key": "some other value" 184 | }, 185 | "adjacencies": [{ 186 | "nodeTo": "node0", 187 | "data": { 188 | "weight": 1 189 | } 190 | }, { 191 | "nodeTo": "node1", 192 | "data": { 193 | "weight": 1 194 | } 195 | }, { 196 | "nodeTo": "node2", 197 | "data": { 198 | "weight": 3 199 | } 200 | }, { 201 | "nodeTo": "node3", 202 | "data": { 203 | "weight": 1 204 | } 205 | }, { 206 | "nodeTo": "node5", 207 | "data": { 208 | "weight": 3 209 | } 210 | }] 211 | }, { 212 | "id": "node5", 213 | "name": "node5 name", 214 | "data": { 215 | "$dim": 32.26403873194912, 216 | "$type": "star", 217 | "some other key": "some other value" 218 | }, 219 | "adjacencies": [{ 220 | "nodeTo": "node0", 221 | "data": { 222 | "weight": 1 223 | } 224 | }, { 225 | "nodeTo": "node1", 226 | "data": { 227 | "weight": 1 228 | } 229 | }, { 230 | "nodeTo": "node2", 231 | "data": { 232 | "weight": 1 233 | } 234 | }, { 235 | "nodeTo": "node3", 236 | "data": { 237 | "weight": 3 238 | } 239 | }, { 240 | "nodeTo": "node4", 241 | "data": { 242 | "weight": 3 243 | } 244 | }] 245 | }]; 246 | //end 247 | //init RGraph 248 | var rgraph = new $jit.RGraph({ 249 | 'injectInto': 'infovis', 250 | //Optional: Add a background canvas 251 | //that draws some concentric circles. 252 | 'background': { 253 | 'CanvasStyles': { 254 | 'strokeStyle': '#555', 255 | 'shadowBlur': 50, 256 | 'shadowColor': '#ccc' 257 | } 258 | }, 259 | //Nodes and Edges parameters 260 | //can be overridden if defined in 261 | //the JSON input data. 262 | //This way we can define different node 263 | //types individually. 264 | Node: { 265 | 'overridable': true, 266 | 'color': '#cc0000' 267 | }, 268 | Edge: { 269 | 'overridable': true, 270 | 'color': '#cccc00' 271 | }, 272 | //Set polar interpolation. 273 | //Default's linear. 274 | interpolation: 'polar', 275 | //Change the transition effect from linear 276 | //to elastic. 277 | transition: $jit.Trans.Elastic.easeOut, 278 | //Change other animation parameters. 279 | duration:3500, 280 | fps: 30, 281 | //Change father-child distance. 282 | levelDistance: 200, 283 | //This method is called right before plotting 284 | //an edge. This method is useful to change edge styles 285 | //individually. 286 | onBeforePlotLine: function(adj){ 287 | //Add some random lineWidth to each edge. 288 | if (!adj.data.$lineWidth) 289 | adj.data.$lineWidth = Math.random() * 5 + 1; 290 | }, 291 | 292 | onBeforeCompute: function(node){ 293 | Log.write("centering " + node.name + "..."); 294 | 295 | //Make right column relations list. 296 | var html = "

    " + node.name + "

    Connections:"; 297 | html += ""; 303 | $jit.id('inner-details').innerHTML = html; 304 | }, 305 | //Add node click handler and some styles. 306 | //This method is called only once for each node/label crated. 307 | onCreateLabel: function(domElement, node){ 308 | domElement.innerHTML = node.name; 309 | domElement.onclick = function () { 310 | rgraph.onClick(node.id, { 311 | hideLabels: false, 312 | onComplete: function() { 313 | Log.write("done"); 314 | } 315 | }); 316 | }; 317 | var style = domElement.style; 318 | style.cursor = 'pointer'; 319 | style.fontSize = "0.8em"; 320 | style.color = "#fff"; 321 | }, 322 | //This method is called when rendering/moving a label. 323 | //This is method is useful to make some last minute changes 324 | //to node labels like adding some position offset. 325 | onPlaceLabel: function(domElement, node){ 326 | var style = domElement.style; 327 | var left = parseInt(style.left); 328 | var w = domElement.offsetWidth; 329 | style.left = (left - w / 2) + 'px'; 330 | } 331 | }); 332 | //load graph. 333 | rgraph.loadJSON(json, 1); 334 | 335 | //compute positions and plot 336 | rgraph.refresh(); 337 | //end 338 | rgraph.controller.onBeforeCompute(rgraph.graph.getNode(rgraph.root)); 339 | Log.write('done'); 340 | 341 | } 342 | -------------------------------------------------------------------------------- /jit/Examples/RGraph/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RGraph - Graph Operations 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Graph Operations 30 |

    31 | 32 | You can do the following operations with the RGraph

    33 | 1.- Removing subtrees or nodes

    34 | 2.- Removing edges

    35 | 3.- Adding another graph, also called sum

    36 | 4.- Morphing (or transforming) the graph into another one
    37 | 38 |
    39 | 40 |
    41 | 42 | 43 |
    See the Example Code
    44 |
    45 | 46 |
    47 |
    48 |
    49 | 50 |
    51 | 52 |

    Global Options

    53 | 54 | 55 | 56 | 59 | 65 | 66 | 67 | 70 | 77 | 78 | 79 | 82 | 85 | 86 | 87 | 90 | 91 |
    57 | duration: 58 | 60 | 64 |
    68 | fps: 69 | 71 | 76 |
    80 | hide labels: 81 | 83 | 84 |
    88 | 89 |
    92 | 93 |

    1.- Remove Nodes

    94 | 95 | 96 | 97 | 100 | 108 | 109 | 110 | 113 | 116 | 117 |
    98 | type: 99 | 101 | 107 |
    111 | 112 | 114 | 115 |
    118 | 119 |

    2.- Remove Edges

    120 | 121 | 122 | 123 | 126 | 134 | 135 | 136 | 139 | 142 | 143 |
    124 | type: 125 | 127 | 133 |
    137 | 138 | 140 | 141 |
    144 | 145 |

    3.- Add Graph (Sum)

    146 | 147 | 148 | 149 | 152 | 159 | 160 | 161 | 164 | 167 | 168 |
    150 | type: 151 | 153 | 158 |
    162 | 163 | 165 | 166 |
    169 | 170 |

    4.- Morph

    171 | 172 | 173 | 174 | 177 | 183 | 184 | 185 | 188 | 191 | 192 |
    175 | type: 176 | 178 | 182 |
    186 | 187 | 189 | 190 |
    193 | 194 |
    195 | 196 |
    197 |
    198 | 199 | 200 | -------------------------------------------------------------------------------- /jit/Examples/RGraph/example4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | RGraph - Node Events 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Node Events 30 |

    31 | 32 | This example shows how to add node events to the visualization.

    33 | This example uses native canvas text for drawing the labels.

    34 | Drag and drop nodes around. 35 | 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Spacetree/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spacetree - Tree Animation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Tree Animation 30 |

    31 | 32 | A static JSON Tree structure is used as input for this animation.

    33 | Click on a node to select it.

    34 | You can select the tree orientation by changing the select box in the right column.

    35 | You can change the selection mode from Normal selection (i.e. center the selected node) to 36 | Set as Root.

    37 | Drag and Drop the canvas to do some panning.

    38 | Leaves color depend on the number of children they actually have. 39 | 40 |
    41 | 42 |
    43 | 44 | 45 |
    See the Example Code
    46 |
    47 | 48 |
    49 |
    50 |
    51 | 52 |
    53 | 54 |

    Tree Orientation

    55 | 56 | 57 | 60 | 63 | 64 | 65 | 68 | 71 | 72 | 73 | 76 | 79 | 80 | 81 | 84 | 87 | 88 |
    58 | 59 | 61 | 62 |
    66 | 67 | 69 | 70 |
    74 | 75 | 77 | 78 |
    82 | 83 | 85 | 86 |
    89 | 90 |

    Selection Mode

    91 | 92 | 93 | 96 | 99 | 100 | 101 | 104 | 107 | 108 |
    94 | 95 | 97 | 98 |
    102 | 103 | 105 | 106 |
    109 | 110 |
    111 | 112 |
    113 |
    114 | 115 | 116 | -------------------------------------------------------------------------------- /jit/Examples/Spacetree/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spacetree - SpaceTree with on-demand nodes 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | SpaceTree with on-demand nodes 30 |

    31 | 32 | This example shows how you can use the request controller method to create a SpaceTree with on demand nodes

    33 | The basic JSON Tree structure is cloned and appended on demand on each node to create an infinite large SpaceTree

    34 | You can select the tree orientation by changing the select box in the right column. 35 | 36 |
    37 | 38 |
    39 | 40 | 41 |
    See the Example Code
    42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |
    49 | 50 |

    Change Tree Orientation

    51 | 52 | 53 | 56 | 59 | 60 | 61 | 64 | 67 | 68 | 71 | 74 | 75 | 76 | 79 | 82 | 83 |
    54 | 55 | 57 | 58 |
    62 | 63 | 65 | 66 |
    69 | 70 | 72 | 73 |
    77 | 78 | 80 | 81 |
    84 | 85 |
    86 | 87 |
    88 |
    89 | 90 | 91 | -------------------------------------------------------------------------------- /jit/Examples/Spacetree/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spacetree - Add/Remove Subtrees 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Add/Remove Subtrees 30 |

    31 | 32 | This example shows how to add/remove subtrees with the SpaceTree.

    33 | Add a subtree by clicking on the Add button located in the right column.

    34 | Remove a subtree by clicking on a red colored node 35 | 36 |
    37 | 38 |
    39 | 40 | 41 |
    See the Example Code
    42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |
    49 | 50 |

    Add Subtrees

    51 | 52 | 53 | 56 | 59 | 60 | 61 | 64 | 67 | 68 |
    54 | Animate: 55 | 57 | 58 |
    62 | 63 | 65 | 66 |
    69 | 70 |
    71 | 72 |
    73 |
    74 | 75 | 76 | -------------------------------------------------------------------------------- /jit/Examples/Spacetree/example4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spacetree - MultiTree 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | MultiTree 30 |

    31 | 32 | A static JSON Tree structure is used as input for this animation.

    33 | By setting the specific orientation for nodes we can create a multitree structure.

    34 | Nodes and Edges are styled with canvas specific styles like shadows.

    35 | Click on a node to select it.

    36 | You can change the selection mode from Normal selection (i.e. center the selected node) to 37 | Set as Root. 38 | 39 |
    40 | 41 |
    42 | 43 | 44 |
    See the Example Code
    45 |
    46 | 47 |
    48 |
    49 |
    50 | 51 |
    52 | 53 |

    Selection Mode

    54 | 55 | 56 | 59 | 62 | 63 | 64 | 67 | 70 | 71 |
    57 | 58 | 60 | 61 |
    65 | 66 | 68 | 69 |
    72 | 73 |
    74 | 75 |
    76 |
    77 | 78 | 79 | -------------------------------------------------------------------------------- /jit/Examples/Spacetree/example5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Spacetree - Style Animations 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Style Animations 30 |

    31 | 32 | This Advanced Example shows how Node, Edge, Label and Canvas specific style animations can be triggered for this 33 | visualization.

    34 | Select the styles to be animated in the right column and hit the Morph Styles button. This will 35 | set random values for these properties and animate them.

    36 | Click on Restore Styles to set the default styles.

    37 | Other styles like alpha and shadows can also be triggered.

    38 | This example also implements a custom node rendering function for Stroke + Fill rectangles. 39 | 40 |
    41 | 42 |
    43 | 44 | 45 |
    See the Example Code
    46 |
    47 | 48 |
    49 |
    50 |
    51 | 52 |
    53 | 54 |

    Actions

    55 | Morph Styles 56 | Restore Styles 57 | 58 |

    Node Styles

    59 | 60 | 61 | 64 | 67 | 68 | 69 | 72 | 75 | 76 | 77 | 80 | 83 | 84 | 85 | 88 | 91 | 92 | 93 | 96 | 99 | 100 | 101 | 104 | 107 | 108 |
    62 | 63 | 65 | 66 |
    70 | 71 | 73 | 74 |
    78 | 79 | 81 | 82 |
    86 | 87 | 89 | 90 |
    94 | 95 | 97 | 98 |
    102 | 103 | 105 | 106 |
    109 | 110 |

    Edge Styles

    111 | 112 | 113 | 116 | 119 | 120 | 121 | 124 | 127 | 128 | 129 | 132 | 135 | 136 |
    114 | 115 | 117 | 118 |
    122 | 123 | 125 | 126 |
    130 | 131 | 133 | 134 |
    137 | 138 |

    Label Styles

    139 | 140 | 141 | 144 | 147 | 148 | 149 | 152 | 155 | 156 | 157 | 160 | 163 | 164 |
    142 | 143 | 145 | 146 |
    150 | 151 | 153 | 154 |
    158 | 159 | 161 | 162 |
    165 | 166 | 167 |
    168 | 169 |
    170 |
    171 | 172 | 173 | -------------------------------------------------------------------------------- /jit/Examples/Sunburst/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sunburst - Connected Sunburst 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Connected Sunburst 30 |

    31 | 32 | A static JSON Graph structure is used as input for this visualization.

    33 | This example shows how properties such as color, height, angular width and line width 34 | can be customized per node and per edge in the JSON structure.

    35 | Left click to select a node and show its relations. 36 | 37 |
    38 | 39 |
    40 | 41 | 42 |
    See the Example Code
    43 |
    44 | 45 |
    46 |
    47 |
    48 | 49 |
    50 | 51 |
    52 | 53 |
    54 | 55 |
    56 |
    57 | 58 | 59 | -------------------------------------------------------------------------------- /jit/Examples/Sunburst/example1.js: -------------------------------------------------------------------------------- 1 | var labelType, useGradients, nativeTextSupport, animate; 2 | 3 | (function() { 4 | var ua = navigator.userAgent, 5 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 6 | typeOfCanvas = typeof HTMLCanvasElement, 7 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 8 | textSupport = nativeCanvasSupport 9 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 10 | //I'm setting this based on the fact that ExCanvas provides text support for IE 11 | //and that as of today iPhone/iPad current text support is lame 12 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 13 | nativeTextSupport = labelType == 'Native'; 14 | useGradients = nativeCanvasSupport; 15 | animate = !(iStuff || !nativeCanvasSupport); 16 | })(); 17 | 18 | var Log = { 19 | elem: false, 20 | write: function(text){ 21 | if (!this.elem) 22 | this.elem = document.getElementById('log'); 23 | this.elem.innerHTML = text; 24 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 25 | } 26 | }; 27 | 28 | 29 | function init(){ 30 | //init data 31 | var json = [ 32 | //"root" node is invisible 33 | { 34 | "id": "node0", 35 | "name": "", 36 | "data": { 37 | "$type": "none" 38 | }, 39 | "adjacencies": [ 40 | { 41 | "nodeTo": "node1", 42 | "data": { 43 | '$type': 'none' 44 | } 45 | }, { 46 | "nodeTo": "node2", 47 | "data": { 48 | '$type': 'none' 49 | } 50 | }, { 51 | "nodeTo": "node3", 52 | "data": { 53 | '$type': 'none' 54 | } 55 | }, { 56 | "nodeTo": "node4", 57 | "data": { 58 | "$type": "none" 59 | } 60 | }, { 61 | "nodeTo": "node5", 62 | "data": { 63 | "$type": "none" 64 | } 65 | }, { 66 | "nodeTo": "node6", 67 | "data": { 68 | "$type": "none" 69 | } 70 | }, { 71 | "nodeTo": "node7", 72 | "data": { 73 | "$type": "none" 74 | } 75 | }, { 76 | "nodeTo": "node8", 77 | "data": { 78 | "$type": "none" 79 | } 80 | }, { 81 | "nodeTo": "node9", 82 | "data": { 83 | "$type": "none" 84 | } 85 | }, { 86 | "nodeTo": "node10", 87 | "data": { 88 | "$type": "none" 89 | } 90 | } 91 | ] 92 | }, { 93 | "id": "node1", 94 | "name": "node 1", 95 | "data": { 96 | "$angularWidth": 13.00, 97 | "$color": "#33a", 98 | "$height": 70 99 | }, 100 | "adjacencies": [ 101 | { 102 | "nodeTo": "node3", 103 | "data": { 104 | "$color": "#ddaacc", 105 | "$lineWidth": 4 106 | } 107 | }, { 108 | "nodeTo": "node5", 109 | "data": { 110 | "$color": "#ccffdd", 111 | "$lineWidth": 4 112 | } 113 | }, { 114 | "nodeTo": "node7", 115 | "data": { 116 | "$color": "#dd99dd", 117 | "$lineWidth": 4 118 | } 119 | }, { 120 | "nodeTo": "node8", 121 | "data": { 122 | "$color": "#dd99dd", 123 | "$lineWidth": 4 124 | } 125 | }, { 126 | "nodeTo": "node10", 127 | "data": { 128 | "$color": "#ddaacc", 129 | "$lineWidth": 4 130 | } 131 | } 132 | ] 133 | }, { 134 | "id": "node2", 135 | "name": "node 2", 136 | "data": { 137 | "$angularWidth": 24.90, 138 | "$color": "#55b", 139 | "$height": 73 140 | }, 141 | "adjacencies": [ 142 | "node8", "node9", "node10" 143 | ] 144 | }, { 145 | "id": "node3", 146 | "name": "node 3", 147 | "data": { 148 | "$angularWidth": 10.50, 149 | "$color": "#77c", 150 | "$height": 75 151 | }, 152 | "adjacencies": [ 153 | "node8", "node9", "node10" 154 | ] 155 | }, { 156 | "id": "node4", 157 | "name": "node 4", 158 | "data": { 159 | "$angularWidth": 5.40, 160 | "$color": "#99d", 161 | "$height": 75 162 | }, 163 | "adjacencies": [ 164 | "node8", "node9", "node10" 165 | ] 166 | }, { 167 | "id": "node5", 168 | "name": "node 5", 169 | "data": { 170 | "$angularWidth": 32.26, 171 | "$color": "#aae", 172 | "$height": 80 173 | }, 174 | "adjacencies": [ 175 | "node8", "node9", "node10" 176 | ] 177 | }, { 178 | "id": "node6", 179 | "name": "node 6", 180 | "data": { 181 | "$angularWidth": 24.90, 182 | "$color": "#bf0", 183 | "$height": 85 184 | }, 185 | "adjacencies": [ 186 | "node8", "node9", "node10" 187 | ] 188 | }, { 189 | "id": "node7", 190 | "name": "node 7", 191 | "data": { 192 | "$angularWidth": 14.90, 193 | "$color": "#cf5", 194 | "$height": 85 195 | }, 196 | "adjacencies": [ 197 | "node8", "node9", "node10" 198 | ] 199 | }, { 200 | "id": "node8", 201 | "name": "node 8", 202 | "data": { 203 | "$angularWidth": 34.90, 204 | "$color": "#dfa", 205 | "$height": 80 206 | }, 207 | "adjacencies": [ 208 | "node9", "node10" 209 | ] 210 | }, { 211 | "id": "node9", 212 | "name": "node 9", 213 | "data": { 214 | "$angularWidth": 42.90, 215 | "$color": "#CCC", 216 | "$height": 75 217 | }, 218 | "adjacencies": [ 219 | "node10" 220 | ] 221 | }, { 222 | "id": "node10", 223 | "name": "node 10", 224 | "data": { 225 | "$angularWidth": 100.90, 226 | "$color": "#C37", 227 | "$height": 70 228 | }, 229 | "adjacencies": [] 230 | } 231 | ]; 232 | //end 233 | //init Sunburst 234 | var sb = new $jit.Sunburst({ 235 | //id container for the visualization 236 | injectInto: 'infovis', 237 | //Change node and edge styles such as 238 | //color, width, lineWidth and edge types 239 | Node: { 240 | overridable: true, 241 | type: useGradients? 'gradient-multipie' : 'multipie' 242 | }, 243 | Edge: { 244 | overridable: true, 245 | type: 'hyperline', 246 | lineWidth: 2, 247 | color: '#777' 248 | }, 249 | //Draw canvas text. Can also be 250 | //'HTML' or 'SVG' to draw DOM labels 251 | Label: { 252 | type: nativeTextSupport? 'Native' : 'SVG' 253 | }, 254 | //Add animations when hovering and clicking nodes 255 | NodeStyles: { 256 | enable: true, 257 | type: 'Native', 258 | stylesClick: { 259 | 'color': '#33dddd' 260 | }, 261 | stylesHover: { 262 | 'color': '#dd3333' 263 | }, 264 | duration: 700 265 | }, 266 | Events: { 267 | enable: true, 268 | type: 'Native', 269 | //List node connections onClick 270 | onClick: function(node, eventInfo, e){ 271 | if (!node) return; 272 | var html = "

    " + node.name + " connections

    "; 280 | } 281 | }, 282 | levelDistance: 190, 283 | // Only used when Label type is 'HTML' or 'SVG' 284 | // Add text to the labels. 285 | // This method is only triggered on label creation 286 | onCreateLabel: function(domElement, node){ 287 | var labels = sb.config.Label.type; 288 | if (labels === 'HTML') { 289 | domElement.innerHTML = node.name; 290 | } else if (labels === 'SVG') { 291 | domElement.firstChild.appendChild(document.createTextNode(node.name)); 292 | } 293 | }, 294 | // Only used when Label type is 'HTML' or 'SVG' 295 | // Change node styles when labels are placed 296 | // or moved. 297 | onPlaceLabel: function(domElement, node){ 298 | var labels = sb.config.Label.type; 299 | if (labels === 'SVG') { 300 | var fch = domElement.firstChild; 301 | var style = fch.style; 302 | style.display = ''; 303 | style.cursor = 'pointer'; 304 | style.fontSize = "0.8em"; 305 | fch.setAttribute('fill', "#fff"); 306 | } else if (labels === 'HTML') { 307 | var style = domElement.style; 308 | style.display = ''; 309 | style.cursor = 'pointer'; 310 | if (node._depth <= 1) { 311 | style.fontSize = "0.8em"; 312 | style.color = "#ddd"; 313 | } 314 | var left = parseInt(style.left); 315 | var w = domElement.offsetWidth; 316 | style.left = (left - w / 2) + 'px'; 317 | } 318 | } 319 | }); 320 | // load JSON data. 321 | sb.loadJSON(json); 322 | // compute positions and plot. 323 | sb.refresh(); 324 | //end 325 | } 326 | -------------------------------------------------------------------------------- /jit/Examples/Sunburst/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sunburst - Sunburst of a Directory Tree 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Sunburst of a Directory Tree 30 |

    31 | 32 | A static JSON Tree structure is used as input for this visualization.

    33 | Tips are used to describe the file size and its last modified date.

    34 | Left click to rotate the Sunburst to the selected node and see its details. 35 | 36 |
    37 | 38 |
    39 | 40 | 41 |
    See the Example Code
    42 |
    43 | 44 |
    45 |
    46 |
    47 | 48 |
    49 | 50 |
    51 | 52 |
    53 | 54 |
    55 |
    56 | 57 | 58 | -------------------------------------------------------------------------------- /jit/Examples/Treemap/example1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Treemap - Animated Squarified, SliceAndDice and Strip TreeMaps 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Animated Squarified, SliceAndDice and Strip TreeMaps 30 |

    31 | 32 | In this example a static JSON tree is loaded into a Squarified Treemap.

    33 | Left click to set a node as root for the visualization.

    34 | Right click to set the parent node as root for the visualization.

    35 | You can choose a different tiling algorithm below: 36 | 37 | 38 |
    39 | 40 |
    41 | 42 | 43 | 46 | 49 | 50 | 51 | 54 | 57 | 58 | 61 | 64 | 65 |
    44 | 45 | 47 | 48 |
    52 | 53 | 55 | 56 |
    59 | 60 | 62 | 63 |
    66 |
    67 | 68 | Go to Parent 69 | 70 | 71 |
    See the Example Code
    72 |
    73 | 74 |
    75 |
    76 |
    77 | 78 |
    79 | 80 |
    81 | 82 |
    83 | 84 |
    85 |
    86 | 87 | 88 | -------------------------------------------------------------------------------- /jit/Examples/Treemap/example2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Treemap - TreeMap with on-demand nodes 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | TreeMap with on-demand nodes 30 |

    31 | 32 | This example shows how you can use the request controller method to create a TreeMap with on demand nodes

    33 | This example makes use of native Canvas text and shadows, but can be easily adapted to use HTML like the other examples.

    34 | There should be only one level shown at a time.

    35 | Clicking on a band should show a new TreeMap with its most listened albums.

    36 | 37 |
    38 | 39 |
    40 | 41 | 42 | 45 | 48 | 49 | 50 | 53 | 56 | 57 | 60 | 63 | 64 |
    43 | 44 | 46 | 47 |
    51 | 52 | 54 | 55 |
    58 | 59 | 61 | 62 |
    65 |
    66 | 67 | Go to Parent 68 | 69 | 70 |
    See the Example Code
    71 |
    72 | 73 |
    74 |
    75 |
    76 | 77 |
    78 | 79 |
    80 | 81 |
    82 | 83 |
    84 |
    85 | 86 | 87 | -------------------------------------------------------------------------------- /jit/Examples/Treemap/example3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Treemap - Cushion TreeMaps 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    22 | 23 |
    24 | 25 | 26 | 27 |
    28 |

    29 | Cushion TreeMaps 30 |

    31 | 32 | In this example a static JSON tree is loaded into a Cushion Treemap.

    33 | Left click to set a node as root for the visualization.

    34 | Right click to set the parent node as root for the visualization.

    35 | You can choose a different tiling algorithm below: 36 | 37 | 38 |
    39 | 40 |
    41 | 42 | 43 | 46 | 49 | 50 | 51 | 54 | 57 | 58 | 61 | 64 | 65 |
    44 | 45 | 47 | 48 |
    52 | 53 | 55 | 56 |
    59 | 60 | 62 | 63 |
    66 |
    67 | 68 | Go to Parent 69 | 70 | 71 |
    See the Example Code
    72 |
    73 | 74 |
    75 |
    76 |
    77 | 78 |
    79 | 80 |
    81 | 82 |
    83 | 84 |
    85 |
    86 | 87 | 88 | -------------------------------------------------------------------------------- /jit/Examples/css/AreaChart.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | } 8 | 9 | #infovis { 10 | width:800px; 11 | } 12 | 13 | #id-list { 14 | background-color:#EEEEEE; 15 | border:1px solid #CCCCCC; 16 | margin:10px 20px 0 20px; 17 | padding:5px; 18 | text-align:left; 19 | text-indent:2px; 20 | } 21 | 22 | #id-list li { 23 | list-style: none; 24 | margin-bottom:3px; 25 | 26 | } 27 | 28 | .query-color { 29 | border:1px solid #999999; 30 | float:left; 31 | height:10px; 32 | margin:3px 3px 0 0; 33 | width:10px; 34 | } 35 | 36 | #update, #restore { 37 | text-align: center; 38 | width: 100px; 39 | margin:10px 35px; 40 | } 41 | 42 | .button { 43 | display: inline-block; 44 | outline: none; 45 | cursor: pointer; 46 | text-align: center; 47 | text-decoration: none; 48 | font: 14px / 100% Arial, Helvetica, sans-serif; 49 | padding: 0.5em 1em 0.55em; 50 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 51 | -webkit-border-radius: 0.5em; 52 | -moz-border-radius: 0.5em; 53 | border-radius: 0.5em; 54 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 55 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 56 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 57 | } 58 | 59 | .button:hover { 60 | text-decoration: none; 61 | } 62 | 63 | .button:active { 64 | position: relative; 65 | top: 1px; 66 | } 67 | 68 | /* gray */ 69 | .gray { 70 | color: #e9e9e9; 71 | border: solid 1px #555; 72 | background: #6e6e6e; 73 | background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757)); 74 | background: -moz-linear-gradient(top, #888, #575757); 75 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757'); 76 | } 77 | 78 | .gray:hover { 79 | background: #616161; 80 | background: -webkit-gradient(linear, left top, left bottom, from(#757575), to(#4b4b4b)); 81 | background: -moz-linear-gradient(top, #757575, #4b4b4b); 82 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#757575', endColorstr='#4b4b4b'); 83 | } 84 | 85 | .gray:active { 86 | color: #afafaf; 87 | background: -webkit-gradient(linear, left top, left bottom, from(#575757), to(#888)); 88 | background: -moz-linear-gradient(top, #575757, #888); 89 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#575757', endColorstr='#888888'); 90 | } 91 | 92 | /* white */ 93 | .white { 94 | color: #606060; 95 | border: solid 1px #b7b7b7; 96 | background: #fff; 97 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 98 | background: -moz-linear-gradient(top, #fff, #ededed); 99 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 100 | } 101 | 102 | .white:hover { 103 | background: #ededed; 104 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 105 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 106 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 107 | } 108 | 109 | .white:active { 110 | color: #999; 111 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 112 | background: -moz-linear-gradient(top, #ededed, #fff); 113 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 114 | } 115 | -------------------------------------------------------------------------------- /jit/Examples/css/BarChart.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | } 8 | 9 | #infovis { 10 | width:800px; 11 | } 12 | 13 | #id-list { 14 | background-color:#EEEEEE; 15 | border:1px solid #CCCCCC; 16 | margin:10px 20px 0 20px; 17 | padding:5px; 18 | text-align:left; 19 | text-indent:2px; 20 | } 21 | 22 | #id-list li { 23 | list-style: none; 24 | margin-bottom:3px; 25 | 26 | } 27 | 28 | .query-color { 29 | border:1px solid #999999; 30 | float:left; 31 | height:10px; 32 | margin:3px 3px 0 0; 33 | width:10px; 34 | } 35 | 36 | #update { 37 | margin:10px 40px; 38 | } 39 | 40 | .button { 41 | display: inline-block; 42 | outline: none; 43 | cursor: pointer; 44 | text-align: center; 45 | text-decoration: none; 46 | font: 14px / 100% Arial, Helvetica, sans-serif; 47 | padding: 0.5em 1em 0.55em; 48 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 49 | -webkit-border-radius: 0.5em; 50 | -moz-border-radius: 0.5em; 51 | border-radius: 0.5em; 52 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 53 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 54 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 55 | } 56 | 57 | .button:hover { 58 | text-decoration: none; 59 | } 60 | 61 | .button:active { 62 | position: relative; 63 | top: 1px; 64 | } 65 | 66 | /* gray */ 67 | .gray { 68 | color: #e9e9e9; 69 | border: solid 1px #555; 70 | background: #6e6e6e; 71 | background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757)); 72 | background: -moz-linear-gradient(top, #888, #575757); 73 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757'); 74 | } 75 | 76 | .gray:hover { 77 | background: #616161; 78 | background: -webkit-gradient(linear, left top, left bottom, from(#757575), to(#4b4b4b)); 79 | background: -moz-linear-gradient(top, #757575, #4b4b4b); 80 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#757575', endColorstr='#4b4b4b'); 81 | } 82 | 83 | .gray:active { 84 | color: #afafaf; 85 | background: -webkit-gradient(linear, left top, left bottom, from(#575757), to(#888)); 86 | background: -moz-linear-gradient(top, #575757, #888); 87 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#575757', endColorstr='#888888'); 88 | } 89 | 90 | /* white */ 91 | .white { 92 | color: #606060; 93 | border: solid 1px #b7b7b7; 94 | background: #fff; 95 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 96 | background: -moz-linear-gradient(top, #fff, #ededed); 97 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 98 | } 99 | 100 | .white:hover { 101 | background: #ededed; 102 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 103 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 104 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 105 | } 106 | 107 | .white:active { 108 | color: #999; 109 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 110 | background: -moz-linear-gradient(top, #ededed, #fff); 111 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 112 | } 113 | -------------------------------------------------------------------------------- /jit/Examples/css/ForceDirected.css: -------------------------------------------------------------------------------- 1 | #inner-details { 2 | font-size:12px; 3 | } 4 | 5 | span.close { 6 | color:#FF5555; 7 | cursor:pointer; 8 | font-weight:bold; 9 | margin-left:3px; 10 | } 11 | 12 | span.name { 13 | cursor: pointer; 14 | } 15 | 16 | /*TOOLTIPS*/ 17 | .tip { 18 | text-align: left; 19 | width:auto; 20 | max-width:500px; 21 | } 22 | 23 | .tip-title { 24 | font-size: 11px; 25 | text-align:center; 26 | margin-bottom:2px; 27 | } 28 | -------------------------------------------------------------------------------- /jit/Examples/css/ForceDirected3D.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | background: #efefef; 8 | } 9 | 10 | #infovis { 11 | width:800px; 12 | } -------------------------------------------------------------------------------- /jit/Examples/css/HeatMap.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabwe/words/65b81a28d84339e53f8379a79164a39a3573904d/jit/Examples/css/HeatMap.css -------------------------------------------------------------------------------- /jit/Examples/css/Hypertree.css: -------------------------------------------------------------------------------- 1 | #infovis-canvaswidget { 2 | margin:25px 0 0 25px; 3 | } -------------------------------------------------------------------------------- /jit/Examples/css/Icicle.css: -------------------------------------------------------------------------------- 1 | #update { 2 | margin:10px 40px; 3 | } 4 | 5 | .button { 6 | display: inline-block; 7 | outline: none; 8 | cursor: pointer; 9 | text-align: center; 10 | text-decoration: none; 11 | font: 14px / 100% Arial, Helvetica, sans-serif; 12 | padding: 0.5em 1em 0.55em; 13 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 14 | -webkit-border-radius: 0.5em; 15 | -moz-border-radius: 0.5em; 16 | border-radius: 0.5em; 17 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 18 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 19 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 20 | } 21 | 22 | .button:hover { 23 | text-decoration: none; 24 | } 25 | 26 | .button:active { 27 | position: relative; 28 | top: 1px; 29 | } 30 | 31 | /* white */ 32 | .white { 33 | color: #606060; 34 | border: solid 1px #b7b7b7; 35 | background: #fff; 36 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 37 | background: -moz-linear-gradient(top, #fff, #ededed); 38 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 39 | } 40 | 41 | .white:hover { 42 | background: #ededed; 43 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 44 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 45 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 46 | } 47 | 48 | .white:active { 49 | color: #999; 50 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 51 | background: -moz-linear-gradient(top, #ededed, #fff); 52 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 53 | } 54 | 55 | 56 | .tip { 57 | text-align: left; 58 | width:auto; 59 | max-width:500px; 60 | } 61 | 62 | .tip-title { 63 | font-size: 11px; 64 | text-align:center; 65 | margin-bottom:2px; 66 | } 67 | 68 | #right-container { 69 | display: none; 70 | } 71 | 72 | #center-container { 73 | width:800px; 74 | } 75 | 76 | #infovis { 77 | width:800px; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /jit/Examples/css/Other.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display:none; 3 | } 4 | 5 | #infovis { 6 | width:800px; 7 | background-color:#1a1a1a; 8 | } 9 | -------------------------------------------------------------------------------- /jit/Examples/css/PieChart.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | } 8 | 9 | #infovis { 10 | width:800px; 11 | } 12 | 13 | #id-list { 14 | background-color:#EEEEEE; 15 | border:1px solid #CCCCCC; 16 | margin:10px 20px 0 20px; 17 | padding:5px; 18 | text-align:left; 19 | text-indent:2px; 20 | } 21 | 22 | #id-list li { 23 | list-style: none; 24 | margin-bottom:3px; 25 | 26 | } 27 | 28 | .query-color { 29 | border:1px solid #999999; 30 | float:left; 31 | height:10px; 32 | margin:3px 3px 0 0; 33 | width:10px; 34 | } 35 | 36 | #update { 37 | margin:10px 40px; 38 | } 39 | 40 | .button { 41 | display: inline-block; 42 | outline: none; 43 | cursor: pointer; 44 | text-align: center; 45 | text-decoration: none; 46 | font: 14px / 100% Arial, Helvetica, sans-serif; 47 | padding: 0.5em 1em 0.55em; 48 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 49 | -webkit-border-radius: 0.5em; 50 | -moz-border-radius: 0.5em; 51 | border-radius: 0.5em; 52 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 53 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 54 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 55 | } 56 | 57 | .button:hover { 58 | text-decoration: none; 59 | } 60 | 61 | .button:active { 62 | position: relative; 63 | top: 1px; 64 | } 65 | 66 | /* gray */ 67 | .gray { 68 | color: #e9e9e9; 69 | border: solid 1px #555; 70 | background: #6e6e6e; 71 | background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757)); 72 | background: -moz-linear-gradient(top, #888, #575757); 73 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757'); 74 | } 75 | 76 | .gray:hover { 77 | background: #616161; 78 | background: -webkit-gradient(linear, left top, left bottom, from(#757575), to(#4b4b4b)); 79 | background: -moz-linear-gradient(top, #757575, #4b4b4b); 80 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#757575', endColorstr='#4b4b4b'); 81 | } 82 | 83 | .gray:active { 84 | color: #afafaf; 85 | background: -webkit-gradient(linear, left top, left bottom, from(#575757), to(#888)); 86 | background: -moz-linear-gradient(top, #575757, #888); 87 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#575757', endColorstr='#888888'); 88 | } 89 | 90 | /* white */ 91 | .white { 92 | color: #606060; 93 | border: solid 1px #b7b7b7; 94 | background: #fff; 95 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 96 | background: -moz-linear-gradient(top, #fff, #ededed); 97 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 98 | } 99 | 100 | .white:hover { 101 | background: #ededed; 102 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 103 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 104 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 105 | } 106 | 107 | .white:active { 108 | color: #999; 109 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 110 | background: -moz-linear-gradient(top, #ededed, #fff); 111 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 112 | } 113 | -------------------------------------------------------------------------------- /jit/Examples/css/RGraph.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabwe/words/65b81a28d84339e53f8379a79164a39a3573904d/jit/Examples/css/RGraph.css -------------------------------------------------------------------------------- /jit/Examples/css/Spacetree.css: -------------------------------------------------------------------------------- 1 | .jit-autoadjust-label { 2 | padding: 15px; 3 | } 4 | 5 | #update, #restore { 6 | text-align: center; 7 | width: 100px; 8 | margin:0px 35px 10px 35px; 9 | } 10 | 11 | .button { 12 | display: inline-block; 13 | outline: none; 14 | cursor: pointer; 15 | text-align: center; 16 | text-decoration: none; 17 | font: 14px / 100% Arial, Helvetica, sans-serif; 18 | padding: 0.5em 1em 0.55em; 19 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 20 | -webkit-border-radius: 0.5em; 21 | -moz-border-radius: 0.5em; 22 | border-radius: 0.5em; 23 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 24 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 25 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 26 | } 27 | 28 | .button:hover { 29 | text-decoration: none; 30 | } 31 | 32 | .button:active { 33 | position: relative; 34 | top: 1px; 35 | } 36 | 37 | /* white */ 38 | .white { 39 | color: #606060; 40 | border: solid 1px #b7b7b7; 41 | background: #fff; 42 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 43 | background: -moz-linear-gradient(top, #fff, #ededed); 44 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 45 | } 46 | 47 | .white:hover { 48 | background: #ededed; 49 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 50 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 51 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 52 | } 53 | 54 | .white:active { 55 | color: #999; 56 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 57 | background: -moz-linear-gradient(top, #ededed, #fff); 58 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 59 | } 60 | -------------------------------------------------------------------------------- /jit/Examples/css/Sunburst.css: -------------------------------------------------------------------------------- 1 | /*TOOLTIPS*/ 2 | #inner-details { 3 | font-size:12px; 4 | } 5 | 6 | .tip { 7 | text-align: left; 8 | width:auto; 9 | max-width:500px; 10 | } 11 | 12 | .tip-title { 13 | font-size: 11px; 14 | text-align:center; 15 | margin-bottom:2px; 16 | } 17 | 18 | pre { 19 | background-color:#EEEEEE; 20 | border:1px solid #CCCCCC; 21 | font-size:10px; 22 | margin:5px auto; 23 | padding:5px; 24 | width:170px; 25 | 26 | white-space: pre-wrap; 27 | white-space: -moz-pre-wrap; 28 | white-space: -webkit-pre-wrap; 29 | white-space: -pre-wrap; 30 | white-space: -o-pre-wrap; 31 | word-wrap: break-word; 32 | } -------------------------------------------------------------------------------- /jit/Examples/css/TimeGraph.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | } 8 | 9 | #infovis { 10 | width:800px; 11 | /*height: 250px;*/ 12 | } 13 | 14 | #id-list { 15 | background-color:#EEEEEE; 16 | border:1px solid #CCCCCC; 17 | margin:10px 20px 0 20px; 18 | padding:5px; 19 | text-align:left; 20 | text-indent:2px; 21 | } 22 | 23 | #id-list li { 24 | list-style: none; 25 | margin-bottom:3px; 26 | 27 | } 28 | 29 | .query-color { 30 | border:1px solid #999999; 31 | float:left; 32 | height:10px; 33 | margin:3px 3px 0 0; 34 | width:10px; 35 | } 36 | 37 | #update { 38 | margin:10px 40px; 39 | } 40 | 41 | .button { 42 | display: inline-block; 43 | outline: none; 44 | cursor: pointer; 45 | text-align: center; 46 | text-decoration: none; 47 | font: 14px / 100% Arial, Helvetica, sans-serif; 48 | padding: 0.5em 1em 0.55em; 49 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 50 | -webkit-border-radius: 0.5em; 51 | -moz-border-radius: 0.5em; 52 | border-radius: 0.5em; 53 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 54 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 55 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 56 | } 57 | 58 | .button:hover { 59 | text-decoration: none; 60 | } 61 | 62 | .button:active { 63 | position: relative; 64 | top: 1px; 65 | } 66 | 67 | /* gray */ 68 | .gray { 69 | color: #e9e9e9; 70 | border: solid 1px #555; 71 | background: #6e6e6e; 72 | background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757)); 73 | background: -moz-linear-gradient(top, #888, #575757); 74 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757'); 75 | } 76 | 77 | .gray:hover { 78 | background: #616161; 79 | background: -webkit-gradient(linear, left top, left bottom, from(#757575), to(#4b4b4b)); 80 | background: -moz-linear-gradient(top, #757575, #4b4b4b); 81 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#757575', endColorstr='#4b4b4b'); 82 | } 83 | 84 | .gray:active { 85 | color: #afafaf; 86 | background: -webkit-gradient(linear, left top, left bottom, from(#575757), to(#888)); 87 | background: -moz-linear-gradient(top, #575757, #888); 88 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#575757', endColorstr='#888888'); 89 | } 90 | 91 | /* white */ 92 | .white { 93 | color: #606060; 94 | border: solid 1px #b7b7b7; 95 | background: #fff; 96 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 97 | background: -moz-linear-gradient(top, #fff, #ededed); 98 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 99 | } 100 | 101 | .white:hover { 102 | background: #ededed; 103 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 104 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 105 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 106 | } 107 | 108 | .white:active { 109 | color: #999; 110 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 111 | background: -moz-linear-gradient(top, #ededed, #fff); 112 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 113 | } 114 | -------------------------------------------------------------------------------- /jit/Examples/css/Treemap.css: -------------------------------------------------------------------------------- 1 | #right-container { 2 | display: none; 3 | } 4 | 5 | #center-container { 6 | width:800px; 7 | } 8 | 9 | #infovis { 10 | width:800px; 11 | } 12 | 13 | .node { 14 | color:#fff; 15 | font-size:9px; 16 | overflow:hidden; 17 | cursor:pointer; 18 | /* 19 | text-shadow:2px 2px 5px #000; 20 | -o-text-shadow:2px 2px 5px #000; 21 | -webkit-text-shadow:2px 2px 5px #000; 22 | -moz-text-shadow:2px 2px 5px #000; 23 | */ 24 | } 25 | 26 | /*TOOLTIPS*/ 27 | .tip { 28 | color: #fff; 29 | width: 139px; 30 | background-color: black; 31 | opacity:0.9; 32 | filter:alpha(opacity=90); 33 | font-size:10px; 34 | font-family:Verdana, Geneva, Arial, Helvetica, sans-serif; 35 | padding:7px; 36 | } 37 | 38 | .album { 39 | width:100px; 40 | margin:3px; 41 | } 42 | 43 | #id-list { 44 | background-color:#EEEEEE; 45 | border:1px solid #CCCCCC; 46 | margin:10px 20px 0 20px; 47 | padding:5px; 48 | text-align:left; 49 | text-indent:2px; 50 | } 51 | 52 | #id-list table { 53 | margin-top:2px; 54 | } 55 | 56 | #back { 57 | margin:10px 40px; 58 | } 59 | 60 | .button { 61 | display: inline-block; 62 | outline: none; 63 | cursor: pointer; 64 | text-align: center; 65 | text-decoration: none; 66 | font: 14px / 100% Arial, Helvetica, sans-serif; 67 | padding: 0.5em 1em 0.55em; 68 | text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.3); 69 | -webkit-border-radius: 0.5em; 70 | -moz-border-radius: 0.5em; 71 | border-radius: 0.5em; 72 | -webkit-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 73 | -moz-box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 74 | box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2); 75 | } 76 | 77 | .button:hover { 78 | text-decoration: none; 79 | } 80 | 81 | .button:active { 82 | position: relative; 83 | top: 1px; 84 | } 85 | 86 | /* white */ 87 | .white { 88 | color: #606060; 89 | border: solid 1px #b7b7b7; 90 | background: #fff; 91 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#ededed)); 92 | background: -moz-linear-gradient(top, #fff, #ededed); 93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed'); 94 | } 95 | 96 | .white:hover { 97 | background: #ededed; 98 | background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#dcdcdc)); 99 | background: -moz-linear-gradient(top, #fff, #dcdcdc); 100 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#dcdcdc'); 101 | } 102 | 103 | .white:active { 104 | color: #999; 105 | background: -webkit-gradient(linear, left top, left bottom, from(#ededed), to(#fff)); 106 | background: -moz-linear-gradient(top, #ededed, #fff); 107 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#ffffff'); 108 | } 109 | 110 | -------------------------------------------------------------------------------- /jit/Examples/css/base.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin:0; 3 | padding:0; 4 | font-family: "Lucida Grande", Verdana; 5 | font-size: 0.9em; 6 | text-align: center; 7 | background-color:#F2F2F2; 8 | } 9 | 10 | input, select { 11 | font-size:0.9em; 12 | } 13 | 14 | table { 15 | margin-top:-10px; 16 | margin-left:7px; 17 | } 18 | 19 | h4 { 20 | font-size:1.1em; 21 | text-decoration:none; 22 | font-weight:normal; 23 | color:#23A4FF; 24 | } 25 | 26 | a { 27 | color:#23A4FF; 28 | } 29 | 30 | #container { 31 | width: 1000px; 32 | height: 600px; 33 | margin:0 auto; 34 | position:relative; 35 | } 36 | 37 | #left-container, 38 | #right-container, 39 | #center-container { 40 | height:600px; 41 | position:absolute; 42 | top:0; 43 | } 44 | 45 | #left-container, #right-container { 46 | width:200px; 47 | color:#686c70; 48 | text-align: left; 49 | overflow: auto; 50 | background-color:#fff; 51 | background-repeat:no-repeat; 52 | border-bottom:1px solid #ddd; 53 | } 54 | 55 | #left-container { 56 | left:0; 57 | background-image:url('col2.png'); 58 | background-position:center right; 59 | border-left:1px solid #ddd; 60 | 61 | } 62 | 63 | #right-container { 64 | right:0; 65 | background-image:url('col1.png'); 66 | background-position:center left; 67 | border-right:1px solid #ddd; 68 | } 69 | 70 | #right-container h4{ 71 | text-indent:8px; 72 | } 73 | 74 | #center-container { 75 | width:600px; 76 | left:200px; 77 | background-color:#1a1a1a; 78 | color:#ccc; 79 | } 80 | 81 | .text { 82 | margin: 7px; 83 | } 84 | 85 | #inner-details { 86 | font-size:0.8em; 87 | list-style:none; 88 | margin:7px; 89 | } 90 | 91 | #log { 92 | position:absolute; 93 | top:10px; 94 | font-size:1.0em; 95 | font-weight:bold; 96 | color:#23A4FF; 97 | } 98 | 99 | 100 | #infovis { 101 | position:relative; 102 | width:600px; 103 | height:600px; 104 | margin:auto; 105 | overflow:hidden; 106 | } 107 | 108 | /*TOOLTIPS*/ 109 | .tip { 110 | color: #111; 111 | width: 139px; 112 | background-color: white; 113 | border:1px solid #ccc; 114 | -moz-box-shadow:#555 2px 2px 8px; 115 | -webkit-box-shadow:#555 2px 2px 8px; 116 | -o-box-shadow:#555 2px 2px 8px; 117 | box-shadow:#555 2px 2px 8px; 118 | opacity:0.9; 119 | filter:alpha(opacity=90); 120 | font-size:10px; 121 | font-family:Verdana, Geneva, Arial, Helvetica, sans-serif; 122 | padding:7px; 123 | } -------------------------------------------------------------------------------- /jit/Examples/css/col1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabwe/words/65b81a28d84339e53f8379a79164a39a3573904d/jit/Examples/css/col1.png -------------------------------------------------------------------------------- /jit/Examples/css/col2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabwe/words/65b81a28d84339e53f8379a79164a39a3573904d/jit/Examples/css/col2.png -------------------------------------------------------------------------------- /jit/Examples/css/gradient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yabwe/words/65b81a28d84339e53f8379a79164a39a3573904d/jit/Examples/css/gradient.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "words", 3 | "version": "0.0.0", 4 | "description": "Words", 5 | "main": "index.html", 6 | "scripts": { 7 | "start": "npm run test && npm run build && node index.js", 8 | "build": "node bin/build.js", 9 | "test": "mocha test/unit/*.js", 10 | "coverage": "istanbul cover node_modules/mocha/bin/_mocha test/unit/*.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/yabwe/words" 15 | }, 16 | "author": "Nate Mielnik", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "browserify": "^13.0.0", 20 | "chai": "^3.5.0", 21 | "connect": "^3.4.0", 22 | "istanbul": "^0.4.2", 23 | "mocha": "^2.4.5", 24 | "serve-static": "^1.10.0", 25 | "sinon": "^1.17.3", 26 | "sinon-chai": "^2.8.0" 27 | }, 28 | "dependencies": { 29 | "diff": "1.4.0" 30 | }, 31 | "bugs": { 32 | "url": "https://github.com/yabwe/words/issues" 33 | }, 34 | "homepage": "http://github.com/yabwe/words" 35 | } 36 | -------------------------------------------------------------------------------- /src/js/block.js: -------------------------------------------------------------------------------- 1 | var Util = require('./util'); 2 | var Word = require('./word'); 3 | var Char = require('./char'); 4 | 5 | /* 6 | * A Block is an object which represents a chunk of text which is separated by other chunks of text by new lines. 7 | * 8 | * Block objects have 2 main properties: 9 | * 10 | * 1. words 11 | * - Array of all the Word objects which are its children. 12 | * 2. parent 13 | * - A reference to its parent Document object. 14 | */ 15 | var Block = function (words, parent, type) { 16 | this.parent = parent; 17 | 18 | if (!words || !words.length) { 19 | this.words = [new Word([new Char('')], this)]; 20 | } else { 21 | this.words = words; 22 | this.words.forEach(function (word) { 23 | word.parent = this; 24 | }, this); 25 | } 26 | 27 | if (type) { 28 | this.type = type; 29 | } 30 | } 31 | 32 | Block.prototype = { 33 | type: 'p', 34 | 35 | /* merge(block) 36 | * 37 | * Merge the provided block into this block 38 | */ 39 | merge: function (block) { 40 | var first = true; 41 | while(block.getWords().length) { 42 | // Remove the word from the previous block and update its parent reference 43 | var nextWord = block.removeWord(block.getFirstWord()); 44 | nextWord.parent = this; 45 | 46 | // If this is the first word we're merging in, we need to attempt to merge the words together. 47 | // If there's a space in there, word.merge() accounts for it 48 | if (first) { 49 | this.getLastWord().merge(nextWord); 50 | first = false; 51 | } else { 52 | this.words.push(nextWord); 53 | } 54 | } 55 | }, 56 | 57 | /* insertAfter(refWord, word) 58 | * 59 | * Insert a single Word object into this list 60 | * of words, after the provided reference Word (refWord) 61 | * 62 | * If refWord doesn't exist or isn't found, the word 63 | * is added to the end 64 | */ 65 | insertAfter: function (refWord, word) { 66 | if (!word) { 67 | return; 68 | } 69 | 70 | word.parent = this; 71 | 72 | var targetIndex = this.words.indexOf(refWord); 73 | if (targetIndex === -1) { 74 | targetIndex = this.words.length; 75 | } else { 76 | targetIndex += 1; 77 | } 78 | 79 | this.words.splice(targetIndex, 0, word); 80 | 81 | word.split(); 82 | }, 83 | 84 | insertWordsAfter: function (words) { 85 | var newBlock = new Block(words); 86 | this.parent.insertAfter(this, newBlock); 87 | return newBlock; 88 | }, 89 | 90 | /* insertBefore(refWord, word) 91 | * 92 | * Insert a single Word object into this list 93 | * of words, before the provided reference Word (refWord) 94 | * 95 | * If refWord doesn't exist or isn't found, the word 96 | * is added to the front 97 | */ 98 | insertBefore: function (refWord, word) { 99 | if (!word) { 100 | return; 101 | } 102 | 103 | word.parent = this; 104 | 105 | var targetIndex = this.words.indexOf(refWord); 106 | if (targetIndex === -1) { 107 | this.words.unshift(word); 108 | } else { 109 | this.words.splice(targetIndex, 0, word); 110 | } 111 | 112 | word.split(); 113 | }, 114 | 115 | /* removeWordsAfter(refWord) 116 | * 117 | * Remove all the words within this block that 118 | * are after the provided reference Word (refWord) 119 | * 120 | * An array of the removed Word objects is returned 121 | * 122 | * The refWord is NOT removed 123 | */ 124 | removeWordsAfter: function (refWord) { 125 | var targetIndex = this.words.indexOf(refWord); 126 | if (targetIndex === -1 || targetIndex === (this.words.length - 1)) { 127 | return []; 128 | } 129 | 130 | var removed = this.words.splice(targetIndex + 1, this.words.length); 131 | removed.forEach(function (word) { 132 | delete word.parent; 133 | }); 134 | 135 | return removed; 136 | }, 137 | 138 | /* removeWord(word) 139 | * 140 | * Remove the specified Word object 141 | * If this block is empty, it will remove 142 | * itself from the Document 143 | */ 144 | removeWord: function (word) { 145 | var index = this.words.indexOf(word); 146 | if (index !== -1) { 147 | this.words.splice(index, 1); 148 | delete word.parent; 149 | } 150 | 151 | // If block is empty, remove it 152 | if (this.words.length === 0) { 153 | this.parent.removeBlock(this); 154 | } 155 | 156 | return word; 157 | }, 158 | 159 | splitAndInsertBlocks: function (refWord, wordArrays) { 160 | // Since we're creating new blocks, we will need to move all 161 | // sibling words of this word into the final created block 162 | var siblingWords = this.removeWordsAfter(refWord), 163 | prevBlock = this; 164 | 165 | wordArrays.forEach(function (words) { 166 | prevBlock = prevBlock.insertWordsAfter(words); 167 | }); 168 | // If we have sibling words, they need to added to the end 169 | // of the final created block 170 | siblingWords.forEach(function (word) { 171 | prevBlock.insertAfter(null, word); 172 | }); 173 | }, 174 | 175 | getWords: function () { 176 | return this.words; 177 | }, 178 | 179 | getFirstWord: function () { 180 | return this.words[0]; 181 | }, 182 | 183 | getLastWord: function () { 184 | return this.words[this.words.length - 1]; 185 | }, 186 | 187 | getChars: function () { 188 | return this.words.reduce(function (prev, curr) { 189 | return prev.concat(curr.getChars()); 190 | }, []); 191 | }, 192 | 193 | toJSON: function (id) { 194 | var json = { 195 | id: 'b' + id, 196 | name: 'B', 197 | type: this.type, 198 | children: [] 199 | }; 200 | this.words.forEach(function (word, index) { 201 | json.children.push(word.toJSON(id + '-' + index)); 202 | }); 203 | return json; 204 | }, 205 | 206 | toString: function () { 207 | return this.words.join(''); 208 | }, 209 | 210 | toHTML: function () { 211 | var content = ''; 212 | this.words.forEach(function (word, index) { 213 | content += word.toHTML(); 214 | }, this); 215 | return '<' + this.type + '>' + content + ''; 216 | } 217 | } 218 | 219 | module.exports = Block; -------------------------------------------------------------------------------- /src/js/char.js: -------------------------------------------------------------------------------- 1 | var Util = require('./util'); 2 | 3 | /* 4 | * A Char is an object which represents a single character of text. Currently, this will be where all formatting information will be stored (ie bold, italic, blockquote, etc.). 5 | * 6 | * Char objects will represent every single character within a **Document**. This includes spaces, newlines, and the empty character terminator. 7 | * 8 | * Char objects have 3 main properties: 9 | * 10 | * 1. char 11 | * - The character it contains 12 | * 2. props 13 | * - Key-Value pair representing a formatting property and whether it is applied (ie bold, italic, blockquote) 14 | * 3. parent 15 | * - A reference to its parent Word object. 16 | */ 17 | var Char = function (char, parent, props) { 18 | this.parent = parent; 19 | 20 | this.char = char || ''; 21 | this.props = props || {}; 22 | } 23 | 24 | Char.prototype = { 25 | 26 | getProps: function () { 27 | var props = []; 28 | Object.keys(this.props).forEach(function (prop) { 29 | if (this.props[prop] === true) { 30 | props.push(prop); 31 | } 32 | }, this); 33 | return props; 34 | }, 35 | 36 | toJSON: function (id) { 37 | var name = this.char; 38 | if (Util.isNewLine(this.char)) { 39 | name = '\\n'; 40 | } else if (Util.isSpace(this.char)) { 41 | name = '[ ]'; 42 | } 43 | return { 44 | id: 'c' + id, 45 | name: name, 46 | children: this.getProps() 47 | }; 48 | }, 49 | 50 | toString: function () { 51 | return this.char; 52 | }, 53 | 54 | toHTML: function () { 55 | return this.char; 56 | } 57 | } 58 | 59 | module.exports = Char; -------------------------------------------------------------------------------- /src/js/document.js: -------------------------------------------------------------------------------- 1 | var Util = require('./util'); 2 | var Block = require('./block'); 3 | var Word = require('./word'); 4 | var Char = require('./char'); 5 | 6 | /* 7 | * A Document is the top level object and the root of the tree. It represents all of the text within the editor. 8 | * 9 | * Document objects have 2 main properties: 10 | * 11 | * 1. blocks 12 | * - Array of all the **Block** objects which are its children. These are loosely tied to block elements in that blocks are always separated by new lines. 13 | * 2. chars 14 | * - Array of all the **Char** objects within the entire editor. These are the leaf-nodes of the data tree. 15 | */ 16 | var Document = function (text) { 17 | this.chars = []; 18 | this.blocks = [new Block(null, this)]; 19 | this.chars = this.blocks.reduce(function (prev, curr) { 20 | return prev.concat(curr.getChars()); 21 | }, []); 22 | this.tail = this.chars[0]; 23 | 24 | if (text) { 25 | this.insertCharsAt(0, text); 26 | } 27 | } 28 | 29 | Document.prototype = { 30 | 31 | /* execAction(action, selection) 32 | * 33 | * Given an action (ie bold, italic) and a selection data object 34 | * apply the action to all the chars/words within the selection 35 | */ 36 | execAction: function (action, selection) { 37 | var nextWord; 38 | // Since this.chars is an array, we can index directly into it to 39 | // find the CHAR nodes within the selection 40 | if (this.chars[selection.start]) { 41 | for (var i = selection.start; i < this.chars.length && i < selection.end; i++) { 42 | var next = this.chars[i]; 43 | // For now, let's only apply actions on words. This means just find the parent 44 | // of each char, and apply the action if we haven't already 45 | if (next.parent !== nextWord) { 46 | nextWord = next.parent; 47 | if (nextWord) { 48 | // Call toggleProp(), which should turn the property on/off for the whole word 49 | nextWord.toggleProp(action); 50 | } 51 | } 52 | } 53 | } 54 | }, 55 | 56 | /* insertCharsAt(index, str) 57 | * 58 | * Given a string and a target index, create new 59 | * Char objects for each character and insert them into 60 | * the tree data structure. 61 | * 62 | * This includes splitting up the tree when spaces and 63 | * newlines are inserted 64 | */ 65 | insertCharsAt: function (index, str) { 66 | if (!str) { 67 | return; 68 | } 69 | 70 | var newChars = [], 71 | nextChar = this.chars[index], 72 | nextWord, 73 | prevChar = this.chars[index - 1], 74 | prevWord; 75 | 76 | str.split('').forEach(function (part) { 77 | newChars.push(new Char(part)); 78 | }, this); 79 | 80 | if (Util.exists(nextChar)) { 81 | nextWord = nextChar.parent; 82 | } 83 | if (Util.exists(prevChar)) { 84 | prevWord = prevChar.parent; 85 | } 86 | 87 | if (nextWord === prevWord || !prevWord) { 88 | nextWord.insertBefore(nextChar, newChars); 89 | } else { 90 | if (Util.isNewLine(prevChar.char) || Util.isSpace(prevChar.char)) { 91 | if (nextWord) { 92 | nextWord.insertBefore(null, newChars); 93 | } else { 94 | var newWord = new Word(newChars); 95 | prevWord.parent.insertAfter(prevWord, newWord); 96 | } 97 | } else { 98 | prevWord.insertAfter(prevChar, newChars); 99 | } 100 | } 101 | 102 | // All of the chars now have their proper parent word objects 103 | // and the tree is updated, so just insert the chars into the array 104 | this.chars.splice.apply(this.chars, [index, 0].concat(newChars)); 105 | }, 106 | 107 | /* removeCharsAt(index, count) 108 | * 109 | * Remove a specified amount of Char objects from the 110 | * tree, starting at the specified index 111 | */ 112 | removeCharsAt: function (index, count) { 113 | if (!this.chars[index]) { 114 | return; 115 | } 116 | 117 | var prevChar = this.chars[index - 1]; 118 | var lastChar = this.chars[index + count]; 119 | 120 | var removedChars = this.chars.splice(index, count); 121 | 122 | removedChars.forEach(function (char) { 123 | char.parent.removeChar(char); 124 | }); 125 | 126 | // If both chars are in the same word, we don't need to merge anything 127 | if (prevChar && prevChar.parent !== lastChar.parent) { 128 | prevChar.parent.merge(lastChar.parent); 129 | } 130 | }, 131 | 132 | /* insertAfter(refBlock, block) 133 | * 134 | * Insert a single Block object into this list 135 | * of blocks, after the provided reference Block (refBlock) 136 | * 137 | * If refBlock doesn't exist or isn't found, the block 138 | * is added to the end 139 | */ 140 | insertAfter: function (refBlock, block) { 141 | if (!block) { 142 | return; 143 | } 144 | 145 | block.parent = this; 146 | 147 | var targetIndex = this.blocks.indexOf(refBlock); 148 | if (targetIndex === -1) { 149 | targetIndex = this.blocks.length; 150 | } else { 151 | targetIndex += 1; 152 | } 153 | 154 | this.blocks.splice(targetIndex, 0, block); 155 | }, 156 | 157 | /* removeBlock(block) 158 | * 159 | * Remove the specified block 160 | */ 161 | removeBlock: function (block) { 162 | var index = this.blocks.indexOf(block); 163 | if (index !== -1) { 164 | this.blocks.splice(index, 1); 165 | block.parent = null; 166 | } 167 | }, 168 | 169 | toJSON: function () { 170 | var json = { 171 | id: 'doc', 172 | name: 'd', 173 | children: [] 174 | } 175 | this.blocks.forEach(function (block, index) { 176 | json.children.push(block.toJSON(index)); 177 | }); 178 | return json; 179 | }, 180 | 181 | toString: function () { 182 | return this.blocks.join(''); 183 | }, 184 | 185 | toHTML: function () { 186 | var str = ''; 187 | this.blocks.forEach(function (block) { 188 | str += block.toHTML(); 189 | }, this); 190 | return str; 191 | } 192 | } 193 | 194 | module.exports = Document; -------------------------------------------------------------------------------- /src/js/tree-ux.js: -------------------------------------------------------------------------------- 1 | var jit = require('../../jit/jit'); 2 | 3 | var labelType, useGradients, nativeTextSupport, animate; 4 | 5 | (function() { 6 | var ua = navigator.userAgent, 7 | iStuff = ua.match(/iPhone/i) || ua.match(/iPad/i), 8 | typeOfCanvas = typeof HTMLCanvasElement, 9 | nativeCanvasSupport = (typeOfCanvas == 'object' || typeOfCanvas == 'function'), 10 | textSupport = nativeCanvasSupport 11 | && (typeof document.createElement('canvas').getContext('2d').fillText == 'function'); 12 | //I'm setting this based on the fact that ExCanvas provides text support for IE 13 | //and that as of today iPhone/iPad current text support is lame 14 | labelType = (!nativeCanvasSupport || (textSupport && !iStuff))? 'Native' : 'HTML'; 15 | nativeTextSupport = labelType == 'Native'; 16 | useGradients = nativeCanvasSupport; 17 | animate = !(iStuff || !nativeCanvasSupport); 18 | })(); 19 | 20 | var Log = { 21 | elem: false, 22 | write: function(text){ 23 | if (!this.elem) 24 | this.elem = document.getElementById('log'); 25 | this.elem.innerHTML = text; 26 | this.elem.style.left = (500 - this.elem.offsetWidth / 2) + 'px'; 27 | } 28 | }; 29 | 30 | var TreeUX = function (elementId) { 31 | var self = this; 32 | this.st = new $jit.ST({ 33 | orientation: 'top', 34 | //id of viz container element 35 | injectInto: elementId, 36 | levelsToShow: 4, 37 | //set duration for the animation 38 | duration: 800, 39 | //set animation transition type 40 | transition: $jit.Trans.Quart.easeInOut, 41 | duration: 1, 42 | //set distance between node and its children 43 | levelDistance: 40, 44 | offsetY: 110, 45 | constrained: false, 46 | //enable panning 47 | Navigation: { 48 | enable:true, 49 | panning:true 50 | }, 51 | //set node and edge styles 52 | //set overridable=true for styling individual 53 | //nodes or edges 54 | Node: { 55 | height: 20, 56 | width: 15, 57 | type: 'rectangle', 58 | color:'#23A4FF', 59 | align:"center", 60 | overridable: true 61 | }, 62 | 63 | Edge: { 64 | type: 'bezier', 65 | overridable: true 66 | }, 67 | 68 | onBeforeCompute: function(node){ 69 | Log.write("loading " + node.name); 70 | }, 71 | 72 | onAfterCompute: function(){ 73 | Log.write("done"); 74 | }, 75 | 76 | //This method is called on DOM label creation. 77 | //Use this method to add event handlers and styles to 78 | //your node. 79 | onCreateLabel: function(label, node){ 80 | label.id = node.id; 81 | label.innerHTML = node.name; 82 | label.onclick = function(){ 83 | self.st.onClick(node.id); 84 | }; 85 | //set label styles 86 | var style = label.style; 87 | style.cursor = 'pointer'; 88 | style.color = '#000'; 89 | style.fontWeight = 'bold' 90 | style.fontSize = '12px'; 91 | style.lineHeight = 20 + 'px'; 92 | style.textAlign= 'center'; 93 | style.paddingLeft = '2px'; 94 | }, 95 | 96 | //This method is called right before plotting 97 | //a node. It's useful for changing an individual node 98 | //style properties before plotting it. 99 | //The data properties prefixed with a dollar 100 | //sign will override the global node style properties. 101 | onBeforePlotNode: function(node){ 102 | //add some color to the nodes in the path between the 103 | //root node and the selected node. 104 | if (node.selected) { 105 | node.data.$color = "#ff7"; 106 | } 107 | else { 108 | delete node.data.$color; 109 | //if the node belongs to the last plotted level 110 | if(!node.anySubnode("exist")) { 111 | //count children number 112 | var count = 0; 113 | node.eachSubnode(function(n) { count++; }); 114 | //assign a node color based on 115 | //how many children it has 116 | node.data.$color = ['#aaa', '#baa', '#caa', '#daa', '#eaa', '#faa'][count]; 117 | } 118 | } 119 | }, 120 | 121 | //This method is called right before plotting 122 | //an edge. It's useful for changing an individual edge 123 | //style properties before plotting it. 124 | //Edge data proprties prefixed with a dollar sign will 125 | //override the Edge global style properties. 126 | onBeforePlotLine: function(adj){ 127 | if (adj.nodeFrom.selected && adj.nodeTo.selected) { 128 | adj.data.$color = "#eed"; 129 | adj.data.$lineWidth = 3; 130 | } 131 | else { 132 | delete adj.data.$color; 133 | delete adj.data.$lineWidth; 134 | } 135 | } 136 | }); 137 | } 138 | 139 | TreeUX.prototype = { 140 | update: function (json) { 141 | json = json || {}; 142 | //load json data 143 | this.st.loadJSON(json); 144 | //compute node positions and layout 145 | this.st.compute(); 146 | //emulate a click on the root node. 147 | setTimeout(function () { 148 | this.st.onClick(this.st.root); 149 | }.bind(this), 5) 150 | } 151 | } 152 | 153 | module.exports = TreeUX; -------------------------------------------------------------------------------- /src/js/util.js: -------------------------------------------------------------------------------- 1 | var Util = { 2 | 3 | blockNames: ['address', 'blockquote', 'div', 'dl', 'fieldset', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'noscript', 'ol', 'p', 'pre', 4 | 5 | 'article', 'aside', 'audio', 'canvas', 'dd', 'figcaption', 'figure', 'footer', 'hgroup', 'main', 'nav', 'output', 'section', 'table', 'tfoot', 'ul', 'video', 6 | 7 | 'dt', 'li', 'tbody', 'td', 'th', 'thead', 'tr'], 8 | 9 | CharCode: { 10 | NEW_LINE: 10, 11 | CARRIAGE_RETURN: 13, 12 | SPACE: 32, 13 | NBSP: 160 14 | }, 15 | 16 | Char: {}, 17 | 18 | exists: function (v) { 19 | return v || v === '' || v === 0 || v === false; 20 | }, 21 | 22 | isNewLine: function (str) { 23 | return !!(str && str.charCodeAt(0) === this.CharCode.NEW_LINE); 24 | }, 25 | 26 | isSpace: function (str) { 27 | return !!(str && str.match(/^\s+$/) && !this.isNewLine(str)); 28 | }, 29 | 30 | createHTMLWordString: function (root) { 31 | var skipRoot = false; 32 | if (this.blockNames.indexOf(root.nodeName.toLowerCase()) !== -1) { 33 | skipRoot = true; 34 | } 35 | var x = null, 36 | treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, x, false); 37 | str = '', 38 | hitNonBlock = false; 39 | while (treeWalker.nextNode()) { 40 | var node = treeWalker.currentNode; 41 | if (skipRoot && node === root) { 42 | continue; 43 | } 44 | if (node.nodeType === 3) { 45 | str += node.nodeValue; 46 | hitNonBlock = true; 47 | } else { 48 | if (this.blockNames.indexOf(node.nodeName.toLowerCase()) !== -1) { 49 | if (hitNonBlock) { 50 | str += this.Char.NEW_LINE; 51 | } 52 | } else { 53 | hitNonBlock = true; 54 | } 55 | } 56 | } 57 | return str.replace(new RegExp(this.Char.NBSP, 'g'), this.Char.SPACE); 58 | }, 59 | 60 | exportSelection: function (root) { 61 | if (!root) { 62 | return null; 63 | } 64 | 65 | var selectionState = null, 66 | selection = document.getSelection(); 67 | 68 | if (selection.rangeCount > 0) { 69 | var range = selection.getRangeAt(0), 70 | preSelectionRange = range.cloneRange(), 71 | start; 72 | 73 | preSelectionRange.selectNodeContents(root); 74 | preSelectionRange.setEnd(range.startContainer, range.startOffset); 75 | start = preSelectionRange.toString().length; 76 | 77 | selectionState = { 78 | start: start, 79 | end: start + range.toString().length 80 | }; 81 | } 82 | 83 | return selectionState; 84 | }, 85 | 86 | importSelection: function (selectionState, root) { 87 | if (!selectionState || !root) { 88 | return; 89 | } 90 | 91 | var range = document.createRange(); 92 | range.setStart(root, 0); 93 | range.collapse(true); 94 | 95 | var node = root, 96 | nodeStack = [], 97 | charIndex = 0, 98 | foundStart = false, 99 | stop = false, 100 | nextCharIndex; 101 | 102 | while (!stop && node) { 103 | if (node.nodeType === 3) { 104 | nextCharIndex = charIndex + node.length; 105 | if (!foundStart && selectionState.start >= charIndex && selectionState.start <= nextCharIndex) { 106 | range.setStart(node, selectionState.start - charIndex); 107 | foundStart = true; 108 | } 109 | if (foundStart && selectionState.end >= charIndex && selectionState.end <= nextCharIndex) { 110 | range.setEnd(node, selectionState.end - charIndex); 111 | stop = true; 112 | } 113 | charIndex = nextCharIndex; 114 | } else { 115 | var i = node.childNodes.length - 1; 116 | while (i >= 0) { 117 | nodeStack.push(node.childNodes[i]); 118 | i -= 1; 119 | } 120 | } 121 | if (!stop) { 122 | node = nodeStack.pop(); 123 | } 124 | } 125 | 126 | var sel = document.getSelection(); 127 | sel.removeAllRanges(); 128 | sel.addRange(range); 129 | } 130 | } 131 | 132 | Object.keys(Util.CharCode).forEach(function (name) { 133 | Util.Char[name] = String.fromCharCode(Util.CharCode[name]); 134 | }); 135 | 136 | module.exports = Util; -------------------------------------------------------------------------------- /src/js/word.js: -------------------------------------------------------------------------------- 1 | var Util = require('./util'); 2 | var Char = require('./char'); 3 | 4 | /* 5 | * A Word is an object which represents a chunk of text which is separated by other chunks of text by spaces (within the same **Block**). All words will contain their ending character, which will either be: 6 | * - A Space (' ') 7 | * - A Newline ('\n') 8 | * - An Empty String ('') 9 | * - The last word in the **Document** will have this empty string as a terminator 10 | * 11 | * Word objects have 2 main properties: 12 | * 13 | * 1. chars 14 | * - Array of all the Char objects which are its children. 15 | * 2. parent 16 | * - A reference to its parent Block object. 17 | */ 18 | var Word = function (text, parent) { 19 | this.parent = parent; 20 | 21 | if (!text && text !== '') { 22 | this.chars = []; 23 | } else if (typeof text === 'string') { 24 | this.chars = []; 25 | var chars = (text) ? Array.prototype.slice.call(text) : ['']; 26 | chars.forEach(function (char) { 27 | this.chars.push(new Char(char, this)); 28 | }, this); 29 | } else { 30 | this.chars = text; 31 | this.chars.forEach(function (char) { 32 | char.parent = this; 33 | }, this); 34 | } 35 | } 36 | 37 | Word.prototype = { 38 | 39 | /* toggleProp(prop) 40 | * 41 | * Given some property name (ie bold, italic), toggle its value for 42 | * all of the chars within this word 43 | * 44 | */ 45 | toggleProp: function (prop) { 46 | var setTrue = false; 47 | /* Loop through each char 48 | * If a char has a false value for the property, set it to true 49 | * 50 | * ie: If half a word is bold, make the whole word bold 51 | */ 52 | this.chars.forEach(function (char) { 53 | if (!char.props[prop]) { 54 | char.props[prop] = true; 55 | setTrue = true; 56 | } 57 | }); 58 | 59 | /* If the entire word already had the property set to true 60 | * loop back through and turn the property off. 61 | * 62 | * ie: If an entire word is already bold, make it all unbold 63 | */ 64 | if (!setTrue) { 65 | this.chars.forEach(function (char) { 66 | char.props[prop] = false; 67 | }); 68 | } 69 | }, 70 | 71 | /* merge(word) 72 | * 73 | * merge the provided word into this word 74 | */ 75 | merge: function (word) { 76 | // If the words are in separate blocks, we need to merge the blocks 77 | if (this.parent !== word.parent) { 78 | // Calling block.merge() will handle merging the words too 79 | this.parent.merge(word.parent); 80 | return; 81 | } 82 | 83 | // Remove each char from the other word, and add it to this one 84 | while (word.getChars().length) { 85 | var nextChar = word.removeChar(word.getFirstChar()); 86 | nextChar.parent = this; 87 | this.chars.push(nextChar); 88 | } 89 | 90 | // We might have ended with a space, or the other word may have started 91 | // with a space, so do a split to make sure 92 | this.split(); 93 | }, 94 | 95 | /* split() 96 | * 97 | * Look for spaces and newlines within this word 98 | * Each space means create a new words 99 | * Each newline means create a new block 100 | */ 101 | split: function () { 102 | var currentChars = [], 103 | thisBlockWords = [], 104 | thisWordChars, 105 | newBlocks = [], 106 | nextBlock; 107 | 108 | this.chars.forEach(function (char, index, array) { 109 | currentChars.push(char); 110 | if (index === array.length - 1) { 111 | return; 112 | } 113 | // We hit something which will end a word and could end a block 114 | if (Util.isNewLine(char.char) || Util.isSpace(char.char)) { 115 | // If we aren't adding to a newWord, that means we're still in the original 116 | if (!thisWordChars) { 117 | thisWordChars = currentChars; 118 | } else { 119 | // Add the newChars to a new word 120 | if (!nextBlock) { 121 | thisBlockWords.push(new Word(currentChars)); 122 | } else { 123 | nextBlock.push(new Word(currentChars)); 124 | } 125 | } 126 | currentChars = []; 127 | 128 | // If we hit a newline, we need to start a new block 129 | if (Util.isNewLine(char.char)) { 130 | if (nextBlock) { 131 | newBlocks.push(nextBlock); 132 | } 133 | nextBlock = []; 134 | } 135 | } 136 | }, this); 137 | 138 | // If we never hit a space, do nothing 139 | if (!thisWordChars) { 140 | return; 141 | } 142 | 143 | // Update our list of chars 144 | this.chars = thisWordChars; 145 | 146 | // If we have any lingering chars, make sure they get added into a word or block 147 | if (currentChars.length) { 148 | if (!nextBlock) { 149 | thisBlockWords.push(new Word(currentChars)); 150 | } else { 151 | nextBlock.push(new Word(currentChars)); 152 | } 153 | } 154 | 155 | // If we have any lingering blocks, make sure they get added into the blocks list 156 | if (nextBlock) { 157 | newBlocks.push(nextBlock); 158 | } 159 | 160 | // First, let's add any new words to this block 161 | var lastWord = this; 162 | if (thisBlockWords.length) { 163 | thisBlockWords.forEach(function (word) { 164 | lastWord.parent.insertAfter(lastWord, word); 165 | lastWord = word; 166 | }); 167 | } 168 | 169 | // Now, let's start creating blocks 170 | if (newBlocks.length) { 171 | this.parent.splitAndInsertBlocks(lastWord, newBlocks); 172 | } 173 | }, 174 | 175 | /* insertAfter(refChar, char) 176 | * 177 | * Insert an array of Char objects into this word 178 | * all after the provided reference char (refChar) 179 | * 180 | * If refChar doesn't exist or isn't found, all the chars 181 | * are added to the end 182 | */ 183 | insertAfter: function (refChar, chars) { 184 | if (!chars) { 185 | return; 186 | } 187 | 188 | chars.forEach(function (char) { 189 | char.parent = this; 190 | }, this); 191 | 192 | var targetIndex = this.chars.indexOf(refChar); 193 | if (targetIndex === -1) { 194 | this.chars = this.chars.concat(chars); 195 | } else { 196 | targetIndex = targetIndex + 1; 197 | this.chars.splice.apply(this.chars, [targetIndex, 0].concat(chars)); 198 | } 199 | 200 | this.split(); 201 | }, 202 | 203 | /* insertBefore(refChar, char) 204 | * 205 | * Insert an array of Char objects into this word 206 | * all before the provided reference char (refChar) 207 | * 208 | * If refChar doesn't exist or isn't found, all the chars 209 | * are inserted at the beginning of the word 210 | */ 211 | insertBefore: function (refChar, chars) { 212 | if (!chars) { 213 | return; 214 | } 215 | 216 | chars.forEach(function (char) { 217 | char.parent = this; 218 | }, this); 219 | 220 | var targetIndex = this.chars.indexOf(refChar); 221 | if (targetIndex === -1) { 222 | this.chars = chars.concat(this.chars); 223 | } else { 224 | this.chars.splice.apply(this.chars, [targetIndex, 0].concat(chars)); 225 | } 226 | 227 | this.split(); 228 | }, 229 | 230 | /* removeChar(char) 231 | * 232 | * Remove the provided Char object from the list 233 | * of chars in this word. 234 | * If the word becomes empty, it will remove 235 | * itself from the block it's in 236 | */ 237 | removeChar: function (char) { 238 | var index = this.chars.indexOf(char); 239 | if (index !== -1) { 240 | this.chars.splice(index, 1); 241 | delete char.parent; 242 | } 243 | 244 | // If word is empty, remove it 245 | if (this.chars.length === 0) { 246 | this.parent.removeWord(this); 247 | } 248 | 249 | return char; 250 | }, 251 | 252 | getChars: function () { 253 | return this.chars; 254 | }, 255 | 256 | getFirstChar: function () { 257 | return this.chars[0]; 258 | }, 259 | 260 | getLastChar: function () { 261 | return this.chars[this.chars.length - 1]; 262 | }, 263 | 264 | toJSON: function (id) { 265 | var json = { 266 | id: 'w' + id, 267 | name: 'w', 268 | children: [] 269 | }; 270 | this.chars.forEach(function (char, index) { 271 | json.children.push(char.toJSON(id + '-' + index)); 272 | }); 273 | return json; 274 | }, 275 | 276 | toString: function () { 277 | return this.chars.join(''); 278 | }, 279 | 280 | toHTML: function () { 281 | var tags = {}, 282 | pre = '', 283 | post = '', 284 | content = ''; 285 | this.chars.forEach(function (next) { 286 | if (!Util.isNewLine(next.char)) { 287 | next.getProps().forEach(function (prop) { 288 | tags[prop] = true; 289 | }); 290 | content += next.toHTML(); 291 | } 292 | }, this); 293 | Object.keys(tags).forEach(function (tag) { 294 | pre = pre + '<' + tag + '>'; 295 | post = '' + post; 296 | }); 297 | return pre + content + post; 298 | } 299 | } 300 | 301 | module.exports = Word; -------------------------------------------------------------------------------- /src/js/words.js: -------------------------------------------------------------------------------- 1 | var Util = require('./util'); 2 | var Document = require('./document'); 3 | var JsDiff = require('diff'); 4 | var TreeUX; 5 | 6 | if (typeof window !== 'undefined') { 7 | TreeUX = require('./tree-ux'); 8 | } 9 | 10 | var Words = function (selector) { 11 | this.element = document.querySelector(selector); 12 | this.element.setAttribute('contenteditable', true); 13 | 14 | this.element.addEventListener('input', this.onInput.bind(this)); 15 | 16 | this.doc = new Document(Util.createHTMLWordString(this.element)); 17 | 18 | // Attach to click for the toolbar 19 | Array.prototype.slice.call(document.querySelectorAll('button')).forEach(function (button) { 20 | button.addEventListener('click', this.onToolbarButtonClick.bind(this)); 21 | }, this); 22 | 23 | if (TreeUX) { 24 | // Just for help while developing, create a visualization 25 | // that shows the JSON tree-structure while text is being edited 26 | this.treeUx = new TreeUX('tree-ux'); 27 | } 28 | 29 | // Build tree from initial content 30 | this.updateState(this.element); 31 | } 32 | 33 | Words.prototype = { 34 | 35 | onToolbarButtonClick: function (event) { 36 | var target = event.currentTarget; 37 | if (target.hasAttribute('data-custom-action')) { 38 | this.execCustomAction(target.getAttribute('data-custom-action')); 39 | } 40 | }, 41 | 42 | execCustomAction: function (action) { 43 | // Export selection, getting start character and end character 44 | var selection = Util.exportSelection(this.element); 45 | 46 | // Execute action, providing given selection 47 | this.doc.execAction(action, selection); 48 | 49 | // Replace the current content with the generated HTML 50 | this.element.innerHTML = this.doc.toHTML(); 51 | 52 | // Restore the selection before the action occurred 53 | Util.importSelection(selection, this.element); 54 | }, 55 | 56 | updateState: function (element) { 57 | var nextStr = Util.createHTMLWordString(element), // text representation of current DOM 58 | currStr = this.doc.toString(), // text representation of the current tree 59 | diff = JsDiff.diffChars(currStr, nextStr), // JsDiff of two strings 60 | index = 0; 61 | 62 | /* Iterate through array of diffs detected by JSDiff 63 | * 64 | * Each 'diff' is an object that represents a part of the string: 65 | * 66 | * { 67 | * added: true/undefined (True if this represents a chunk of text that was added) 68 | * removed: true/undefined (True if this represents a chunk of text that was removed) 69 | * value: string (The chunk of text this diff covers) 70 | * count: int (The length of this chunk of text) 71 | * } 72 | * 73 | * NOTE: If added and removed are both undefined this chunk of text text was unchanged 74 | */ 75 | diff.forEach(function (action) { 76 | if (action.removed) { 77 | this.doc.removeCharsAt(index, action.count); 78 | } else { 79 | if (action.added) { 80 | this.doc.insertCharsAt(index, action.value); 81 | } 82 | index += action.value.length; 83 | } 84 | }, this); 85 | 86 | // For development only, update the tree visualization 87 | var js = this.doc.toJSON(); 88 | if (this.treeUx) { 89 | this.treeUx.update(js); 90 | } 91 | }, 92 | 93 | onInput: function (event) { 94 | this.updateState(event.currentTarget); 95 | } 96 | } 97 | 98 | Words.Util = Util; 99 | Words.Char = require('./char'); 100 | Words.Word = require('./word'); 101 | Words.Block = require('./block'); 102 | Words.Document = Document; 103 | 104 | global.Words = module.exports = Words; -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | *:focus { 2 | outline: none; 3 | } 4 | 5 | body { 6 | font-family: Helvetica, Arial, sans-serif; 7 | font-size: 22px; 8 | line-height: 30px; 9 | } 10 | 11 | .top-bar { 12 | position: fixed; 13 | top: 0; 14 | left: 0; 15 | width: auto; 16 | z-index: 10; 17 | padding: 10px; 18 | background-color: #000; 19 | background-color: rgba(0, 0, 0, .8); 20 | box-shadow: 0 0 4px #000; 21 | box-sizing: border-box; 22 | color: #ccc; 23 | font-size: 12px; 24 | font-weight: bold; 25 | text-align: center; 26 | text-transform: uppercase; 27 | } 28 | 29 | h1 { 30 | font-size: 60px; 31 | font-weight: bold; 32 | text-align: center; 33 | margin-bottom: 40px; 34 | padding-bottom: 40px; 35 | letter-spacing: -2px; 36 | border-bottom: 1px solid #dbdbdb; 37 | } 38 | 39 | h2 { 40 | font-size: 32px; 41 | line-height: 42px; 42 | } 43 | 44 | h3 { 45 | font-size: 26px; 46 | line-height: 32px; 47 | } 48 | 49 | h4 { 50 | font-size: 24px; 51 | line-height: 28px; 52 | } 53 | 54 | p { 55 | margin-bottom: 40px; 56 | } 57 | 58 | a { 59 | color:black; 60 | } 61 | 62 | a:hover { 63 | color:green; 64 | } 65 | 66 | pre { 67 | font-family: 'Menlo', monospace; 68 | font-size: 15px; 69 | background-color: #f0f0f0; 70 | padding: 15px; 71 | border: 1px solid #ccc; 72 | border-radius: 5px; 73 | color: #666; 74 | } 75 | 76 | 77 | blockquote { 78 | display: block; 79 | padding-left: 20px; 80 | border-left: 6px solid #df0d32; 81 | margin-left: -15px; 82 | padding-left: 15px; 83 | font-style: italic; 84 | color: #555; 85 | } 86 | 87 | #container { 88 | width: 960px; 89 | margin: 30px auto; 90 | } 91 | 92 | .editable, 93 | .secondEditable 94 | { 95 | outline: none; 96 | margin: 0 0 20px 0; 97 | padding: 0 0 20px 0; 98 | border-bottom: 1px solid #dbdbdb; 99 | } 100 | 101 | #columns { 102 | width: 90%; 103 | margin: 30px auto; 104 | } 105 | 106 | .column-container { 107 | 108 | } 109 | 110 | .column { 111 | vertical-align: top; 112 | display: inline-block; 113 | width: 30%; 114 | margin: 10px 1%; 115 | } 116 | 117 | 118 | 119 | 120 | 121 | #log { 122 | position:relative; 123 | top:10px; 124 | font-size:1.0em; 125 | font-weight:bold; 126 | color:#23A4FF; 127 | } 128 | 129 | 130 | #tree-ux { 131 | position:relative; 132 | width:100%; 133 | height:400px; 134 | margin:auto; 135 | overflow:hidden; 136 | } -------------------------------------------------------------------------------- /test/unit/char.spec.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | 3 | var Char = require('../../src/js/char'); 4 | 5 | describe('Char', function () { 6 | describe('constructor', function () { 7 | it('should exist', function () { 8 | expect(Char).not.to.be.undefined; 9 | }); 10 | 11 | it('should initialize core properties', function () { 12 | var char = new Char(); 13 | 14 | expect(char.parent).to.be.undefined; 15 | expect(char.char).to.be.a('string'); 16 | expect(char.char).to.be.empty; 17 | expect(char.props).to.be.a('object'); 18 | expect(char.props).to.be.empty; 19 | }); 20 | 21 | it('should accept a character and a parent reference', function () { 22 | var tempParent = {}, 23 | tempChar = 'X', 24 | char = new Char('X', tempParent); 25 | 26 | expect(char.char).to.equal(tempChar); 27 | expect(char.parent).to.equal(tempParent); 28 | }); 29 | 30 | it('should accept props as an object', function () { 31 | var props = { 32 | 'b': true, 33 | 'i': false 34 | }, 35 | char = new Char('X', null, props); 36 | 37 | expect(char.props).to.equal(props); 38 | expect(char.getProps()).to.deep.equal(['b']); 39 | }); 40 | }); 41 | 42 | describe('getProps', function () { 43 | it('should return properties as an array', function () { 44 | var char = new Char('a'); 45 | 46 | char.props['PROP'] = true; 47 | char.props['B'] = true; 48 | 49 | var props = char.getProps(); 50 | expect(props).to.be.an.instanceof(Array); 51 | expect(props.length).to.equal(2); 52 | expect(props[0]).to.equal('PROP'); 53 | expect(props[1]).to.equal('B'); 54 | }); 55 | 56 | it('should return properties that have a value of true', function () { 57 | var char = new Char('a'); 58 | 59 | char.props['B'] = 'B'; 60 | char.props['I'] = true; 61 | char.props['U'] = null; 62 | char.props['P'] = false; 63 | 64 | var props = char.getProps(); 65 | expect(props.length).to.equal(1); 66 | expect(props[0]).to.equal('I'); 67 | }); 68 | 69 | it('should return an empty array when nothing is true', function () { 70 | var char = new Char('a'); 71 | 72 | expect(char.getProps()).to.be.empty; 73 | 74 | char['B'] = 'B'; 75 | expect(char.getProps()).to.be.empty; 76 | }); 77 | }); 78 | 79 | describe('toJSON', function () { 80 | it('should return a JSON representaton of the char', function () { 81 | var char = new Char('a'), 82 | json = char.toJSON(1), 83 | output = { 84 | name: 'a', 85 | id: 'c1', 86 | children: [] 87 | }; 88 | 89 | expect(json).to.deep.equal(output); 90 | }); 91 | 92 | it('should return JSON containing props as children', function () { 93 | var char = new Char('b'); 94 | 95 | char.props['B'] = true; 96 | char.props['I'] = false; 97 | 98 | var output = { 99 | name: 'b', 100 | id: 'c2', 101 | children: ['B'] 102 | }; 103 | 104 | expect(char.toJSON(2)).to.deep.equal(output); 105 | }); 106 | 107 | it('should return [ ] for spaces', function () { 108 | var char = new Char(' '); 109 | 110 | expect(char.toJSON('c')).to.deep.equal({ 111 | name: '[ ]', 112 | id: 'cc', 113 | children: [] 114 | }); 115 | }); 116 | 117 | it('should return \\n for new lines', function () { 118 | var char = new Char('\n'); 119 | 120 | expect(char.toJSON('x')).to.deep.equal({ 121 | name: '\\n', 122 | id: 'cx', 123 | children: [] 124 | }); 125 | }); 126 | }); 127 | 128 | describe('toString and toHTML', function () { 129 | it('should return the character', function () { 130 | var char = new Char('a'), 131 | str = char.toString(), 132 | html = char.toHTML(); 133 | 134 | expect(str).to.be.a('string'); 135 | expect(str).to.equal('a'); 136 | expect(html).to.be.a('string'); 137 | expect(html).to.equal('a'); 138 | }); 139 | 140 | it('should return empty string by default', function () { 141 | var char = new Char(), 142 | str = char.toString(), 143 | html = char.toHTML(); 144 | 145 | expect(str).to.be.a('string'); 146 | expect(str).to.be.empty; 147 | expect(html).to.be.a('string'); 148 | expect(html).to.be.empty; 149 | }); 150 | }); 151 | }); -------------------------------------------------------------------------------- /test/unit/util.spec.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | 3 | var Util = require('../../src/js/util'); 4 | 5 | describe('Util', function () { 6 | describe('exists', function () { 7 | it('should return true for empty string false and 0', function () { 8 | expect(Util.exists('')).to.be.true; 9 | expect(Util.exists(false)).to.be.true; 10 | expect(Util.exists(0)).to.be.true; 11 | }); 12 | 13 | it('should return false for null and undefined', function () { 14 | expect(Util.exists(undefined)).to.be.false; 15 | expect(Util.exists(null)).to.be.false; 16 | }); 17 | 18 | it('should return false for a deleted property', function () { 19 | var obj = { prop: false }; 20 | expect(Util.exists(obj.prop)).to.be.true; 21 | 22 | delete obj.prop; 23 | expect(Util.exists(obj.prop)).to.be.false; 24 | }); 25 | }); 26 | 27 | describe('isNewLine', function () { 28 | it('should return true for \\n', function () { 29 | expect(Util.isNewLine('\n')).to.be.true; 30 | expect(Util.isNewLine('\r')).to.be.false; 31 | expect(Util.isNewLine(' ')).to.be.false; 32 | }); 33 | }); 34 | 35 | describe('isSpace', function () { 36 | it('should return true for spaces', function () { 37 | var whiteSpaces = [' ', '\t', '\v', '\f', '\u00A0', '\u2000', '\u2001', '\u2002', '\u2003','\u2028', '\u2029']; 38 | 39 | whiteSpaces.forEach(function (str) { 40 | expect(Util.isSpace(str)).to.be.true; 41 | }); 42 | }); 43 | 44 | it('should return false for new line chars and empty strings', function () { 45 | expect(Util.isSpace('\n')).to.be.false; 46 | expect(Util.isSpace('')).to.be.false; 47 | }); 48 | }); 49 | }); --------------------------------------------------------------------------------