Skip to content

Instantly share code, notes, and snippets.

@infographicstw
Last active December 22, 2015 12:02
Show Gist options
  • Save infographicstw/122779b04901c4ffa455 to your computer and use it in GitHub Desktop.
Save infographicstw/122779b04901c4ffa455 to your computer and use it in GitHub Desktop.
Force Layout Examples
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="1.js"></script></head><body><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="1.js")
body
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var t,n,e,r;return t=function(){return r.attr({cx:function(t){return t.x},cy:function(t){return t.y},r:5})},n=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100].map(function(t){return{idx:t}}),e=d3.layout.force().size([800,400]),e.nodes(n).on("tick",t).start(),r=d3.select("svg").selectAll("circle").data(n).enter().append("circle")});
<- $(document).ready
tick = ->
circles.attr do
cx: ->
it.x
cy: -> it.y
r: 5
nodes = [1 to 100].map(->{idx: it})
force = d3.layout.force!size [800 400]
force.nodes(nodes).on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="2.js"></script></head><body><div style="position:absolute;top:5px;left:50%;margin-left:-40px" onclick="reset()" class="btn btn-default">重新執行</div><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="2.js")
body
.btn.btn-default(style="position:absolute;top:5px;left:50%;margin-left:-40px",onclick="reset()") 重新執行
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var r,n,t,e,a,o,c,u;for(r=d3.scale.linear().domain([2,12]).range(["#090","#f00"]),n=function(){return a.attr({cx:function(r){return r.x},cy:function(r){return r.y},r:function(r){return r.r},fill:function(n){return r(n.r)}})},t=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50].map(function(r){return{idx:r,r:parseInt(10*Math.random())+2}}),e=d3.layout.force().size([800,400]).charge(function(r){return 5*-r.r}),e.nodes(t).on("tick",n).start(),a=d3.select("svg").selectAll("circle").data(t).enter().append("circle"),o=0,c=t.length;c>o;++o)u=t[o],u.x=u.px=400*Math.random()+200,u.y=u.py=300*Math.random()+50;return n(),e.stop(),window.reset=function(){var r,n,a,o;for(e.start(),r=0,a=(n=t).length;a>r;++r)o=n[r],o.x=o.px=400*Math.random()+200,o.y=o.py=300*Math.random()+50;return e.alpha(1)}});
<- $(document).ready
color = d3.scale.linear!domain [2 12] .range <[#090 #f00]>
tick = ->
circles.attr do
cx: ->
it.x
cy: -> it.y
r: -> it.r
fill: -> color it.r
nodes = [1 to 50].map -> do
idx: it
r: parseInt(Math.random!*10) + 2
force = d3.layout.force!size [800 400] .charge(->-it.r * 5)
force.nodes(nodes).on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
tick!
force.stop!
window.reset = ->
force.start!
for n in nodes =>
n.x = n.px = Math.random! * 400 + 200
n.y = n.py = Math.random! * 300 + 50
force.alpha 1
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="3.js"></script></head><body><div style="position:absolute;top:5px;left:50%;margin-left:-40px" onclick="reset()" class="btn btn-default">重新執行</div><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="3.js")
body
.btn.btn-default(style="position:absolute;top:5px;left:50%;margin-left:-40px",onclick="reset()") 重新執行
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var r,n,t,a,e,o,y,c,u;for(r=50,n=d3.scale.linear().domain([2,12]).range(["#090","#f00"]),t=function(){var r,t,y,c;for(r=0,y=(t=a).length;y>r;++r)c=t[r],c.vy+=c.ay,c.y2+=c.vy,c.y2+c.r>=300&&(c.y2=300-c.r,c.vy=-1*Math.abs(c.vy),c.y=c.y2),c.vy=.95*c.vy;return o.attr({cx:function(r){return r.x},cy:function(r){return r.y2},r:function(r){return r.r},fill:function(r){return n(r.r)}}),e.alpha(.01)},a=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30].map(function(r){return{idx:r,r:parseInt(10*Math.random())+2,vy:0,ay:1.2,y2:300*Math.random()}}),e=d3.layout.force().size([800,400]).charge(function(n){return-n.r*r}).alpha(.01),e.nodes(a).on("tick",t).start(),o=d3.select("svg").selectAll("circle").data(a).enter().append("circle"),y=0,c=a.length;c>y;++y)u=a[y],u.x=u.px=400*Math.random()+200,u.y=u.py=300*Math.random()+50;return t(),e.stop(),window.reset=function(){var r,n,t,o,y=[];for(e.start(),r=0,t=(n=a).length;t>r;++r)o=n[r],o.x=o.px=400*Math.random()+200,y.push(o.y2=o.y=o.py=300*Math.random()+50);return y}});
<- $(document).ready
rate = 50
color = d3.scale.linear!domain [2 12] .range <[#090 #f00]>
tick = ->
for n in nodes =>
n.vy += n.ay
n.y2 += n.vy
if n.y2 + n.r >= 300 =>
n.y2 = 300 - n.r
n.vy = -1 * Math.abs(n.vy)
n.y = n.y2
n.vy = n.vy * 0.95
circles.attr do
cx: -> it.x
cy: -> it.y2
r: -> it.r
fill: -> color it.r
force.alpha 0.01
nodes = [1 to 30].map -> do
idx: it
r: parseInt(Math.random!*10) + 2
vy: 0
ay: 1.2
y2: Math.random!*300
force = d3.layout.force!size [800 400] .charge(->-it.r * rate).alpha 0.01
force.nodes(nodes).on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
tick!
force.stop!
window.reset = ->
force.start!
for n in nodes =>
n.x = n.px = Math.random!*400 + 200
n.y2 = n.y = n.py = Math.random!*300 + 50
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="4.js"></script></head><body><div style="position:absolute;top:5px;left:50%;margin-left:-40px" onclick="reset()" class="btn btn-default">重新執行</div><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="4.js")
body
.btn.btn-default(style="position:absolute;top:5px;left:50%;margin-left:-40px",onclick="reset()") 重新執行
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var r,t,n,a,e,o,c,u;for(r=d3.scale.linear().domain([2,12]).range(["#090","#f00"]),t=function(){var t,o,c,u,i;for(t=0,c=(o=n).length;c>t;++t)u=o[t],i=Math.sqrt(Math.pow(u.y-200,2)+Math.pow(u.x-400,2)),110>i&&0!==i?(u.x+=1.1*(u.x-400)/i,u.y+=1.1*(u.y-200)/i):i>140&&0!==i&&(u.x-=1.1*(u.x-400)/i,u.y-=1.1*(u.y-200)/i);return e.attr({cx:function(r){return r.x},cy:function(r){return r.y},r:function(r){return r.r},fill:function(t){return r(t.r)}}),a.alpha(.01)},n=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30].map(function(r){return{idx:r,r:parseInt(10*Math.random())+2,vy:0,ay:.3}}),a=d3.layout.force().size([800,400]).charge(function(r){return 30*-r.r}).alpha(.01),a.nodes(n).on("tick",t).start(),e=d3.select("svg").selectAll("circle").data(n).enter().append("circle"),o=0,c=n.length;c>o;++o)u=n[o],u.x=u.px=400*Math.random()+200,u.y=u.py=300*Math.random()+50;return t(),a.stop(),window.reset=function(){var r,t,e,o,c=[];for(a.start(),r=0,e=(t=n).length;e>r;++r)o=t[r],o.x=o.px=400*Math.random()+200,c.push(o.y=o.py=300*Math.random()+50);return c}});
<- $(document).ready
color = d3.scale.linear!domain [2 12] .range <[#090 #f00]>
tick = ->
for n in nodes =>
r = Math.sqrt((n.y - 200)**2 + (n.x - 400)**2)
if r < 110 and r!=0 =>
n.x += 1.1 * (n.x - 400) / r
n.y += 1.1 * (n.y - 200) / r
else if r > 140 and r!=0 =>
n.x -= 1.1 * (n.x - 400) / r
n.y -= 1.1 * (n.y - 200) / r
/*if ( n.y - 200 )+ n.r >= 300 =>
n.y = 300 - n.r
n.vy = -1 * Math.abs(n.vy)
n.vy = n.vy * 0.9*/
circles.attr do
cx: -> it.x
cy: -> it.y
r: -> it.r
fill: -> color it.r
force.alpha 0.01
nodes = [1 to 30].map -> do
idx: it
r: parseInt(Math.random!*10) + 2
vy: 0
ay: 0.3
force = d3.layout.force!size [800 400] .charge(->-it.r * 30).alpha 0.01
force.nodes(nodes) .on(\tick, tick) .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
tick!
force.stop!
window.reset = ->
force.start!
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="5.js"></script></head><body><div style="position:absolute;top:5px;left:50%;margin-left:-40px" onclick="reset()" class="btn btn-default">重新執行</div><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="5.js")
body
.btn.btn-default(style="position:absolute;top:5px;left:50%;margin-left:-40px",onclick="reset()") 重新執行
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var r,t,a,n,e,o,c,h,u;for(r=.01,t=d3.scale.linear().domain([2,12]).range(["#090","#f00"]),a=function(){var a,c,h,u,d,i,f,s,l,M,y,p,m,x,g;for(a=0,h=(c=n).length;h>a;++a){for(u=c[a],d=[1e6,0,0],i=d[0],f=d[1],s=d[2],l=-1;1>=l;++l)for(M=l,y=-1;1>=y;++y)p=y,m=r*M+u.x,x=r*p+u.y,m>590&&(m=590),210>m&&(m=210),g=150>x?50*-Math.sqrt(1-Math.pow(Math.abs((m-400)/100)-1,2))+150:3*Math.sqrt(1-Math.sqrt(Math.abs((m-400)/100)/2))*50+150,g=Math.abs(x-g),i>g&&(d=[g,m,x],i=d[0],f=d[1],s=d[2]);d=[f,s],u.x=d[0],u.y=d[1],.5>r&&(r*=1.0005)}return o.attr({cx:function(r){return r.x},cy:function(r){return r.y},r:function(r){return r.r},fill:function(r){return t(r.r)}}),e.alpha(.01)},n=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60].map(function(r){return{idx:r,r:parseInt(10*Math.random())+2,vy:0,ay:.3,x:400*Math.random()+200,y:300*Math.random()+50}}),e=d3.layout.force().size([800,400]).charge(function(r){return 20*-r.r}).alpha(.01),e.nodes(n).on("tick",a).start(),o=d3.select("svg").selectAll("circle").data(n).enter().append("circle"),c=0,h=n.length;h>c;++c)u=n[c],u.x=u.px=400*Math.random()+200,u.y=u.py=300*Math.random()+50;return a(),e.stop(),window.reset=function(){var t,a,o,c;for(e.start(),t=0,o=(a=n).length;o>t;++t)c=a[t],c.x=c.px=400*Math.random()+200,c.y=c.py=300*Math.random()+50;return r=.01}});
<- $(document).ready
rate = 0.01
color = d3.scale.linear!domain [2 12] .range <[#090 #f00]>
tick = ->
for n in nodes =>
[mv,mx,my] = [1000000, 0, 0]
for dx from -1 to 1
for dy from -1 to 1
nx = rate * dx + n.x
ny = rate * dy + n.y
if nx > 590 => nx = 590
if nx < 210 => nx = 210
if ny < 150 =>
v = -Math.sqrt(1 - (Math.abs((nx - 400)/100) - 1)**2) * 50 + 150
else
v = 3 * Math.sqrt(1 - (Math.sqrt(Math.abs((nx - 400)/100)/2))) * 50 + 150
v = Math.abs(ny - v)
if v < mv => [mv,mx,my] = [v,nx,ny]
[n.x, n.y] = [mx, my]
if rate < 0.5 => rate *= 1.0005
circles.attr do
cx: ->
it.x
cy: -> it.y
r: -> it.r
fill: -> color it.r
force.alpha 0.01
nodes = [1 to 60].map -> do
idx: it
r: parseInt(Math.random!*10) + 2
vy: 0
ay: 0.3
x: Math.random! * 400 + 200
y: Math.random! * 300 + 50
force = d3.layout.force!size [800 400] .charge(->-it.r * 20).alpha 0.01
force.nodes(nodes).on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
tick!
force.stop!
window.reset = ->
force.start!
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
rate := 0.01
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="spline.js"></script><script type="text/javascript" src="6.js"></script></head><body><div style="position:absolute;top:5px;left:50%;margin-left:-40px" onclick="reset()" class="btn btn-default">重新執行</div><svg width="100%" height="300px" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="spline.js")
script(type="text/javascript",src="6.js")
body
.btn.btn-default(style="position:absolute;top:5px;left:50%;margin-left:-40px",onclick="reset()") 重新執行
svg(width="100%",height="300px",viewBox="0 0 800 400",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var t,n,r,e,a,o,c,u,i,d;for(t=d3.scale.linear().domain([2,12]).range(["#090","#f00"]),n=function(){return spline.update(r,e),o.attr({cx:function(t){return t.x},cy:function(t){return t.y},r:function(t){return t.r},fill:function(n){return t(n.r)}}),c.attr({d:function(t){return t.d},fill:"none",stroke:"#444"}),a.alpha(.01)},r=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59].map(function(t){return{idx:t,r:parseInt(10*Math.random())+2,x:400*Math.random()+200,y:300*Math.random()+50}}),e=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58].map(function(t){return{source:r[t],target:r[t+1]}}),a=d3.layout.force().size([800,400]).charge(function(t){return 10*-t.r}).alpha(.01).gravity(.1),a.nodes(r).links(e).on("tick",n).start(),o=d3.select("svg").selectAll("circle").data(r).enter().append("circle"),c=d3.select("svg").selectAll("path").data(e).enter().append("path").each(function(t){return t.node=this}),u=0,i=r.length;i>u;++u)d=r[u],d.x=d.px=400*Math.random()+200,d.y=d.py=300*Math.random()+50;return n(),a.stop(),window.reset=function(){var t,n,e,o,c=[];for(a.start(),t=0,e=(n=r).length;e>t;++t)o=n[t],o.x=o.px=400*Math.random()+200,c.push(o.y=o.py=300*Math.random()+50);return c}});
<- $(document).ready
color = d3.scale.linear!domain [2 12] .range <[#090 #f00]>
tick = ->
spline.update nodes, links
circles.attr do
cx: -> it.x
cy: -> it.y
r: -> it.r
fill: -> color it.r
paths.attr do
d: -> it.d
fill: \none
stroke: \#444
force.alpha 0.01
nodes = [0 til 60].map -> do
idx: it
r: parseInt(Math.random!*10) + 2
x: Math.random! * 400 + 200
y: Math.random! * 300 + 50
links = [0 til 59].map -> {source: nodes[it], target: nodes[it + 1]}
force = d3.layout.force!size [800 400] .charge(->-it.r * 10).alpha 0.01 .gravity 0.1
force.nodes(nodes) .links(links) .on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
paths = d3.select \svg .selectAll \path .data links .enter!append \path .each -> it.node = @
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
tick!
force.stop!
window.reset = ->
force.start!
for n in nodes =>
n.x = n.px = Math.random!* 400 + 200
n.y = n.py = Math.random!* 300 + 50
keyframes(name)
width: 100%
@-webkit-keyframes {name}
{block}
@-webkit-keyframes {name}
{block}
@-moz-keyframes {name}
{block}
@-ms-keyframes {name}
{block}
@keyframes {name}
{block}
transition()
-ms-transition arguments
-moz-transition arguments
-webkit-transition arguments
-o-transition arguments
transition arguments
transform()
-ms-transform arguments
-moz-transform arguments
-webkit-transform arguments
-o-transform arguments
transform arguments
animation()
-ms-animation arguments
-moz-animation arguments
-webkit-animation arguments
-o-animation arguments
animation arguments
animation-delay()
-ms-animation-delay arguments
-moz-animation-delay arguments
-webkit-animation-delay arguments
-o-animation-delay arguments
animation-delay arguments
no-selection()
-webkit-touch-callout: none
-webkit-user-select: none
-khtml-user-select: none
-moz-user-select: none
-ms-user-select: none
user-select: none
ellipsis()
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
<!DOCTYPE html><html lang="en"><head prefix="og: http://ogp.me/ns#"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>template</title><!-- libraries--><link rel="icon" type="image/x-icon" href="thumbnail.png"><script type="text/javascript" src="//codeorigin.jquery.com/jquery-1.10.2.min.js"></script><script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script><script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js"></script><link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css"><script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script><!-- custom files--><link rel="stylesheet" type="text/css" href="index.css"><script type="text/javascript" src="index.js"></script></head><body><svg width="800px" height="600px" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid"></svg></body></html>
doctype html
//- basic libraries: jquery(1.10.2), d3js(3.3.11), angularjs(1.2.3), bootstrap(3.0)/semantic-ui(0.9.3/0.9.5)
- var use = { cdn: true, og: false, favicon: true }
- var lib = { jquery: true, d3js: true, angular: true, bootstrap: true, semantic: false }
- var assets = "assets"
- var thumbnail = "thumbnail.png"
- var favicon = "thumbnail.png"
html(lang="en")
head(prefix="og: http://ogp.me/ns#")
meta(charset="utf-8")
meta(name='viewport', content='width=device-width, initial-scale=1.0')
title template
// libraries
include library.jade
// custom files
link(rel="stylesheet",type="text/css",href="index.css")
script(type="text/javascript",src="index.js")
body
svg(width="800px",height="600px",viewBox="0 0 800 600",preserveAspectRatio="xMidYMid")
$(document).ready(function(){var t,n,e,r;return t=function(){return r.attr({cx:function(t){return t.x},cy:function(t){return t.y},r:5})},n=[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100].map(function(t){return{idx:t}}),e=d3.layout.force().size([800,600]),e.nodes(n).on("tick",t).start(),r=d3.select("svg").selectAll("circle").data(n).enter().append("circle")});
<- $(document).ready
tick = ->
circles.attr do
cx: ->
it.x
cy: -> it.y
r: 5
nodes = [1 to 100].map(->{idx: it})
force = d3.layout.force!size [800 600]
force.nodes(nodes).on \tick tick .start!
circles = d3.select \svg .selectAll \circle .data nodes .enter!append \circle
@import vars
@import basic
html,body
width: 100%
height: 100%
margin: 0
padding: 0
if use.og
meta(property="og:locale",content="zh_TW")
meta(property="og:image",content="#{thumbnail}")
meta(property="og:image:type",content="image/png")
meta(property="og:image:width",content="200")
meta(property="og:image:height",content="200")
if use.favicon
link(rel="icon",type="image/x-icon",href="#{favicon}")
if use.cdn
//- from cdn
if lib.jquery
script(type="text/javascript",src="//codeorigin.jquery.com/jquery-1.10.2.min.js")
if lib.d3js
script(type="text/javascript",src="//d3js.org/d3.v3.min.js")
if lib.angular
script(type="text/javascript",src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.3/angular.min.js")
if lib.bootstrap
link(rel="stylesheet",type="text/css",href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css")
script(type="text/javascript",src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js")
if lib.semantic
link(rel="stylesheet",type="text/css",href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/0.9.3/css/semantic.min.css")
script(type="Text/javascript",src="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/0.9.3/javascript/semantic.min.js")
else
//- from local
if lib.jquery
script(type="text/javascript",src="#{assets}/jquery/1.10.2/jquery.min.js")
if lib.d3js
script(type="text/javascript",src="#{assets}/d3js/3.3.11/d3.v3.min.js")
if lib.angular
script(type="text/javascript",src="#{assets}/angular/1.2.3/angular.min.js")
if lib.bootstrap
link(rel="stylesheet",type="text/css",href="#{assets}/bootstrap/3.0.2/css/bootstrap.min.css")
script(type="text/javascript",src="#{assets}/bootstrap/3.0.2/js/bootstrap.min.js")
if lib.semantic
link(rel="stylesheet",type="text/css",href="#{assets}/semantic-ui/0.9.5/css/semantic.min.css")
script(type="text/javascript",src="#{assets}/semantic-ui/0.9.5/js/semantic.min.js")
require! <[chokidar http fs path jade stylus]>
require! 'uglify-js': uglify, LiveScript: lsc
RegExp.escape = -> it.replace /[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"
cwd = path.resolve process.cwd!
cwd-re = new RegExp RegExp.escape "#cwd#{if cwd[* - 1]=='/' => "" else \/}"
pad = -> if "#it".length < 2 => "0#it" else "#it"
now = -> new Date! |> ->
"[#{pad(it.getMonth! + 1)}/#{pad(it.getDate!)}" +
" #{pad(it.getHours!)}:#{pad(it.getMinutes!)}:#{pad(it.getSeconds!)}]"
_log = console.log
console.log = (...arg) -> _log.apply null, [now!] ++ arg
ignore-list = [/^server.ls$/, /^library.jade$/, /^\.[^/]+/, /^node_modules\//,/^assets\//]
ignore-func = (f) -> if f => ignore-list.filter(-> it.exec f.replace(cwd-re, "")replace(/^\.\/+/, ""))length else 0
type-table =
"3gp":"video/3gpp",
"aiff":"audio/x-aiff",
"arj":"application/x-arj-compressed",
"asf":"video/x-ms-asf",
"asx":"video/x-ms-asx",
"au":"audio/ulaw",
"avi":"video/x-msvideo",
"bcpio":"application/x-bcpio",
"ccad":"application/clariscad",
"cod":"application/vnd.rim.cod",
"com":"application/x-msdos-program",
"cpio":"application/x-cpio",
"cpt":"application/mac-compactpro",
"csh":"application/x-csh",
"css":"text/css",
"deb":"application/x-debian-package",
"dl":"video/dl",
"doc":"application/msword",
"drw":"application/drafting",
"dvi":"application/x-dvi",
"dwg":"application/acad",
"dxf":"application/dxf",
"dxr":"application/x-director",
"etx":"text/x-setext",
"ez":"application/andrew-inset",
"fli":"video/x-fli",
"flv":"video/x-flv",
"gif":"image/gif",
"gl":"video/gl",
"gtar":"application/x-gtar",
"gz":"application/x-gzip",
"hdf":"application/x-hdf",
"hqx":"application/mac-binhex40",
"html":"text/html",
"ice":"x-conference/x-cooltalk",
"ico":"image/x-icon",
"ief":"image/ief",
"igs":"model/iges",
"ips":"application/x-ipscript",
"ipx":"application/x-ipix",
"jad":"text/vnd.sun.j2me.app-descriptor",
"jar":"application/java-archive",
"jpeg":"image/jpeg",
"jpg":"image/jpeg",
"js":"text/javascript",
"json":"application/json",
"latex":"application/x-latex",
"lsp":"application/x-lisp",
"lzh":"application/octet-stream",
"m":"text/plain",
"m3u":"audio/x-mpegurl",
"m4v":"video/mp4",
"man":"application/x-troff-man",
"me":"application/x-troff-me",
"midi":"audio/midi",
"mif":"application/x-mif",
"mime":"www/mime",
"mkv":" video/x-matrosk",
"movie":"video/x-sgi-movie",
"mp4":"video/mp4",
"mp41":"video/mp4",
"mp42":"video/mp4",
"mpg":"video/mpeg",
"mpga":"audio/mpeg",
"ms":"application/x-troff-ms",
"mustache":"text/plain",
"nc":"application/x-netcdf",
"oda":"application/oda",
"ogm":"application/ogg",
"pbm":"image/x-portable-bitmap",
"pdf":"application/pdf",
"pgm":"image/x-portable-graymap",
"pgn":"application/x-chess-pgn",
"pgp":"application/pgp",
"pm":"application/x-perl",
"png":"image/png",
"pnm":"image/x-portable-anymap",
"ppm":"image/x-portable-pixmap",
"ppz":"application/vnd.ms-powerpoint",
"pre":"application/x-freelance",
"prt":"application/pro_eng",
"ps":"application/postscript",
"qt":"video/quicktime",
"ra":"audio/x-realaudio",
"rar":"application/x-rar-compressed",
"ras":"image/x-cmu-raster",
"rgb":"image/x-rgb",
"rm":"audio/x-pn-realaudio",
"rpm":"audio/x-pn-realaudio-plugin",
"rtf":"text/rtf",
"rtx":"text/richtext",
"scm":"application/x-lotusscreencam",
"set":"application/set",
"sgml":"text/sgml",
"sh":"application/x-sh",
"shar":"application/x-shar",
"silo":"model/mesh",
"sit":"application/x-stuffit",
"skt":"application/x-koan",
"smil":"application/smil",
"snd":"audio/basic",
"sol":"application/solids",
"spl":"application/x-futuresplash",
"src":"application/x-wais-source",
"stl":"application/SLA",
"stp":"application/STEP",
"sv4cpio":"application/x-sv4cpio",
"sv4crc":"application/x-sv4crc",
"svg":"image/svg+xml",
"swf":"application/x-shockwave-flash",
"tar":"application/x-tar",
"tcl":"application/x-tcl",
"tex":"application/x-tex",
"texinfo":"application/x-texinfo",
"tgz":"application/x-tar-gz",
"tiff":"image/tiff",
"tr":"application/x-troff",
"tsi":"audio/TSP-audio",
"tsp":"application/dsptype",
"tsv":"text/tab-separated-values",
"unv":"application/i-deas",
"ustar":"application/x-ustar",
"vcd":"application/x-cdlink",
"vda":"application/vda",
"vivo":"video/vnd.vivo",
"vrm":"x-world/x-vrml",
"wav":"audio/x-wav",
"wax":"audio/x-ms-wax",
"webm":"video/webm",
"wma":"audio/x-ms-wma",
"wmv":"video/x-ms-wmv",
"wmx":"video/x-ms-wmx",
"wrl":"model/vrml",
"wvx":"video/x-ms-wvx",
"xbm":"image/x-xbitmap",
"xlw":"application/vnd.ms-excel",
"xml":"text/xml",
"xpm":"image/x-xpixmap",
"xwd":"image/x-xwindowdump",
"xyz":"chemical/x-pdb",
"zip":"application/zip"
watch-path = \.
mkdir-recurse = (f) ->
if fs.exists-sync f => return
parent = path.dirname(f)
if !fs.exists-sync parent => mkdir-recurse parent
fs.mkdir-sync f
styl-tree = do
down-hash: {}
up-hash: {}
parse: (filename) ->
dir = path.dirname(filename)
ret = fs.read-file-sync filename .toString!split \\n .map(-> /^ *@import (.+)/.exec it)filter(->it)map(->it.1)
ret = ret.map -> path.join(dir, it.replace(/(\.styl)?$/, ".styl"))
@down-hash[filename] = ret
for it in ret => if not (filename in @up-hash.[][it]) => @up-hash.[][it].push filename
find-root: (filename) ->
work = [filename]
ret = []
while work.length > 0
f = work.pop!
if @up-hash.[][f].length == 0 => ret.push f
else work ++= @up-hash[f]
ret
ctype = (name=null) ->
ret = /\.([^.]+)$/.exec name
return \application/octet-stream if not ret or not ret.1 or not type-table[ret.1]
return type-table[ret.1]
ftype = ->
switch
| /\.ls$/.exec it => "ls"
| /\.styl/.exec it => "styl"
| /\.jade$/.exec it => "jade"
| otherwise => "other"
# assign functions to route-table for server side script routing
sample-cgi = (req, res) ->
res.writeHead 200, {"Content-type": "text/html"}
res.end "hello world!"
route-table = {"/sample-cgi": sample-cgi}
server = (req, res) ->
req.url = req.url - /[?#].*$/
file-path = path.resolve cwd, ".#{req.url}"
if file-path.indexOf(cwd) < 0 =>
res.writeHead 403, ctype!
return res.end "#{req.url} forbidden"
# custom server side script
rel-path = file-path.replace cwd, ""
if rel-path of route-table => return route-table[rel-path] req, res
# directory: give index.html, or generate a list of files
if fs.existsSync(file-path) and fs.lstatSync(file-path)isDirectory! =>
dir = file-path.replace /\/$/,""
file-path = "#{file-path}/index.html"
if not fs.existsSync(file-path) =>
files = fs.readdirSync dir
dir = req.url.replace /\/$/,""
res.writeHead 200, {"Content-type": \text/html}
res.write "<h2>#{dir}<h2>\n<ul>\n"
for it in files => res.write "<li><a href='#{dir}/#{it}'>#{it}</a></li>\n"
return res.end \</ul>\n
# file not exists: 404
if not fs.existsSync(file-path) =>
res.writeHead 404, ctype!
return res.end "#{req.url} not found"
console.log "[ GET ] #{file-path} (#{ctype file-path})"
buf = fs.readFileSync file-path
res.writeHead 200, do
"Content-Length": buf.length
"Content-Type": ctype file-path
res.end buf
log = (error, stdout, stderr) -> if "#{stdout}\n#{stderr}".trim! => console.log that
filecache = {}
update-file = ->
if !it or /node_modules|\.swp$/.exec(it)=> return
src = if it.0 != \/ => path.join(cwd,it) else it
src = src.replace path.join(cwd,\/), ""
[type,cmd,des] = [ftype(src), "",""]
if type == \other => return
if type == \ls =>
if /src\/ls/.exec src =>
try
files = fs.readdir-sync \src/ls/ .map -> "src/ls/#it"
files = files.filter -> (/\/\./.exec it) == null
result = [uglify.minify(lsc.compile(fs.read-file-sync(file)toString!,{bare:true}),{fromString:true}).code for file in files].join("")
fs.write-file-sync "build.min.js", result
console.log "[BUILD] #src --> build.min.js"
catch
console.log "[BUILD] #src failed: "
console.log e.message
return
else =>
des = src.replace /\.ls$/, ".js"
try
mkdir-recurse path.dirname(des)
fs.write-file-sync(
des,
uglify.minify(lsc.compile(fs.read-file-sync(src)toString!,{bare:true}),{fromString:true}).code
)
console.log "[BUILD] #src --> #des"
catch
console.log "[BUILD] #src failed: "
console.log e.message
return
if type == \styl =>
if /(basic|vars)\.styl/.exec it => return
try
styl-tree.parse src
srcs = styl-tree.find-root src
catch
console.log "[BUILD] #src failed: "
console.log e.message
console.log "[BUILD] recursive from #src:"
for src in srcs
try
des = src.replace(/src\/styl/, "static/css").replace(/\.styl$/, ".css")
stylus fs.read-file-sync(src)toString!
.set \filename, src
.define 'index', (a,b) ->
a = (a.string or a.val).split(' ')
return new stylus.nodes.Unit(a.indexOf b.val)
.render (e, css) ->
if e =>
console.log "[BUILD] #src failed: "
console.log " >>>", e.name
console.log " >>>", e.message
else =>
mkdir-recurse path.dirname(des)
fs.write-file-sync des, css
console.log "[BUILD] #src --> #des"
catch
console.log "[BUILD] #src failed: "
console.log e.message
if type == \jade =>
des = src.replace /\.jade$/, ".html"
try
desdir = path.dirname(des)
if !fs.exists-sync(desdir) or !fs.stat-sync(desdir).is-directory! => mkdir-recurse desdir
fs.write-file-sync des, jade.render (fs.read-file-sync src .toString!),{filename: src, basedir: path.join(cwd)}
console.log "[BUILD] #src --> #des"
catch
console.log "[BUILD] #src failed: "
console.log e.message
return
watcher = chokidar.watch watch-path, ignored: ignore-func, persistent: true
.on \add, update-file
.on \change, update-file
http.createServer server .listen 9999, \0.0.0.0
console.log "running server on 0.0.0.0:9999"
var spline;spline={update:function(t,n){var p,r,u,o,h,s,e,i,f=[];for(p=this.computeControlPoints(r=function(){var n,p,r,o=[];for(n=0,r=(p=t).length;r>n;++n)u=p[n],o.push(u.x);return o}()),o=this.computeControlPoints(h=function(){var n,p,r,o=[];for(n=0,r=(p=t).length;r>n;++n)u=p[n],o.push(u.y);return o}()),s=0,e=n.length;e>s;++s)i=s,f.push(n[i].d=this.path(r[i],h[i],p.p1[i],o.p1[i],p.p2[i],o.p2[i],r[i+1],h[i+1]));return f},path:function(t,n,p,r,u,o,h,s){return"M "+t+" "+n+" C "+p+" "+r+" "+u+" "+o+" "+h+" "+s},computeControlPoints:function(t){var n,p,r,u,o,h,s,e,i,f,l,a;for(n=[[],[]],p=n[0],r=n[1],n=[[0],[2],[1],[t[0]+2*t[1]],t.length-1],u=n[0],o=n[1],h=n[2],s=n[3],e=n[4],i=1,f=e-1;f>i;++i)l=i,u.push(1),o.push(4),h.push(1),s.push(4*t[l]+2*t[l+1]);for(u.push(2),o.push(7),h.push(0),s.push(8*t[e-1]+t[e]),i=1;e>i;++i)l=i,a=u[l]/o[l-1],o[l]=o[l]-a*h[l-1],s[l]=s[l]-a*s[l-1];for(p[e-1]=s[e-1]/o[e-1],i=e-2;i>=0;--i)l=i,p[l]=(s[l]-h[l]*p[l+1])/o[l];for(i=0,f=e-1;f>i;++i)l=i,r[l]=2*t[l+1]-p[l+1];return r[e-1]=(t[e]+p[e-1])/2,{p1:p,p2:r}}};
order = 'base'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment