From a1be36909b88c5422b9abaf2c56d2ec06e91defd Mon Sep 17 00:00:00 2001 From: gregorio Date: Tue, 25 Feb 2014 21:57:36 -0300 Subject: [PATCH 01/33] Added refresh button (an uggly one, have to replace with a glyphicon or something), added url onclick at the bottom, doesn't work yet. --- docs.md | 5 +++- src/pty.js | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/docs.md b/docs.md index bf84656..68d8c13 100644 --- a/docs.md +++ b/docs.md @@ -268,7 +268,10 @@ d3.select('div#chart') .width(width) .height(height) .nodeRadius(15) - .nodeBaseURL(function(d) { return '{{site.baseurl}}/data/' + d.id + '.json'; }); + .chartURL(function(d) { return 'www.awesomechart.com/' + d.id + '.html'; }) + .nodeBaseURL(function(d) { return '{{site.baseurl}}/data/' + d.id + '.json'; }) + .initialData('{{ site.baseurl }}/data/A.json'); + // d3.select('div#example06').data([data]).call(chart01); }); diff --git a/src/pty.js b/src/pty.js index d21851f..45ad336 100644 --- a/src/pty.js +++ b/src/pty.js @@ -24,8 +24,10 @@ pty.chart.network = function() { // onClick: function(d, i) {}, nodeClass: function(d, i) { return ''; }, nodeBaseURL: function(d) { return ''; }, + chartURL: function(d) { return ''; }, nodeLabel: function(d, i) { return ''; }, - fixCenter: true + fixCenter: true, + initialData: {} //Implement 'DO NOTHING' if left undefined }; @@ -103,6 +105,12 @@ pty.chart.network = function() { d.isclick = false; } + function printURL(d, i) { + var myURL = me.chartURL(d); + console.log(myURL); + return myURL; + } + // Initialization // -------------- @@ -117,7 +125,10 @@ pty.chart.network = function() { glinks = g.select('g.links'), gnodes = g.select('g.nodes'), glabels = g.select('g.labels'), - gbrand = g.select('g.brand'); + gbrand = g.select('g.brand'), + // gnodeURL = g.select('g.nodeURL'), + grefresh = g.select('g.button'); + // Force layout // ------------ @@ -176,10 +187,17 @@ pty.chart.network = function() { .on('click', function(d, i) { if (d3.select(this).classed('node-clickable')) { onClick(d, i); + gnodeurl.selectAll('text').remove(); + gnodeurl.append('text').attr('text-anchor','start').text(printURL(d)); } + else { + gnodeurl.selectAll('text').remove(); + gnodeurl.append('text').attr('text-anchor','start').text(printURL(d)); //URL does not update properly. + } }); + circles.call(force.drag); circles.exit().remove(); @@ -216,6 +234,32 @@ pty.chart.network = function() { }); + //Other elements + //-------------- + var gbutton = g.append('g') + .attr('class','button') + .attr('transform','translate(' + [10,10] +')'); + + //Refresh button + var refreshButton = gbutton.append('rect') + .attr('width',20) + .attr('height',20) + .attr('fill','black') + .on('click', function() { + + d3.json(me.initialData, function(error,data) { + div.data([data]).call(chart); + }) + + } ); + + //Container for the url + var gnodeurl = svgEnter.append('g') + .attr('class','urlcontainer') + .attr('transform','translate(' + [4, me.height -8 ] +')'); + + gnodeurl.append('text').attr('text-anchor','start').text("Hi five!"); + }); } @@ -223,6 +267,8 @@ pty.chart.network = function() { chart.init = function(selection) { selection.each(function(data) { + var initial = selection; + var svgEnter = d3.select(this), gcont = svgEnter.append('g').attr('class', 'network-chart'); @@ -248,6 +294,27 @@ pty.chart.network = function() { .attr('class', 'masega-brand') .attr('text-anchor', 'end') .text('masega.co'); + + // var gnodeurl = gcont.append('g') + // .attr('class','urlcontainer') + // .attr('transform','translate(' + [4, me.height -8 ] +')'); + + // var gprintnodeurl = gnodeurl.append('a') + // .attr('xlink:href', me.chartURL(this)) + // .append('text') + // .attr('text-anchor','start') + // .text("hola"); + + // var gbutton = gcont.append('g') + // .attr('class','button') + // .attr('transform','translate(' + [10,10] +')'); + + // var refreshButton = gbutton.append('rect') + // .attr('width',20) + // .attr('height',20) + // .attr('fill','black') + // .on('click', function() { gcont.data([initialData]).call(chart); } ); + }); }; From 9e51980c6fe24f1d137b7cb4493dc3ca35b74d8c Mon Sep 17 00:00:00 2001 From: gregorio Date: Wed, 26 Feb 2014 21:54:16 -0300 Subject: [PATCH 02/33] Embellished the refresh button --- docs.md | 1 + src/pty.js | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/docs.md b/docs.md index 68d8c13..c0db658 100644 --- a/docs.md +++ b/docs.md @@ -6,6 +6,7 @@ title: Documentation + # Network Chart diff --git a/src/pty.js b/src/pty.js index 45ad336..40b0677 100644 --- a/src/pty.js +++ b/src/pty.js @@ -241,10 +241,14 @@ pty.chart.network = function() { .attr('transform','translate(' + [10,10] +')'); //Refresh button - var refreshButton = gbutton.append('rect') - .attr('width',20) - .attr('height',20) - .attr('fill','black') + var refreshButton = gbutton.append('circle') + .attr('cx',10) + .attr('cy',10) + .attr('r',10) + .attr('fill','white') + .attr('stroke','black') + .attr('stroke-width',2) + .attr('cursor','pointer') .on('click', function() { d3.json(me.initialData, function(error,data) { @@ -253,6 +257,20 @@ pty.chart.network = function() { } ); + gbutton.append('text') + .attr('x',4) + .attr('y',15) + .attr('cursor','pointer') + .attr('font-family', 'FontAwesome') + .text('\uf0e2' ) + .on('click', function() { + + d3.json(me.initialData, function(error,data) { + div.data([data]).call(chart); + }) + + } ); + //Container for the url var gnodeurl = svgEnter.append('g') .attr('class','urlcontainer') From 23a269ab15774e72521447dfdbfb5dd9c36459d1 Mon Sep 17 00:00:00 2001 From: gregorio Date: Wed, 26 Feb 2014 22:27:27 -0300 Subject: [PATCH 03/33] CHange the way the refresh button works. Now appeals to the json of the root. --- src/pty.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pty.js b/src/pty.js index 40b0677..d4380f2 100644 --- a/src/pty.js +++ b/src/pty.js @@ -251,7 +251,7 @@ pty.chart.network = function() { .attr('cursor','pointer') .on('click', function() { - d3.json(me.initialData, function(error,data) { + d3.json(nodeBaseURL(me.root), function(error,data) { div.data([data]).call(chart); }) From 9befe5f75d069c45ebc0ed1c632c2ff18a0f867c Mon Sep 17 00:00:00 2001 From: gregorio Date: Wed, 26 Feb 2014 22:43:47 -0300 Subject: [PATCH 04/33] Back to previous method for the button, gotta think a bit --- docs.md | 4 ++-- embed.md | 1 + index.md | 1 + src/pty.js | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs.md b/docs.md index c0db658..f6bd33a 100644 --- a/docs.md +++ b/docs.md @@ -26,7 +26,7 @@ title: Documentation .height(height) .nodeRadius(10); - d3.json('{{ site.baseurl }}/data/D.json', function(error, data) { + d3.json('{{ site.baseurl }}/data/A.json', function(error, data) { if (error) { return error; } @@ -86,7 +86,7 @@ The following script initiates a force chart using the data contained in the fil
+
diff --git a/index.md b/index.md index 1b91f09..1165a2d 100644 --- a/index.md +++ b/index.md @@ -6,6 +6,7 @@ title: Panama +
diff --git a/src/pty.js b/src/pty.js index d4380f2..40b0677 100644 --- a/src/pty.js +++ b/src/pty.js @@ -251,7 +251,7 @@ pty.chart.network = function() { .attr('cursor','pointer') .on('click', function() { - d3.json(nodeBaseURL(me.root), function(error,data) { + d3.json(me.initialData, function(error,data) { div.data([data]).call(chart); }) From c8471247ba6ae8cd4392a5a8de3e2a54d7634c74 Mon Sep 17 00:00:00 2001 From: gregorio Date: Wed, 26 Feb 2014 22:46:12 -0300 Subject: [PATCH 05/33] Added a remark about refresh button --- src/pty.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pty.js b/src/pty.js index 40b0677..b799424 100644 --- a/src/pty.js +++ b/src/pty.js @@ -241,6 +241,8 @@ pty.chart.network = function() { .attr('transform','translate(' + [10,10] +')'); //Refresh button + //NOTE: we should be able to appeal to the url of the root, but data.root has no id. + //Write a function that returns the node corresponding to the root. Then apply nodeBaseURL to it. var refreshButton = gbutton.append('circle') .attr('cx',10) .attr('cy',10) From 5b16e4891f19a2f97af6856b725666f41adf4e70 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Thu, 27 Feb 2014 11:08:29 -0300 Subject: [PATCH 06/33] Remove the style for the center links --- css/index.css | 2 +- css/pty.css | 2 +- less/index.less | 8 -------- less/pty.less | 8 -------- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/css/index.css b/css/index.css index d0bf3af..75ac659 100644 --- a/css/index.css +++ b/css/index.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2;stroke-dasharray:5,5}.network-chart .center-link{stroke-opacity:.2;stroke-width:4;stroke-dasharray:none}.network-chart rect.background{fill:#556270}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px} \ No newline at end of file diff --git a/css/pty.css b/css/pty.css index 5071a68..f71a089 100644 --- a/css/pty.css +++ b/css/pty.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2;stroke-dasharray:5,5}.network-chart .center-link{stroke-opacity:.2;stroke-width:4;stroke-dasharray:none}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px} \ No newline at end of file diff --git a/less/index.less b/less/index.less index e8f55ae..a74e46f 100644 --- a/less/index.less +++ b/less/index.less @@ -45,16 +45,8 @@ stroke: @aqua-light; stroke-opacity: .5; stroke-width: 2; - stroke-dasharray: 5,5 } - .center-link { - stroke-opacity: .2; - stroke-width: 4; - stroke-dasharray: none; - } - - // Background rect.background { fill: @slate-dark; diff --git a/less/pty.less b/less/pty.less index 5c84a7c..fd38ca2 100644 --- a/less/pty.less +++ b/less/pty.less @@ -50,16 +50,8 @@ stroke: @aqua-light; stroke-opacity: .5; stroke-width: 2; - stroke-dasharray: 5,5 } - .center-link { - stroke-opacity: .2; - stroke-width: 4; - stroke-dasharray: none; - } - - // Background rect.background { fill: @grey-light; From 3350325a9f73fdc00b4317336b4db874bfcf7a91 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Thu, 27 Feb 2014 11:38:31 -0300 Subject: [PATCH 07/33] Update the node link --- data/G.json | 2 +- index.md | 3 +- pages/A.md | 8 +++++ pages/B.md | 11 +++++++ pages/C.md | 8 +++++ pages/D.md | 8 +++++ pages/E.md | 8 +++++ pages/F.md | 8 +++++ pages/G.md | 8 +++++ pages/H.md | 8 +++++ src/pty.js | 87 +++++++++++++++++++++++++++-------------------------- 11 files changed, 114 insertions(+), 45 deletions(-) create mode 100644 pages/A.md create mode 100644 pages/B.md create mode 100644 pages/C.md create mode 100644 pages/D.md create mode 100644 pages/E.md create mode 100644 pages/F.md create mode 100644 pages/G.md create mode 100644 pages/H.md diff --git a/data/G.json b/data/G.json index dd4390b..c4eb700 100644 --- a/data/G.json +++ b/data/G.json @@ -13,7 +13,7 @@ "name": "E Navarro", "value": 20, "type": "candidato", - "numcn": 4 + "numcn": 3 }, { "id": "D", diff --git a/index.md b/index.md index 1165a2d..3047284 100644 --- a/index.md +++ b/index.md @@ -28,7 +28,8 @@ d3.json('{{ site.baseurl }}/data/A.json', function(error, data) { .height(height) .nodeRadius(15) .nodeLabel(function(d) { return d.name; }) - .nodeBaseURL(function(d) { return '{{site.baseurl}}/data/' + d.id + '.json'; }); + .nodeBaseURL(function(d) { return '{{site.baseurl}}/data/' + d.id + '.json'; }) + .nodeURL(function(d) { return '{{site.baseurl}}/pages/' + d.id; }); d3.select('div#demo').data([data]).call(chart01); }); diff --git a/pages/A.md b/pages/A.md new file mode 100644 index 0000000..f63b3f4 --- /dev/null +++ b/pages/A.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item A +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/B.md b/pages/B.md new file mode 100644 index 0000000..893a951 --- /dev/null +++ b/pages/B.md @@ -0,0 +1,11 @@ +--- +layout: main +title: Item B +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. + + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nesciunt, aperiam, praesentium, eum, debitis modi vero magni neque sed laboriosam labore blanditiis maxime dolore et at distinctio. Velit, nobis, laboriosam, eveniet nulla unde dolore atque ab sunt sint eligendi reprehenderit quo debitis ullam facere accusantium officia dolorum est pariatur inventore illum. \ No newline at end of file diff --git a/pages/C.md b/pages/C.md new file mode 100644 index 0000000..72354d3 --- /dev/null +++ b/pages/C.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item C +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/D.md b/pages/D.md new file mode 100644 index 0000000..9e160c2 --- /dev/null +++ b/pages/D.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item D +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/E.md b/pages/E.md new file mode 100644 index 0000000..a08706f --- /dev/null +++ b/pages/E.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item E +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/F.md b/pages/F.md new file mode 100644 index 0000000..87783cb --- /dev/null +++ b/pages/F.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item F +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/G.md b/pages/G.md new file mode 100644 index 0000000..87c126b --- /dev/null +++ b/pages/G.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item G +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/pages/H.md b/pages/H.md new file mode 100644 index 0000000..60cfa07 --- /dev/null +++ b/pages/H.md @@ -0,0 +1,8 @@ +--- +layout: main +title: Item H +--- + +# {{page.title}} + +Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quam, aut consequatur voluptates asperiores consectetur cum harum voluptatem consequuntur facilis architecto quidem quibusdam itaque placeat ut ab! Commodi, recusandae, aliquid, earum sed dolor ratione fugiat quibusdam iste nemo qui et molestias ipsa nisi vel similique reiciendis odio amet eos consectetur tempora ut numquam illum voluptatibus dignissimos dolores accusamus nam itaque debitis ad modi quis eum eligendi veritatis. Suscipit, nostrum, deleniti, aliquam doloremque ipsam perspiciatis iusto fuga amet adipisci beatae voluptatum perferendis magni sit reiciendis expedita vel voluptate! Animi, veritatis, nisi laborum ratione sapiente quas libero possimus quis facilis officiis id eius. \ No newline at end of file diff --git a/src/pty.js b/src/pty.js index b799424..23d4a2b 100644 --- a/src/pty.js +++ b/src/pty.js @@ -24,6 +24,7 @@ pty.chart.network = function() { // onClick: function(d, i) {}, nodeClass: function(d, i) { return ''; }, nodeBaseURL: function(d) { return ''; }, + nodeURL: function(d) { return ''; }, chartURL: function(d) { return ''; }, nodeLabel: function(d, i) { return ''; }, fixCenter: true, @@ -105,13 +106,6 @@ pty.chart.network = function() { d.isclick = false; } - function printURL(d, i) { - var myURL = me.chartURL(d); - console.log(myURL); - return myURL; - } - - // Initialization // -------------- @@ -126,7 +120,9 @@ pty.chart.network = function() { gnodes = g.select('g.nodes'), glabels = g.select('g.labels'), gbrand = g.select('g.brand'), - // gnodeURL = g.select('g.nodeURL'), + gNodeUrl = g.select('g.url-container'), + nodeUrlLabel = gNodeUrl.select('text.url-container'), + nodeUrlLink = gNodeUrl.select('a.url-container'), grefresh = g.select('g.button'); @@ -185,15 +181,14 @@ pty.chart.network = function() { d3.select(this).classed('node-highlight', false); }) .on('click', function(d, i) { + if (d3.select(this).classed('node-clickable')) { onClick(d, i); - gnodeurl.selectAll('text').remove(); - gnodeurl.append('text').attr('text-anchor','start').text(printURL(d)); } - else { - gnodeurl.selectAll('text').remove(); - gnodeurl.append('text').attr('text-anchor','start').text(printURL(d)); //URL does not update properly. - } + + // Update the link and label + nodeUrlLink.attr('xlink:href', me.nodeURL(d)); + nodeUrlLabel.text(d.name); }); @@ -244,20 +239,18 @@ pty.chart.network = function() { //NOTE: we should be able to appeal to the url of the root, but data.root has no id. //Write a function that returns the node corresponding to the root. Then apply nodeBaseURL to it. var refreshButton = gbutton.append('circle') - .attr('cx',10) - .attr('cy',10) - .attr('r',10) - .attr('fill','white') - .attr('stroke','black') - .attr('stroke-width',2) - .attr('cursor','pointer') - .on('click', function() { - - d3.json(me.initialData, function(error,data) { - div.data([data]).call(chart); - }) - - } ); + .attr('cx',10) + .attr('cy',10) + .attr('r',10) + .attr('fill','white') + .attr('stroke','black') + .attr('stroke-width',2) + .attr('cursor','pointer') + .on('click', function() { + d3.json(me.initialData, function(error,data) { + div.data([data]).call(chart); + }); + }); gbutton.append('text') .attr('x',4) @@ -266,19 +259,19 @@ pty.chart.network = function() { .attr('font-family', 'FontAwesome') .text('\uf0e2' ) .on('click', function() { + d3.json(me.initialData, function(error,data) { + div.data([data]).call(chart); + }); + }); - d3.json(me.initialData, function(error,data) { - div.data([data]).call(chart); - }) - - } ); + // //Container for the url + // var gnodeurl = svgEnter.append('g') + // .attr('class','urlcontainer') + // .attr('transform','translate(' + [4, me.height - 8 ] +')'); - //Container for the url - var gnodeurl = svgEnter.append('g') - .attr('class','urlcontainer') - .attr('transform','translate(' + [4, me.height -8 ] +')'); + // console.log(gnodeurl); - gnodeurl.append('text').attr('text-anchor','start').text("Hi five!"); + // gnodeurl.append('text').attr('text-anchor','start').text("Hi five!"); }); } @@ -304,20 +297,28 @@ pty.chart.network = function() { gcont.append('g').attr('class', 'nodes'); gcont.append('g').attr('class', 'labels'); - var gbrand = gcont.append('g') + // Brand + // ----- + var gBrand = gcont.append('g') .attr('class', 'brand') .attr('transform', 'translate(' + [me.width - 4, me.height - 4] + ')'); - var brandLabel = gbrand.append('a') + var brandLabel = gBrand.append('a') .attr('xlink:href', 'http://www.masega.co') .append('text') .attr('class', 'masega-brand') .attr('text-anchor', 'end') .text('masega.co'); - // var gnodeurl = gcont.append('g') - // .attr('class','urlcontainer') - // .attr('transform','translate(' + [4, me.height -8 ] +')'); + var gNodeUrl = gcont.append('g') + .attr('class','url-container') + .attr('transform','translate(' + [4, me.height - 8] +')'); + + gNodeUrl.append('a') + .attr('class', 'url-container') + .append('text') + .attr('class', 'url-container') + .text(''); // var gprintnodeurl = gnodeurl.append('a') // .attr('xlink:href', me.chartURL(this)) From 37e18776bd1bcf3e680b4ee82bf1b98d41f1769e Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Thu, 27 Feb 2014 11:45:32 -0300 Subject: [PATCH 08/33] Include fontawesome in the local dependencies --- Gruntfile.js | 19 +++++++++++++++++++ bower.json | 3 ++- index.md | 2 +- src/pty.js | 2 -- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 851bcfa..da7e5e8 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -32,6 +32,25 @@ module.exports = function(grunt) { ] }, + fontawesome: { + files: [ + { + cwd: 'bower_components/font-awesome/fonts', + src: '**.*', + dest: 'fonts/', + filter: 'isFile', + expand: true + }, + { + cwd: 'bower_components/font-awesome/css', + src: '**.min.css', + dest: 'css/', + filter: 'isFile', + expand: true + } + ] + }, + jquery: { src: 'bower_components/jquery/dist/jquery.min.js', dest: 'js/lib/jquery.min.js' diff --git a/bower.json b/bower.json index e8af9e8..a285e74 100644 --- a/bower.json +++ b/bower.json @@ -22,7 +22,8 @@ "tests" ], "dependencies": { - "d3": "~3.4.2" + "d3": "~3.4.2", + "font-awesome": "~4.0.3" }, "devDependencies": { "bootstrap": "~3.1.1" diff --git a/index.md b/index.md index 3047284..d5083df 100644 --- a/index.md +++ b/index.md @@ -6,7 +6,7 @@ title: Panama - +
diff --git a/src/pty.js b/src/pty.js index 23d4a2b..952ef54 100644 --- a/src/pty.js +++ b/src/pty.js @@ -191,8 +191,6 @@ pty.chart.network = function() { nodeUrlLabel.text(d.name); }); - - circles.call(force.drag); circles.exit().remove(); From 972366a64af7cad660fedc7d7e46dc2d650908d1 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Thu, 27 Feb 2014 11:45:48 -0300 Subject: [PATCH 09/33] Add the fontawesome assets --- css/font-awesome.min.css | 4 + fonts/FontAwesome.otf | Bin 0 -> 62856 bytes fonts/fontawesome-webfont.eot | Bin 0 -> 38205 bytes fonts/fontawesome-webfont.svg | 414 +++++++++++++++++++++++++++++++++ fonts/fontawesome-webfont.ttf | Bin 0 -> 80652 bytes fonts/fontawesome-webfont.woff | Bin 0 -> 44432 bytes 6 files changed, 418 insertions(+) create mode 100644 css/font-awesome.min.css create mode 100644 fonts/FontAwesome.otf create mode 100644 fonts/fontawesome-webfont.eot create mode 100644 fonts/fontawesome-webfont.svg create mode 100644 fonts/fontawesome-webfont.ttf create mode 100644 fonts/fontawesome-webfont.woff diff --git a/css/font-awesome.min.css b/css/font-awesome.min.css new file mode 100644 index 0000000..449d6ac --- /dev/null +++ b/css/font-awesome.min.css @@ -0,0 +1,4 @@ +/*! + * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.0.3');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.3333333333333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.2857142857142858em;text-align:center}.fa-ul{padding-left:0;margin-left:2.142857142857143em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.142857142857143em;width:2.142857142857143em;top:.14285714285714285em;text-align:center}.fa-li.fa-lg{left:-1.8571428571428572em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0,mirror=1);-webkit-transform:scale(-1,1);-moz-transform:scale(-1,1);-ms-transform:scale(-1,1);-o-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2,mirror=1);-webkit-transform:scale(1,-1);-moz-transform:scale(1,-1);-ms-transform:scale(1,-1);-o-transform:scale(1,-1);transform:scale(1,-1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-asc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-desc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-reply-all:before{content:"\f122"}.fa-mail-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"} \ No newline at end of file diff --git a/fonts/FontAwesome.otf b/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..8b0f54e47e1d356dcf1496942a50e228e0f1ee14 GIT binary patch literal 62856 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
+ + +

Setting link to a new entity

+ +In the following example, when the user clicks on a node, a link appears on the bottom left of the chart. The text corresponds to the `.nodeLabel()` while the link can be set using `.nodeURL()`. + +{% highlight javascript %} +// Set the function to generate the URL of each node +var chart = pty.chart.network() + .nodeLabel(function(d) { return d.name; }) + .nodeURL(function(d) { return '{{site.baseurl}}/pages/' + d.id; }); + +// Bind the container div to the data and invoke the chart +d3.select('div#chart') + .data([data]) + .call(chart); +{% endhighlight %} + +
+ + +

Embed

Create a page containing a single network chart (see [here]({{site.baseurl}}/embed) for instance) and insert the `embed` tag with appropiate values. diff --git a/less/pty.less b/less/pty.less index d0deebf..57d304f 100644 --- a/less/pty.less +++ b/less/pty.less @@ -54,7 +54,7 @@ // Node Link .url-container { - fill: @grey-dark; + fill: black; a { text-decoration: none; } diff --git a/src/pty.js b/src/pty.js index a9ade25..2228f7f 100644 --- a/src/pty.js +++ b/src/pty.js @@ -210,7 +210,12 @@ pty.chart.network = function() { circles.call(force.drag); - circles.exit().remove(); + circles.exit() + .transition() + .delay(300) + .duration(2000) + .attr('cy', me.width) + .remove(); // Labels // ------ From 73f0e1af8e6ecf9beb9a40cac7807748c64a38cf Mon Sep 17 00:00:00 2001 From: gregorio Date: Fri, 28 Feb 2014 22:16:16 -0300 Subject: [PATCH 14/33] Added an uggly green button that zooms in (and out with shift+click). Zoom in button is zooming in with double click only --- src/pty.js | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/pty.js b/src/pty.js index 2228f7f..a4aa396 100644 --- a/src/pty.js +++ b/src/pty.js @@ -95,7 +95,14 @@ pty.chart.network = function() { } }); + //Zoom + var zooming = d3.behavior.zoom() + .scaleExtent([0, 10]) + .on("zoom", zoomed); + function zoomed() { + plotcontainer.attr("transform", "scale(" + d3.event.scale + ")"); + } function onClick(d, i) { @@ -123,6 +130,8 @@ pty.chart.network = function() { .call(chart.init); var g = svg.select('g.network-chart'), + gplot = g.append('g'), + plotcontainer = g.select('g.graph'), glinks = g.select('g.links'), gnodes = g.select('g.nodes'), glabels = g.select('g.labels'), @@ -133,6 +142,14 @@ pty.chart.network = function() { grefresh = g.select('g.refresh-button'); gZoomContainer = g.select('g.zoom-container'); + var rect = svgEnter.append("rect") + .attr('x',100) + .attr("width", 40) + .attr("height", 40) + .style("fill", "green") + .style("pointer-events", "fill") + .call(zooming); + // Force layout // ------------ @@ -261,7 +278,7 @@ pty.chart.network = function() { div.data([firstData]).call(chart); }); - gZoomContainer.select('.zoom-in-button').on('click', function() { console.log('Zooming in!'); }); + gZoomContainer.select('.zoom-in-button').call(zooming); gZoomContainer.select('.zoom-out-button').on('click', function() { console.log('Zooming out!'); }); @@ -287,9 +304,10 @@ pty.chart.network = function() { .attr('height', me.height) .attr('class', 'background'); - gcont.append('g').attr('class', 'links'); - gcont.append('g').attr('class', 'nodes'); - gcont.append('g').attr('class', 'labels'); + var graphcont = gcont.append('g').attr('class','graph'); + graphcont.append('g').attr('class', 'links'); + graphcont.append('g').attr('class', 'nodes'); + graphcont.append('g').attr('class', 'labels'); //Refresh button var gRefreshButton = gcont.append('g') @@ -313,7 +331,8 @@ pty.chart.network = function() { .attr('font-family', 'FontAwesome') .text('\uf0e2' ); - //Zoom + //Zoom button + var gZoom = gcont.append('g') .attr('class','zoom-container') From 5f15ab5ebb2246d6f1a118392acddda44ae0ad14 Mon Sep 17 00:00:00 2001 From: gregorio Date: Sat, 1 Mar 2014 11:06:03 -0300 Subject: [PATCH 15/33] Implemented zoom in by scaling with green button, working on centering, but now leaving --- src/pty.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pty.js b/src/pty.js index a4aa396..e18fc02 100644 --- a/src/pty.js +++ b/src/pty.js @@ -142,13 +142,18 @@ pty.chart.network = function() { grefresh = g.select('g.refresh-button'); gZoomContainer = g.select('g.zoom-container'); + var l = 1.5; + var rect = svgEnter.append("rect") .attr('x',100) .attr("width", 40) .attr("height", 40) .style("fill", "green") .style("pointer-events", "fill") - .call(zooming); + .on('click', function() { + plotcontainer.attr('transform','scale(' + l + ')translate(' + [-me.width*0.125,-me.height*0.125] + ')'); + l = l+0.5; + }); // Force layout From 19a81d0272a7132c249d3966d7b87dcdc9c39bba Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sat, 1 Mar 2014 11:49:24 -0300 Subject: [PATCH 16/33] Improve the zoom behavior. - New DOM structure. - Move the data processing to a method. - Bind the zoom behavior to the background. - Add controls programatically. - Bind the callbacks to the refresh, zoom in and zoom out buttons. --- css/index.css | 2 +- css/pty.css | 2 +- less/index.less | 29 +-- less/pty.less | 27 +-- src/pty.js | 472 ++++++++++++++++++++++++++---------------------- 5 files changed, 276 insertions(+), 256 deletions(-) diff --git a/css/index.css b/css/index.css index 25f4c09..1ff1316 100644 --- a/css/index.css +++ b/css/index.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .refresh-button{fill:#c7f464;stroke:#4ecdc4;stroke-width:3}.network-chart .refresh-button .refresh-button-arrow{fill:#556270;font-family:FontAwesome;stroke-width:.5}.network-chart .zoom-in-button{fill:#c7f464;font-size:15}.network-chart .zoom-out-button{fill:#c7f464;font-size:15} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270} \ No newline at end of file diff --git a/css/pty.css b/css/pty.css index da82cd3..25530fe 100644 --- a/css/pty.css +++ b/css/pty.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .refresh-button{fill:#fff;stroke:#000;stroke-width:2}.network-chart .refresh-button .refresh-button-arrow{fill:#000;font-collor:#000;font-family:FontAwesome;stroke-width:.4} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270} \ No newline at end of file diff --git a/less/index.less b/less/index.less index 2812e44..94ed0ea 100644 --- a/less/index.less +++ b/less/index.less @@ -3,6 +3,7 @@ @grey-dark: #333; @grey-medium: #777; @grey-light: #ddd; +@white: #fff; @slate-dark: #556270; @green-light: #C7F464; @@ -73,28 +74,18 @@ font-size: 11px; } - // Refresh Button - .refresh-button { - fill: @green-light; - stroke: @aqua-light; - stroke-width: 3; - .refresh-button-arrow { - fill: @slate-dark; - font-family: FontAwesome; - stroke-width: 0.5; + // Controls + // -------- + .control-item { + rect { + fill: @white; + fill-opacity: 0.75; } - } - - //Zoom in and out buttons - .zoom-in-button { - fill: @green-light; - font-size: 15; - } + text { + fill: @slate-dark; + } - .zoom-out-button { - fill: @green-light; - font-size: 15; } } diff --git a/less/pty.less b/less/pty.less index 57d304f..064b36e 100644 --- a/less/pty.less +++ b/less/pty.less @@ -3,6 +3,7 @@ @grey-dark: #333; @grey-medium: #777; @grey-light: #ddd; +@white: #fff; @slate-dark: #556270; @green-light: #C7F464; @@ -76,25 +77,17 @@ font-size: 11px; } - // Refresh Button - .refresh-button{ - fill: white; - stroke: black; - stroke-width: 2; - .refresh-button-arrow{ - fill: black; - font-collor: black; - font-family: FontAwesome; - stroke-width: 0.4; + // Controls + // -------- + .control-item { + rect { + fill: @white; + fill-opacity: 0.75; } - } - - //Zoom in and out buttons - .zoom-in-button { - } - - .zoom-out-button { + text { + fill: @slate-dark; + } } } diff --git a/src/pty.js b/src/pty.js index a4aa396..9253919 100644 --- a/src/pty.js +++ b/src/pty.js @@ -4,6 +4,25 @@ var pty = { version: '0.1.0' // semver }; +// SVG Transformations +// ------------------- +pty.svg = {}; + +// SVG Translation +pty.svg.translate = function(dx, dy) { + if ((arguments.length === 1) && (dx.length === 2)) { + dy = dx[1]; dx = dx[0]; + } + + return 'translate(' + [dx, dy] + ')'; +}; + +// SVG Scale +pty.svg.scale = function(sx, sy) { + if (arguments.length < 2) { sy = sx; } + return 'scale(' + [sx, sy] + ')'; +}; + // Charting Module pty.chart = {}; @@ -26,8 +45,13 @@ pty.chart.network = function() { nodeURL: function(d) { return ''; }, nodeLabel: function(d, i) { return ''; }, fixCenter: true, + duration: 2000, + delay: 200, + zoomExtent: [1, 8], + controlsPosition: [10, 10] }; + // Flag to know if the network chart has been drawn var initialData = false; // Charting Function @@ -35,142 +59,78 @@ pty.chart.network = function() { selection.each(function(data) { var div = d3.select(this), - svg = div.selectAll('svg').data([data]); - - // Data Preprocessing - // ------------------ - - if (!initialData) { - data.nodes.forEach(function(d) { d.__first = true; }); - data.links.forEach(function(d) { d.__first = true; }); - initialData = true; - } - - var idx = {}, - dataNodes = [], - k = 0; - - // Identify unique nodes and construct a dictionary of indices - data.nodes.forEach(function(d) { - if (!idx.hasOwnProperty(d.id)) { - idx[d.id] = k; - k += 1; - dataNodes.push(d); - } - }); - - dataNodes.forEach( function(d) { - d.internalcn = 0; - }); - - // Identify unique links and set the source and target of each one - k = 0; - var idxLinks = {}, dataLinks = []; - - data.links.forEach(function(d) { - - d.source = Math.min(idx[d.from], idx[d.to]); - d.target = Math.max(idx[d.from], idx[d.to]); - d.linkID = d.source + '-' + d.target; - - if (!idxLinks.hasOwnProperty(d.linkID)) { - idxLinks[d.linkID] = k; - dataLinks.push(d); - dataNodes[idx[d.from]].internalcn += 1; - dataNodes[idx[d.to]].internalcn += 1; - k += 1; - } - }); - - dataNodes.forEach( function(d) { - d.isclick = (d.internalcn < d.numcn); - }); + svg = div.selectAll('svg').data([data]), + svgEnter = svg.enter().append('svg').call(chart.init); + + var gContainer = svg.select('g.network-chart'), + gBackground = gContainer.select('g.background'), + gControls = gContainer.select('g.controls'), + gZoomCont = gContainer.select('g.zoom-container'), + gChart = gZoomCont.select('g.network'), + gLinks = gChart.select('g.links'), + gNodes = gChart.select('g.nodes'), + gLabels = gChart.select('g.labels'), + gNodeUrl = gControls.select('g.url-container'), + nodeUrlLabel = gNodeUrl.select('text.url-container'), + nodeUrlLink = gNodeUrl.select('a.url-container'); - // Fix the center node - dataNodes.forEach(function(d) { - if (d.id === data.root) { - d.x = me.width / 2; - d.y = me.height / 2; - d.fixed = me.fixCenter; - } - }); + // Controls + var gControlRefresh = gControls.select('g.control-item.refresh'), + gControlZoomIn = gControls.select('g.control-item.zoom-in'), + gControlZoomOut = gControls.select('g.control-item.zoom-out'); - //Zoom - var zooming = d3.behavior.zoom() - .scaleExtent([0, 10]) - .on("zoom", zoomed); + // Process the data to remove duplicate links + var networkData = chart.parseNetworkData(data); - function zoomed() { - plotcontainer.attr("transform", "scale(" + d3.event.scale + ")"); + // Mouse zoom callback + function onZoom() { + var d = d3.event.translate, + s = d3.event.scale; + gChart.attr('transform', pty.svg.translate(d) + pty.svg.scale(s)); } + // Configure the zoom behavior + var zoomBehavior = d3.behavior.zoom() + .scaleExtent(me.zoomExtent) + .on('zoom', onZoom); + // Bind the zoom behavior to the background rectangle + gBackground.select('rect.background').call(zoomBehavior); + + // The click callback requests the network of the clicked node function onClick(d, i) { - d3.json(me.nodeBaseURL(d), function(error, data) { + d3.json(me.nodeBaseURL(d), function(error, nodeData) { var olddata; if (!error) { olddata = div.data()[0]; - olddata.nodes = olddata.nodes.concat(data.nodes); - olddata.links = olddata.links.concat(data.links); + olddata.nodes = olddata.nodes.concat(nodeData.nodes); + olddata.links = olddata.links.concat(nodeData.links); div.data([olddata]).call(chart); } }); + d.isclick = false; } - // Initialization - // -------------- - - // Initialize the SVG element - var svgEnter = svg.enter().append('svg') - .attr('width', me.width) - .attr('height', me.height) - .call(chart.init); - - var g = svg.select('g.network-chart'), - gplot = g.append('g'), - plotcontainer = g.select('g.graph'), - glinks = g.select('g.links'), - gnodes = g.select('g.nodes'), - glabels = g.select('g.labels'), - gbrand = g.select('g.brand'), - gNodeUrl = g.select('g.url-container'), - nodeUrlLabel = gNodeUrl.select('text.url-container'), - nodeUrlLink = gNodeUrl.select('a.url-container'), - grefresh = g.select('g.refresh-button'); - gZoomContainer = g.select('g.zoom-container'); - - var rect = svgEnter.append("rect") - .attr('x',100) - .attr("width", 40) - .attr("height", 40) - .style("fill", "green") - .style("pointer-events", "fill") - .call(zooming); - - // Force layout // ------------ + // Configure and start the force layout var force = d3.layout.force() .charge(me.charge) .friction(me.friction) .linkStrength(me.linkStrength) - .size([me.width, me.height]); - - force.nodes(dataNodes) - .links(dataLinks) + .size([me.width, me.height]) + .nodes(networkData.nodes) + .links(networkData.links) .start(); - // Initialize the node label to have the root node information - - var nodeRoot = dataNodes.filter(function(d) { return d.id === data.root; }).pop(); - + // Set the label to the root node if (!svgEnter.empty()) { - nodeUrlLink.attr('xlink:href', me.nodeURL(nodeRoot)); - nodeUrlLabel.text(me.nodeLabel(nodeRoot)); + nodeUrlLink.attr('xlink:href', me.nodeURL(networkData.rootNode)); + nodeUrlLabel.text(me.nodeLabel(networkData.rootNode)); } // Graphic Elements @@ -179,13 +139,13 @@ pty.chart.network = function() { // Links // ----- - var links = glinks.selectAll('line.link') + var links = gLinks.selectAll('line.link') .data(force.links(), function(d) { return d.linkID; }); links.enter().append('line') .attr('class', 'link') .classed('center-link', function(d) { - return (d.from === data.root) || (d.to === data.root); + return (d.from === networkData.root) || (d.to === networkData.root); }); // Remove unused links @@ -194,95 +154,106 @@ pty.chart.network = function() { // Nodes // ----- - var circles = gnodes.selectAll('circle.node') + var circles = gNodes.selectAll('circle.node') .data(force.nodes(), function(d) { return d.id; }); // Update circles.classed('node-clickable', function(d) { return d.isclick; }); circles.enter().append('circle') - .attr('class', function(d, i) { - return me.nodeClass(d, i) + ' node'; - }) + .attr('class', function(d, i) { return me.nodeClass(d, i) + ' node'; }) .attr('r', me.nodeRadius) .classed('node', true) .classed('node-center', function(d) { return d.id === data.root; }) .classed('node-clickable', function(d) { return (d.id !== data.root) && (d.isclick); }) - .on('mouseover', function(d) { - d3.select(this).classed('node-highlight', true); - }) - .on('mouseout', function(d) { - d3.select(this).classed('node-highlight', false); - }) + .on('mouseover', function(d) { d3.select(this).classed('node-highlight', true); }) + .on('mouseout', function(d) { d3.select(this).classed('node-highlight', false); }) .on('click', function(d, i) { - - if (d3.select(this).classed('node-clickable')) { - onClick(d, i); - } - - // Update the link and label + if (d3.select(this).classed('node-clickable')) { onClick(d, i); } + // Update the title and link of the lower left label nodeUrlLink.attr('xlink:href', me.nodeURL(d)); nodeUrlLabel.text('' + me.nodeLabel(d)); }); circles.call(force.drag); - circles.exit() - .transition() - .delay(300) - .duration(2000) + // Drop the circles on exit + circles.exit().transition() + .delay(me.delay) + .duration(me.duration) .attr('cy', me.width) .remove(); // Labels // ------ - var words = glabels.selectAll('text.node-label') + // Select the labels + var nodeLabels = gLabels.selectAll('text.node-label') .data(force.nodes(), function(d) { return d.id; }); - words.enter().append('text') + // Append the text elements on enter + nodeLabels.enter().append('text') .text(me.nodeLabel) .attr('x', function(d, i) { return d.x + me.nodeRadius; }) .attr('y', function(d, i) { return d.y - me.nodeRadius; }) .attr('class', 'node-label'); - words.exit().remove(); + // Remove the labels on exit + nodeLabels.exit().remove(); - // Force On Tick + // Update the position of nodes, labels and links on each tick event + // of the force layout. force.on('tick', function() { links - .attr("x1", function(d) { return d.source.x; }) - .attr("y1", function(d) { return d.source.y; }) - .attr("x2", function(d) { return d.target.x; }) - .attr("y2", function(d) { return d.target.y; }); + .attr('x1', function(d) { return d.source.x; }) + .attr('y1', function(d) { return d.source.y; }) + .attr('x2', function(d) { return d.target.x; }) + .attr('y2', function(d) { return d.target.y; }); circles .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }); - words + nodeLabels .attr('x', function(d, i) { return d.x + me.nodeRadius; }) .attr('y', function(d, i) { return d.y - me.nodeRadius; }); - }); - //Other elements - //-------------- - grefresh.on('click', function() { - var firstData = { - root: data.root, - nodes: data.nodes.filter(function(d) { return d.__first; }), - links: data.links.filter(function(d) { return d.__first; }) - }; - div.data([firstData]).call(chart); - }); + // Control Callbacks + //------------------ - gZoomContainer.select('.zoom-in-button').call(zooming); + // Refresh + gControlRefresh.on('click', function() { + var firstData = { + root: data.root, + nodes: data.nodes.filter(function(d) { return d.__first; }), + links: data.links.filter(function(d) { return d.__first; }) + }; + div.data([firstData]).call(chart); + }); - gZoomContainer.select('.zoom-out-button').on('click', function() { console.log('Zooming out!'); }); + // Zoom In + gControlZoomIn.on('click', function() { + // Compute the new zoom level and update the zoom behavior + var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]); + zoomBehavior.scale(newScale); + // Scale the chart group + gChart.transition().duration(me.duration) + .attr('transform', pty.svg.scale(newScale)); + }); + + // Zoom Out + gControlZoomOut.on('click', function() { + // Compute the new zoom level and update the zoom behavior + var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]); + zoomBehavior.scale(newScale); + // Scale the chart group + gChart.transition().duration(me.duration) + .attr('transform', pty.svg.scale(newScale)); + }); }); } @@ -291,73 +262,74 @@ pty.chart.network = function() { chart.init = function(selection) { selection.each(function(data) { - var initial = selection; - var svgEnter = d3.select(this), - gcont = svgEnter.append('g').attr('class', 'network-chart'); - - // Add a background group and rectangle - gcont.append('g') - .attr('class', 'background') - .append('rect') + gContainer = svgEnter.append('g').attr('class', 'network-chart'), + gBackground = gContainer.append('g').attr('class', 'background'), + gControls = gContainer.append('g').attr('class', 'controls'), + gBrand = gContainer.append('g').attr('class', 'brand'), + gZoomCont = gContainer.append('g').attr('class', 'zoom-container'), + gChart = gZoomCont.append('g').attr('class', 'network'); + + // Set the SVG element width and height + svgEnter.attr('width', me.width).attr('height', me.height); + + // Background + gBackground.append('rect') .attr('width', me.width) .attr('height', me.height) .attr('class', 'background'); - var graphcont = gcont.append('g').attr('class','graph'); - graphcont.append('g').attr('class', 'links'); - graphcont.append('g').attr('class', 'nodes'); - graphcont.append('g').attr('class', 'labels'); - - //Refresh button - var gRefreshButton = gcont.append('g') - .attr('class','refresh-button') - .attr('transform', 'translate(' + [10, 10] +')'); - - var RefreshCircle = gRefreshButton.append('circle') - .attr('class','refresh-button') - .attr('cx',10) - .attr('cy',10) - .attr('r',10) - .attr('stroke','black') - .attr('stroke-width',2) - .attr('cursor','pointer'); - - var RefreshIcon = gRefreshButton.append('text') - .attr('class','refresh-button-arrow') - .attr('x',4) - .attr('y',15) - .attr('cursor','pointer') - .attr('font-family', 'FontAwesome') - .text('\uf0e2' ); - - //Zoom button - - - var gZoom = gcont.append('g') - .attr('class','zoom-container') - .attr('transform', 'translate(' + [13,70] + ')'); - - var gZoomIn = gZoom.append('text') - .attr('class','zoom-in-button') - .attr('cursor','pointer') - .attr('font-family','FontAwesome') - .attr('font-size',17) - .text('\uf0fe'); - - var gZoomOut = gZoom.append('text') - .attr('class','zoom-out-button') - .attr('cursor','pointer') - .attr('y',30) - .attr('font-family','FontAwesome') - .attr('font-size',17) - .text('\uf146'); + // Nodes, Links and Labels + gChart.append('g').attr('class', 'links'); + gChart.append('g').attr('class', 'nodes'); + gChart.append('g').attr('class', 'labels'); + + // Controls + // -------- + + gControls + .attr('transform', pty.svg.translate(me.controlsPosition)); + + // Refresh Button + var controls = [ + {name: 'refresh', icon: '\uf0e2'}, + {name: 'zoom-in', icon: '\uf067'}, + {name: 'zoom-out', icon: '\uf068'} + ]; + + // Controls Scale + var yScale = d3.scale.ordinal() + .domain(d3.range(controls.length)) + .rangeBands([0, 30 * controls.length], 0.1); + + var bgSize = yScale.rangeBand(); + + var gControlItem = gControls.selectAll('g.control-item') + .data(controls); + + gControlItem.enter().append('g') + .attr('class', function(d) { return 'control-item ' + d.name; }) + .attr('transform', function(d, i) { return pty.svg.translate(0, yScale(i)); }); + + gControlItem.append('rect') + .attr('width', bgSize) + .attr('height', bgSize) + .attr('rx', 0.25 * bgSize) + .attr('ry', 0.25 * bgSize); + + var iconLabel = gControlItem.append('text') + .attr('class', function(d) { return d.name; }) + .attr('font-family', 'FontAwesome') + .attr('fill', 'white') + .text(function(d) { return d.icon; }); + + iconLabel + .attr('x', function() { return 0.5 * (bgSize - this.getBBox().width); }) + .attr('y', function() { return 0.5 * (bgSize + this.getBBox().height) - 2; }); // Brand // ----- - var gBrand = gcont.append('g') - .attr('class', 'brand') - .attr('transform', 'translate(' + [me.width - 4, me.height - 4] + ')'); + gBrand.attr('transform', pty.svg.translate(me.width - 4, me.height - 4)); var brandLabel = gBrand.append('a') .attr('xlink:href', 'http://www.masega.co') @@ -366,29 +338,93 @@ pty.chart.network = function() { .attr('text-anchor', 'end') .text('masega.co'); - var gNodeUrl = gcont.append('g') + // Node Label and Link + // ------------------- + var gNodeUrl = gControls.append('g') .attr('class','url-container') - .attr('transform','translate(' + [10, me.height - 8] +')'); + .attr('transform', pty.svg.translate(10, me.height - 16)); + // Link var gNodeLink = gNodeUrl.append('a') .attr('class', 'url-container'); - gNodeLink - .append('text') + // Icon + gNodeLink.append('text') .attr('class', 'url-container-icon') .text('\uf0c1'); - gNodeLink - .append('text') + // Label + gNodeLink.append('text') .attr('x', 20) .attr('class', 'url-container') .text(''); - }); }; + chart.parseNetworkData = function(jsonData) { + + var data = { + root: jsonData.root, + rootNode: null, + nodes: [], + links: [] + }; + + if (!initialData) { + jsonData.nodes.forEach(function(d) { d.__first = true; }); + jsonData.links.forEach(function(d) { d.__first = true; }); + initialData = true; + } + + var idxNodes = {}, + idxLinks = {}, + k = 0; + + // Identify unique nodes and construct a dictionary of indices + jsonData.nodes.forEach(function(d) { + if (!idxNodes.hasOwnProperty(d.id)) { + idxNodes[d.id] = k; + k += 1; + data.nodes.push(d); + } + }); + + data.nodes.forEach( function(d) { d.internalcn = 0; }); + + // Identify unique links and set the source and target of each one + k = 0; + jsonData.links.forEach(function(d) { + + d.source = Math.min(idxNodes[d.from], idxNodes[d.to]); + d.target = Math.max(idxNodes[d.from], idxNodes[d.to]); + d.linkID = d.source + '-' + d.target; + + if (!idxLinks.hasOwnProperty(d.linkID)) { + idxLinks[d.linkID] = k; + data.links.push(d); + data.nodes[idxNodes[d.from]].internalcn += 1; + data.nodes[idxNodes[d.to]].internalcn += 1; + k += 1; + } + }); + + data.nodes.forEach( function(d) { d.isclick = (d.internalcn < d.numcn); }); + + // Set the position of the root node and fix it to the center + data.nodes.forEach(function(d) { + if (d.id === data.root) { + d.x = me.width / 2; + d.y = me.height / 2; + d.fixed = me.fixCenter; + data.rootNode = d; + } + }); + + return data; + }; // Accessor Methods + // ---------------- // Generate Accessor Methods function createAccessor(attr) { From 2d16c2db2aeaedf3d935ed151993792b2828854a Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sat, 1 Mar 2014 12:07:10 -0300 Subject: [PATCH 17/33] Fix the zoom translation --- src/pty.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/pty.js b/src/pty.js index 9253919..266c8de 100644 --- a/src/pty.js +++ b/src/pty.js @@ -236,23 +236,27 @@ pty.chart.network = function() { // Zoom In gControlZoomIn.on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]); + var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]), + translate = pty.svg.translate(zoomBehavior.translate()); + zoomBehavior.scale(newScale); // Scale the chart group gChart.transition().duration(me.duration) - .attr('transform', pty.svg.scale(newScale)); + .attr('transform', translate + pty.svg.scale(newScale)); }); // Zoom Out gControlZoomOut.on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]); + var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]), + translate = pty.svg.translate(zoomBehavior.translate()); + zoomBehavior.scale(newScale); // Scale the chart group gChart.transition().duration(me.duration) - .attr('transform', pty.svg.scale(newScale)); + .attr('transform', translate + pty.svg.scale(newScale)); }); }); From 3e9057b1b19fb7712701fb186f6e907ed5246dee Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 13:35:23 -0300 Subject: [PATCH 18/33] Add the embed demo --- Gruntfile.js | 16 +++++++++++++--- bower.json | 3 ++- embed.md | 23 +---------------------- src/pty.js | 22 ++++++++++++++++------ 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0ef46f4..7bcd099 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -59,6 +59,11 @@ module.exports = function(grunt) { d3: { src: 'bower_components/d3/d3.min.js', dest: 'js/lib/d3.min.js' + }, + + underscore: { + src: 'bower_components/underscore/underscore.js', + dest: 'js/lib/underscore.js' } }, @@ -77,9 +82,16 @@ module.exports = function(grunt) { }, watch: { + options: { + livereload: true, + }, less: { files: ['less/*.less'], tasks: ['less'] + }, + pty: { + files: ['src/pty.js'], + tasks: ['less'] } } @@ -92,9 +104,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-watch'); - // Register Tasks - - // Test Task + // Tasks grunt.registerTask('build', ['copy', 'less']); grunt.registerTask('dist', ['build']); grunt.registerTask('default', ['build']); diff --git a/bower.json b/bower.json index a285e74..417b547 100644 --- a/bower.json +++ b/bower.json @@ -23,7 +23,8 @@ ], "dependencies": { "d3": "~3.4.2", - "font-awesome": "~4.0.3" + "font-awesome": "~4.0.3", + "underscore": "~1.6.0" }, "devDependencies": { "bootstrap": "~3.1.1" diff --git a/embed.md b/embed.md index b1692ca..020de5a 100644 --- a/embed.md +++ b/embed.md @@ -23,28 +23,7 @@ title: Embed .height(height) .nodeRadius(15) .nodeBaseURL(function(d) { return '{{site.baseurl}}/data/' + d.id + '.json'; }); - // .onClick(function(d) { - - // d.isclick = false; - - // var dataurl = "../data/" + d.id + ".json"; - - - // d3.json(dataurl, function(error, data) { - - // if (!error) { - - // var olddata = d3.select('div#chart01').data()[0]; - - // olddata.nodes = olddata.nodes.concat(data.nodes); - // olddata.links = olddata.links.concat(data.links); - - // d3.select('div#chart01') - // .data([olddata]) - // .call(chart01);} - // }); - // }); d3.select('div#chart01').data([data]).call(chart01); }); - \ No newline at end of file + diff --git a/src/pty.js b/src/pty.js index 266c8de..5ef9ecc 100644 --- a/src/pty.js +++ b/src/pty.js @@ -48,7 +48,8 @@ pty.chart.network = function() { duration: 2000, delay: 200, zoomExtent: [1, 8], - controlsPosition: [10, 10] + controlsPosition: [10, 10], + embedCallback: false }; // Flag to know if the network chart has been drawn @@ -77,7 +78,8 @@ pty.chart.network = function() { // Controls var gControlRefresh = gControls.select('g.control-item.refresh'), gControlZoomIn = gControls.select('g.control-item.zoom-in'), - gControlZoomOut = gControls.select('g.control-item.zoom-out'); + gControlZoomOut = gControls.select('g.control-item.zoom-out'), + gControlEmbed = gControls.select('g.control-item.embed'); // Process the data to remove duplicate links var networkData = chart.parseNetworkData(data); @@ -259,6 +261,12 @@ pty.chart.network = function() { .attr('transform', translate + pty.svg.scale(newScale)); }); + // Embed + if (me.embedCallback) { + gControlEmbed.on('click', me.embedCallback); + } else { + gControlEmbed.remove(); + } }); } @@ -296,9 +304,11 @@ pty.chart.network = function() { // Refresh Button var controls = [ - {name: 'refresh', icon: '\uf0e2'}, - {name: 'zoom-in', icon: '\uf067'}, - {name: 'zoom-out', icon: '\uf068'} + {name: 'refresh', icon: '\uf0e2'}, + {name: 'zoom-in', icon: '\uf067'}, + {name: 'zoom-out', icon: '\uf068'}, + {name: 'embed', icon: '\uf121'}, + {name: 'fullscreen', icon: '\uf065'} ]; // Controls Scale @@ -328,7 +338,7 @@ pty.chart.network = function() { .text(function(d) { return d.icon; }); iconLabel - .attr('x', function() { return 0.5 * (bgSize - this.getBBox().width); }) + .attr('x', function() { return 0.5 * (bgSize - this.getBBox().width) - 1; }) .attr('y', function() { return 0.5 * (bgSize + this.getBBox().height) - 2; }); // Brand From b87796c6f4705e5cd3554c236431ff23c24c6a6d Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 13:35:48 -0300 Subject: [PATCH 19/33] Add the embed demo --- js/lib/underscore.js | 1343 ++++++++++++++++++++++++++++++++++++++++++ pages/embed-demo.md | 94 +++ 2 files changed, 1437 insertions(+) create mode 100644 js/lib/underscore.js create mode 100644 pages/embed-demo.md diff --git a/js/lib/underscore.js b/js/lib/underscore.js new file mode 100644 index 0000000..9a4cabe --- /dev/null +++ b/js/lib/underscore.js @@ -0,0 +1,1343 @@ +// Underscore.js 1.6.0 +// http://underscorejs.org +// (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.6.0'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return obj; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, length = obj.length; i < length; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; + } + } + return obj; + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, predicate, context) { + var result; + any(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, predicate, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context); + each(obj, function(value, index, list) { + if (predicate.call(context, value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, predicate, context) { + return _.filter(obj, function(value, index, list) { + return !predicate.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context); + each(obj, function(value, index, list) { + if (!(result = result && predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, predicate, context) { + predicate || (predicate = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context); + each(obj, function(value, index, list) { + if (result || (result = predicate.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs) { + return _.filter(obj, _.matches(attrs)); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.find(obj, _.matches(attrs)); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + var result = -Infinity, lastComputed = -Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed > lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + var result = Infinity, lastComputed = Infinity; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + if (computed < lastComputed) { + result = value; + lastComputed = computed; + } + }); + return result; + }; + + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, key, value) { + _.has(result, key) ? result[key].push(value) : result[key] = [value]; + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } + each(input, function(value) { + if (_.isArray(value) || _.isArguments(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Split an array into two arrays: one whose elements all satisfy the given + // predicate, and one whose elements all do not satisfy the predicate. + _.partition = function(array, predicate) { + var pass = [], fail = []; + each(array, function(elem) { + (predicate(elem) ? pass : fail).push(elem); + }); + return [pass, fail]; + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(_.flatten(arguments, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.contains(other, item); + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var length = _.max(_.pluck(arguments, 'length').concat(0)); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(arguments, '' + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, length = list.length; i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, length = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < length; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(length); + + while(idx < length) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. _ acts + // as a placeholder, allowing any combination of arguments to be pre-filled. + _.partial = function(func) { + var boundArgs = slice.call(arguments, 1); + return function() { + var position = 0; + var args = boundArgs.slice(); + for (var i = 0, length = args.length; i < length; i++) { + if (args[i] === _) args[i] = arguments[position++]; + } + while (position < arguments.length) args.push(arguments[position++]); + return func.apply(this, args); + }; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) throw new Error('bindAll must be passed function names'); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function() { + previous = options.leading === false ? 0 : _.now(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = _.now(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + + var later = function() { + var last = _.now() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + + return function() { + context = this; + args = arguments; + timestamp = _.now(); + var callNow = immediate && !timeout; + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = function(obj) { + if (!_.isObject(obj)) return []; + if (nativeKeys) return nativeKeys(obj); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] === void 0) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Returns a predicate for checking whether an object has a given set of `key:value` pairs. + _.matches = function(attrs) { + return function(obj) { + if (obj === attrs) return true; //avoid comparing an object to itself. + for (var key in attrs) { + if (attrs[key] !== obj[key]) + return false; + } + return true; + } + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(Math.max(0, n)); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // A (possibly faster) way to get the current timestamp as an integer. + _.now = Date.now || function() { return new Date().getTime(); }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property) { + if (object == null) return void 0; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}).call(this); diff --git a/pages/embed-demo.md b/pages/embed-demo.md new file mode 100644 index 0000000..68a9751 --- /dev/null +++ b/pages/embed-demo.md @@ -0,0 +1,94 @@ +--- +layout: main +title: Embed Demo +--- + + + + + + + + +# {{ page.title }} + + + + +
+
+
+
+
+ + + + From 58910c37f60d097ef071350841da24fe5a6d2fac Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 14:17:55 -0300 Subject: [PATCH 20/33] Example of fullscreen --- pages/fullscreen-demo.md | 80 ++++++++++++++++++++++++++++++++++++++++ src/pty.js | 36 +++++++++++++----- 2 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 pages/fullscreen-demo.md diff --git a/pages/fullscreen-demo.md b/pages/fullscreen-demo.md new file mode 100644 index 0000000..a0ecc2a --- /dev/null +++ b/pages/fullscreen-demo.md @@ -0,0 +1,80 @@ +--- +layout: main +title: Fullscreen Demo +--- + + + + + + + + +# {{ page.title }} + +
+ +
+ + +
+
+
+
+
+ + + +
+ diff --git a/src/pty.js b/src/pty.js index 5ef9ecc..8b65edc 100644 --- a/src/pty.js +++ b/src/pty.js @@ -49,7 +49,11 @@ pty.chart.network = function() { delay: 200, zoomExtent: [1, 8], controlsPosition: [10, 10], - embedCallback: false + refreshCallback: true, + zoomInCallback: true, + zoomOutCallback: true, + embedCallback: false, + fullscreenCallback: false }; // Flag to know if the network chart has been drawn @@ -79,7 +83,8 @@ pty.chart.network = function() { var gControlRefresh = gControls.select('g.control-item.refresh'), gControlZoomIn = gControls.select('g.control-item.zoom-in'), gControlZoomOut = gControls.select('g.control-item.zoom-out'), - gControlEmbed = gControls.select('g.control-item.embed'); + gControlEmbed = gControls.select('g.control-item.embed'), + gControlFullscreen = gControls.select('g.control-item.fullscreen'); // Process the data to remove duplicate links var networkData = chart.parseNetworkData(data); @@ -267,6 +272,13 @@ pty.chart.network = function() { } else { gControlEmbed.remove(); } + + // Fullscreen + if (me.fullscreenCallback) { + gControlFullscreen.on('click', me.fullscreenCallback); + } else { + gControlFullscreen.remove(); + } }); } @@ -304,22 +316,26 @@ pty.chart.network = function() { // Refresh Button var controls = [ - {name: 'refresh', icon: '\uf0e2'}, - {name: 'zoom-in', icon: '\uf067'}, - {name: 'zoom-out', icon: '\uf068'}, - {name: 'embed', icon: '\uf121'}, - {name: 'fullscreen', icon: '\uf065'} + {name: 'refresh', icon: '\uf0e2', callback: me.refreshCallback}, + {name: 'zoom-in', icon: '\uf067', callback: me.zoomInCallback}, + {name: 'zoom-out', icon: '\uf068', callback: me.zoomOutCallback}, + {name: 'embed', icon: '\uf121', callback: me.embedCallback}, + {name: 'fullscreen', icon: '\uf065', callback: me.fullscreenCallback} ]; + var activeControls = controls.filter(function(d) { + return d.callback; + }); + // Controls Scale var yScale = d3.scale.ordinal() - .domain(d3.range(controls.length)) - .rangeBands([0, 30 * controls.length], 0.1); + .domain(d3.range(activeControls.length)) + .rangeBands([0, 30 * activeControls.length], 0.1); var bgSize = yScale.rangeBand(); var gControlItem = gControls.selectAll('g.control-item') - .data(controls); + .data(activeControls); gControlItem.enter().append('g') .attr('class', function(d) { return 'control-item ' + d.name; }) From 1e0785c0f59d80a3551ca5919988dfad2ab4d5e1 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 15:47:38 -0300 Subject: [PATCH 21/33] Add the index links --- _includes/navbar.html | 2 ++ index.md | 11 ----------- pages/fullscreen-demo.md | 25 +++++++++++++++---------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/_includes/navbar.html b/_includes/navbar.html index 1923852..9f9f6e8 100644 --- a/_includes/navbar.html +++ b/_includes/navbar.html @@ -15,6 +15,8 @@
diff --git a/index.md b/index.md index b33d731..e01f1b9 100644 --- a/index.md +++ b/index.md @@ -14,17 +14,6 @@ title: Panama
- - From cdd71e99c280841c0029da85b4f84e4413ec3561 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 15:53:37 -0300 Subject: [PATCH 22/33] Fix the fullscreen --- pages/fullscreen-demo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/fullscreen-demo.md b/pages/fullscreen-demo.md index 204dfae..8d319bb 100644 --- a/pages/fullscreen-demo.md +++ b/pages/fullscreen-demo.md @@ -66,7 +66,7 @@ d3.json('{{ site.baseurl }}/data/A.json', function(error, data) { } function toFullScreen() { - chart01 + chart .width(screen.width) .height(screen.height) .fullscreenCallback(toNormal); From c493a8fe55e4769509ba5126bfda5679cff9874b Mon Sep 17 00:00:00 2001 From: gregorio Date: Sun, 2 Mar 2014 17:29:12 -0300 Subject: [PATCH 23/33] Fullscreen button has arrows in right direction, cursors change on mouseover over control buttons --- css/index.css | 2 +- css/pty.css | 2 +- less/index.less | 3 ++ less/pty.less | 3 ++ src/pty.js | 96 +++++++++++++++++++++++++++++++------------------ 5 files changed, 70 insertions(+), 36 deletions(-) diff --git a/css/index.css b/css/index.css index 1ff1316..8048528 100644 --- a/css/index.css +++ b/css/index.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file diff --git a/css/pty.css b/css/pty.css index 25530fe..75f7209 100644 --- a/css/pty.css +++ b/css/pty.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file diff --git a/less/index.less b/less/index.less index 94ed0ea..769f334 100644 --- a/less/index.less +++ b/less/index.less @@ -87,6 +87,9 @@ } } + .control-highlight{ + cursor: pointer; + } } diff --git a/less/pty.less b/less/pty.less index 064b36e..a8edb47 100644 --- a/less/pty.less +++ b/less/pty.less @@ -90,6 +90,9 @@ } } + .control-highlight{ + cursor: pointer; + } } diff --git a/src/pty.js b/src/pty.js index 8b65edc..f2c3a94 100644 --- a/src/pty.js +++ b/src/pty.js @@ -47,7 +47,7 @@ pty.chart.network = function() { fixCenter: true, duration: 2000, delay: 200, - zoomExtent: [1, 8], + zoomExtent: [0.5, 8], controlsPosition: [10, 10], refreshCallback: true, zoomInCallback: true, @@ -58,7 +58,8 @@ pty.chart.network = function() { // Flag to know if the network chart has been drawn var initialData = false; - + // Flag to know if we are in fullscreen mode + var isScreenFull = false; // Charting Function function chart(selection) { selection.each(function(data) { @@ -89,6 +90,12 @@ pty.chart.network = function() { // Process the data to remove duplicate links var networkData = chart.parseNetworkData(data); + if ( isScreenFull ) { + gControlFullscreen.select('text').text('\uf066'); + } else { + gControlFullscreen.select('text').text('\uf065'); + } + // Mouse zoom callback function onZoom() { var d = d3.event.translate, @@ -121,6 +128,7 @@ pty.chart.network = function() { d.isclick = false; } + // Force layout // ------------ @@ -231,51 +239,71 @@ pty.chart.network = function() { //------------------ // Refresh - gControlRefresh.on('click', function() { - var firstData = { - root: data.root, - nodes: data.nodes.filter(function(d) { return d.__first; }), - links: data.links.filter(function(d) { return d.__first; }) - }; - div.data([firstData]).call(chart); - }); + gControlRefresh + .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) + .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) + .on('click', function() { + var firstData = { + root: data.root, + nodes: data.nodes.filter(function(d) { return d.__first; }), + links: data.links.filter(function(d) { return d.__first; }) + }; + div.data([firstData]).call(chart); + }); // Zoom In - gControlZoomIn.on('click', function() { - // Compute the new zoom level and update the zoom behavior - var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]), - translate = pty.svg.translate(zoomBehavior.translate()); - - zoomBehavior.scale(newScale); - - // Scale the chart group - gChart.transition().duration(me.duration) - .attr('transform', translate + pty.svg.scale(newScale)); - }); + gControlZoomIn + .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) + .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) + .on('click', function() { + // Compute the new zoom level and update the zoom behavior + var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]), + translate = pty.svg.translate(zoomBehavior.translate()); + + zoomBehavior.scale(newScale); + + // Scale the chart group + gChart.transition().duration(me.duration) + .attr('transform', translate + pty.svg.scale(newScale)); + }); // Zoom Out - gControlZoomOut.on('click', function() { - // Compute the new zoom level and update the zoom behavior - var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]), - translate = pty.svg.translate(zoomBehavior.translate()); - - zoomBehavior.scale(newScale); - - // Scale the chart group - gChart.transition().duration(me.duration) - .attr('transform', translate + pty.svg.scale(newScale)); - }); + gControlZoomOut + .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) + .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) + .on('click', function() { + // Compute the new zoom level and update the zoom behavior + var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]), + translate = pty.svg.translate(zoomBehavior.translate()); + + zoomBehavior.scale(newScale); + + // Scale the chart group + gChart.transition().duration(me.duration) + .attr('transform', translate + pty.svg.scale(newScale)); + }); // Embed if (me.embedCallback) { - gControlEmbed.on('click', me.embedCallback); + gControlEmbed + .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) + .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) + .on('click', me.embedCallback); } else { gControlEmbed.remove(); } // Fullscreen if (me.fullscreenCallback) { - gControlFullscreen.on('click', me.fullscreenCallback); + gControlFullscreen + .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) + .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) + .on('click', function() { + console.log(isScreenFull); + isScreenFull = !isScreenFull; + console.log(isScreenFull); + d3.select(this).call(me.fullscreenCallback); + }); } else { gControlFullscreen.remove(); } From fc5060954a73baebe9b2f2764b5765364f5e5a39 Mon Sep 17 00:00:00 2001 From: gregorio Date: Sun, 2 Mar 2014 17:51:02 -0300 Subject: [PATCH 24/33] after discussing zoom issues (no voluntary changes) --- src/pty.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pty.js b/src/pty.js index f2c3a94..6e47ebf 100644 --- a/src/pty.js +++ b/src/pty.js @@ -257,9 +257,8 @@ pty.chart.network = function() { .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) .on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.min([zoomBehavior.scale() + 1, me.zoomExtent[1]]), + var newScale = d3.min([zoomBehavior.scale() + 0.2, me.zoomExtent[1]]), translate = pty.svg.translate(zoomBehavior.translate()); - zoomBehavior.scale(newScale); // Scale the chart group From 2827c39cb7f3e00cf13104e23040fcba3bb22cf4 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 18:05:20 -0300 Subject: [PATCH 25/33] Fix the zoom issue (and add another) --- src/pty.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/pty.js b/src/pty.js index 6e47ebf..54c089f 100644 --- a/src/pty.js +++ b/src/pty.js @@ -53,7 +53,8 @@ pty.chart.network = function() { zoomInCallback: true, zoomOutCallback: true, embedCallback: false, - fullscreenCallback: false + fullscreenCallback: false, + zoomBehavior: d3.behavior.zoom() }; // Flag to know if the network chart has been drawn @@ -104,12 +105,12 @@ pty.chart.network = function() { } // Configure the zoom behavior - var zoomBehavior = d3.behavior.zoom() + me.zoomBehavior .scaleExtent(me.zoomExtent) .on('zoom', onZoom); // Bind the zoom behavior to the background rectangle - gBackground.select('rect.background').call(zoomBehavior); + gBackground.select('rect.background').call(me.zoomBehavior); // The click callback requests the network of the clicked node function onClick(d, i) { @@ -257,9 +258,10 @@ pty.chart.network = function() { .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) .on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.min([zoomBehavior.scale() + 0.2, me.zoomExtent[1]]), - translate = pty.svg.translate(zoomBehavior.translate()); - zoomBehavior.scale(newScale); + var newScale = d3.min([me.zoomBehavior.scale() + 0.2, me.zoomExtent[1]]), + translate = pty.svg.translate(me.zoomBehavior.translate()); + + me.zoomBehavior.scale(newScale); // Scale the chart group gChart.transition().duration(me.duration) @@ -272,10 +274,10 @@ pty.chart.network = function() { .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) .on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.max([zoomBehavior.scale() - 1, me.zoomExtent[0]]), - translate = pty.svg.translate(zoomBehavior.translate()); + var newScale = d3.max([me.zoomBehavior.scale() - 1, me.zoomExtent[0]]), + translate = pty.svg.translate(me.zoomBehavior.translate()); - zoomBehavior.scale(newScale); + me.zoomBehavior.scale(newScale); // Scale the chart group gChart.transition().duration(me.duration) From c8a6e8d91e06dd38c793fbde342c993f5c3a4dcf Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Sun, 2 Mar 2014 18:11:26 -0300 Subject: [PATCH 26/33] Apply the zoom behavior on chart --- src/pty.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pty.js b/src/pty.js index 54c089f..6984b6d 100644 --- a/src/pty.js +++ b/src/pty.js @@ -109,6 +109,11 @@ pty.chart.network = function() { .scaleExtent(me.zoomExtent) .on('zoom', onZoom); + gChart + .attr('transform', pty.svg.translate(me.zoomBehavior.translate()) + pty.svg.scale(me.zoomBehavior.scale())); + + + // Bind the zoom behavior to the background rectangle gBackground.select('rect.background').call(me.zoomBehavior); @@ -274,7 +279,7 @@ pty.chart.network = function() { .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) .on('click', function() { // Compute the new zoom level and update the zoom behavior - var newScale = d3.max([me.zoomBehavior.scale() - 1, me.zoomExtent[0]]), + var newScale = d3.max([me.zoomBehavior.scale() - 0.2, me.zoomExtent[0]]), translate = pty.svg.translate(me.zoomBehavior.translate()); me.zoomBehavior.scale(newScale); From 682a7675534a615b612e5a84b854bb37ddeee8a3 Mon Sep 17 00:00:00 2001 From: gregorio Date: Sun, 2 Mar 2014 18:30:38 -0300 Subject: [PATCH 27/33] Cursor to hand on backround --- css/index.css | 2 +- css/pty.css | 2 +- less/index.less | 2 ++ less/pty.less | 2 ++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/css/index.css b/css/index.css index 8048528..136492e 100644 --- a/css/index.css +++ b/css/index.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4;cursor:default}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270;cursor:move}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file diff --git a/css/pty.css b/css/pty.css index 75f7209..c036bf0 100644 --- a/css/pty.css +++ b/css/pty.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4;cursor:default}.network-chart .node-center{fill:#4ecdc4;stroke:#556270;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#556270;stroke-width:2;stroke-opacity:.5}.network-chart text.node-label{fill:#333;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .url-container{fill:#000}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart rect.background{fill:#ddd;cursor:move}.network-chart .brand text{fill:#777;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file diff --git a/less/index.less b/less/index.less index 769f334..d7caf38 100644 --- a/less/index.less +++ b/less/index.less @@ -18,6 +18,7 @@ .node-highlight{ fill: @aqua-light; + cursor: default; } .node-center { @@ -51,6 +52,7 @@ // Background rect.background { fill: @slate-dark; + cursor: move; } // Node Link diff --git a/less/pty.less b/less/pty.less index a8edb47..b1ebd94 100644 --- a/less/pty.less +++ b/less/pty.less @@ -18,6 +18,7 @@ .node-highlight{ fill: @aqua-light; + cursor: default; } .node-center { @@ -67,6 +68,7 @@ // Background rect.background { fill: @grey-light; + cursor: move; } From 254573f024dd4269711b8ee2d2c3dcc5f2a6ee02 Mon Sep 17 00:00:00 2001 From: gregorio Date: Sun, 2 Mar 2014 21:48:13 -0300 Subject: [PATCH 28/33] Added a preliminary legend. --- css/index.css | 2 +- docs.md | 18 +++++++++++---- less/index.less | 20 +++++++++++++++++ less/pty.less | 8 +++++++ src/pty.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 101 insertions(+), 7 deletions(-) diff --git a/css/index.css b/css/index.css index 136492e..470b27f 100644 --- a/css/index.css +++ b/css/index.css @@ -1 +1 @@ -.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4;cursor:default}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270;cursor:move}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file +.network-chart .node{fill:#c7f464}.network-chart .node-highlight{fill:#4ecdc4;cursor:default}.network-chart .node-center{fill:#4ecdc4;stroke:#ddd;stroke-width:3}.network-chart .node-clickable{cursor:pointer;stroke:#4ecdc4;stroke-width:3;stroke-opacity:1}.network-chart text.node-label{fill:#ddd;text-anchor:start;font-family:sans-serif;font-size:12px}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart .link{stroke:#4ecdc4;stroke-opacity:.5;stroke-width:2}.network-chart rect.background{fill:#556270;cursor:move}.network-chart .url-container{fill:#c7f464}.network-chart .url-container a{text-decoration:none}.network-chart .url-container .url-container-icon{font-family:FontAwesome}.network-chart .brand text{fill:#ddd;font-family:Roboto,sans-serif;font-size:11px}.network-chart .control-item rect{fill:#fff;fill-opacity:.75}.network-chart .control-item text{fill:#556270}.network-chart .control-highlight{cursor:pointer} \ No newline at end of file diff --git a/docs.md b/docs.md index 25d0e45..795a6a3 100644 --- a/docs.md +++ b/docs.md @@ -170,14 +170,19 @@ Set the styles for circles of class `persona` and `institucion`. .network-chart circle.institucion { fill: #556270; } + +.network-chart circle.candidato { + fill: white; +} {% endhighlight %} -Set the function to determine the node class using the attributes of each node element. +Set the function to determine the node class using the attributes of each node element. Additionally, set `.placelegend(true)` to show the legend. {% highlight javascript %} // Create a chart and set the class for the nodes var chart = pty.chart.network() - .nodeClass(function(d) { return d.type; }); + .nodeClass(function(d) { return d.type; }) + .placelegend(true); d3.select('div#example04') .data([data]) @@ -193,17 +198,22 @@ d3.select('div#example04') .network-chart circle.institucion { fill: #556270; } + + .network-chart circle.candidato { + fill: white; + }
+

Adding a Legend

+ +To add a legend, the user has to provide a list of all the node types as follows and submit the list to `.legendItems()`. + +{% highlight javascript %} + var legend = [ + {name: 'Persona', type: 'persona'}, + {name: 'Candidato', type: 'candidato'}, + {name: 'Institución', type: 'institucion'} + ]; + + var chart01 = pty.chart.network() + .legendItems(legend); +{% endhighlight %} + +The style of the circles representing each node type in the legend has to be set separately form the style for the nodes in the graph. This allows for instance to draw a stroke around the legend circles in order to differentiate them from the background without altering the style of the nodes of the graph. + +{% highlight javascript %} +// Legend + .legend { + + .persona { + fill: #75507b; + stroke: @grey-light; + stroke-width: 1; + } + + .candidato { + fill: #729fcf; + stroke: @grey-light; + stroke-width: 1; + } + + .institucion { + fill: #8ae234; + stroke: @grey-light; + stroke-width: 1; + } + + text { + font-size: 11px; + fill: @grey-light; + } + } +{% endhighlight %} + +
+ +
+ +
+ +

Adding Labels

@@ -233,16 +324,23 @@ d3.select('div#example05') .call(chart); {% endhighlight %} -
+
@@ -306,7 +404,7 @@ d3.select('div#chart') .call(chart); {% endhighlight %} -
+
From a0df12896726f74b7fc822b12e6f3b6618126926 Mon Sep 17 00:00:00 2001 From: gregorio Date: Mon, 3 Mar 2014 17:26:22 -0300 Subject: [PATCH 31/33] Corrected a missplaced example. Added pages/fullscreen-demo2.md showing all the functions (except embed) --- docs.md | 13 +++-- example.html | 114 ++++++++++++++++++++++++++++++++++++++ pages/fullscreen-demo2.md | 109 ++++++++++++++++++++++++++++++++++++ 3 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 example.html create mode 100644 pages/fullscreen-demo2.md diff --git a/docs.md b/docs.md index 0754ec2..fca21f5 100644 --- a/docs.md +++ b/docs.md @@ -300,6 +300,13 @@ The style of the circles representing each node type in the legend has to be set
+ \ No newline at end of file diff --git a/pages/fullscreen-demo2.md b/pages/fullscreen-demo2.md new file mode 100644 index 0000000..78018f3 --- /dev/null +++ b/pages/fullscreen-demo2.md @@ -0,0 +1,109 @@ +--- +layout: main +title: Fullscreen Demo +--- + + + + + + + + +# {{ page.title }} + +
+ +
+ + +
+
+
+
+
+ +
+ +
+ + + +
+ From c674604f3fd64c9d4d4e94dda781fc00ac9eee21 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Mon, 3 Mar 2014 17:52:59 -0300 Subject: [PATCH 32/33] Remove comments --- pages/fullscreen-demo2.md | 109 -------------------------------------- src/pty.js | 66 ----------------------- 2 files changed, 175 deletions(-) delete mode 100644 pages/fullscreen-demo2.md diff --git a/pages/fullscreen-demo2.md b/pages/fullscreen-demo2.md deleted file mode 100644 index 78018f3..0000000 --- a/pages/fullscreen-demo2.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -layout: main -title: Fullscreen Demo ---- - - - - - - - - -# {{ page.title }} - -
- -
- - -
-
-
-
-
- -
- -
- - - -
- diff --git a/src/pty.js b/src/pty.js index 2400f9f..4090046 100644 --- a/src/pty.js +++ b/src/pty.js @@ -164,70 +164,6 @@ pty.chart.network = function() { // Graphic Elements // ---------------- - //Legend - //------ - - // if (me.legendItems) { - - // var gLegendItems = gLegend.selectAll('g.legend-item').data(me.legendItems); - - // var legendScale = d3.scale.ordinal() - // .domain() - // .rangePoints([]) - - - - - // } - - // if (me.placelegend){ - - // var legendContainer = svgEnter.selectAll('.legend'); - - // legendContainer.attr('class','network-chart'); - - // legendContainer.append('circle') - // .attr('class','node') - // .classed('node',true) - // .attr('class','persona') - // .attr('r',10); - - // legendContainer.append('text') - // .attr('text-anchor','start') - // .attr('fill','black') - // .attr('x',15) - // .attr('y',5) - // .text('Persona'); - - // legendContainer.append('circle') - // .attr('class','node') - // .classed('node',true) - // .attr('class','candidato') - // .attr('cx',120) - // .attr('r',10); - - // legendContainer.append('text') - // .attr('text-anchor','start') - // .attr('fill','black') - // .attr('x',135) - // .attr('y',5) - // .text('Candidato'); - - // legendContainer.append('circle') - // .attr('class','node') - // .classed('node',true) - // .attr('class','institucion') - // .attr('cx',240) - // .attr('r',10); - - // legendContainer.append('text') - // .attr('text-anchor','start') - // .attr('fill','black') - // .attr('x',255) - // .attr('y',5) - // .text('Institución'); - // } - // Links // ----- @@ -375,9 +311,7 @@ pty.chart.network = function() { .on('mouseover', function(d) { d3.select(this).classed('control-highlight','true'); }) .on('mouseout', function(d) { d3.select(this).classed('control-highlight','false'); }) .on('click', function() { - console.log(isScreenFull); isScreenFull = !isScreenFull; - console.log(isScreenFull); d3.select(this).call(me.fullscreenCallback); }); } else { From 88dc8c1030875cec50e60323b8f64584309a7fc2 Mon Sep 17 00:00:00 2001 From: Pablo Navarro Castillo Date: Mon, 3 Mar 2014 17:55:41 -0300 Subject: [PATCH 33/33] Fix the font-awesome link --- bower.json | 2 +- docs.md | 3 ++- package.json | 2 +- src/pty.js | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bower.json b/bower.json index 417b547..a7be23a 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "panama-network", - "version": "0.1.0", + "version": "0.2.0", "authors": [ "gregorio " ], diff --git a/docs.md b/docs.md index fca21f5..0f66561 100644 --- a/docs.md +++ b/docs.md @@ -3,10 +3,11 @@ layout: main title: Documentation --- + - + # Network Chart diff --git a/package.json b/package.json index 9984eea..2bb4268 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "panama-network", - "version": "0.1.0", + "version": "0.2.0", "description": "Visualization of influence networks for candidates in Panama", "main": "Gruntfile.js", "repository": { diff --git a/src/pty.js b/src/pty.js index 4090046..66bbc46 100644 --- a/src/pty.js +++ b/src/pty.js @@ -1,7 +1,7 @@ /* globals d3:false */ var pty = { - version: '0.1.0' // semver + version: '0.2.0' // semver }; // SVG Transformations