Skip to content

Commit 97bd7ec

Browse files
author
Tang Ning
committed
Initial commit
1 parent ff07629 commit 97bd7ec

12 files changed

+561
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules/
2+
releases/
3+
*.swp

Gruntfile.js

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module.exports = function(grunt) {
2+
require('jit-grunt')(grunt);
3+
4+
grunt.initConfig({
5+
less: {
6+
development: {
7+
options: {
8+
compress: true,
9+
yuicompress: true,
10+
optimization: 2
11+
},
12+
files: {
13+
"stylesheets/main.css": "stylesheets/main.less"
14+
}
15+
}
16+
},
17+
watch: {
18+
styles: {
19+
files: ['stylesheets/**/*.less'], // which files to watch
20+
tasks: ['less'],
21+
options: {
22+
nospawn: true
23+
}
24+
}
25+
}
26+
});
27+
28+
grunt.registerTask('default', ['less', 'watch']);
29+
};

images/Icon.png

1.47 KB
Loading

images/[email protected]

2.99 KB
Loading

images/QuickStats.icns

259 KB
Binary file not shown.

index.html

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Web Scraper</title>
6+
7+
<link rel="stylesheet" href="node_modules/photon/dist/css/photon.css">
8+
<link href="./stylesheets/main.css" rel="stylesheet" type="text/css">
9+
10+
<script>var d3 = require('d3');</script>
11+
</head>
12+
13+
<body>
14+
<div class="window">
15+
<div class="window-content">
16+
<div id="container"></div>
17+
18+
<div class="gauge-container">
19+
<p>CPU Usage</p>
20+
<svg id="cpu-usage" width="100%" height="150"></svg>
21+
</div>
22+
23+
<div class="gauge-container">
24+
<p>Memory Usage</p>
25+
<svg id="mem-usage" width="100%" height="150"></svg>
26+
</div>
27+
</div>
28+
</div>
29+
30+
<script id="template" type="text/ractive">
31+
<table class="table-striped">
32+
<tr>
33+
<td>CPU Architecture</td>
34+
<td>{{ arch }}</td>
35+
</tr>
36+
<tr>
37+
<td>CPU Info</td>
38+
<td>{{ cpu_model }}</td>
39+
</tr>
40+
<tr>
41+
<td>Total Memory</td>
42+
<td>{{ total_mem }}</td>
43+
</tr>
44+
<tr>
45+
<td>Free Memory</td>
46+
<td>{{ free_mem }}</td>
47+
</tr>
48+
</table>
49+
</script>
50+
51+
<script src="js/liquidFillGauge.js"></script>
52+
<script src="js/stats.js"></script>
53+
</body>
54+
</html>

js/liquidFillGauge.js

+268
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
/*!
2+
* @license Open source under BSD 2-clause (http://choosealicense.com/licenses/bsd-2-clause/)
3+
* Copyright (c) 2015, Curtis Bratton
4+
* All rights reserved.
5+
*
6+
* Liquid Fill Gauge v1.1
7+
*/
8+
function liquidFillGaugeDefaultSettings(){
9+
return {
10+
minValue: 0, // The gauge minimum value.
11+
maxValue: 100, // The gauge maximum value.
12+
circleThickness: 0.05, // The outer circle thickness as a percentage of it's radius.
13+
circleFillGap: 0.05, // The size of the gap between the outer circle and wave circle as a percentage of the outer circles radius.
14+
circleColor: "#178BCA", // The color of the outer circle.
15+
waveHeight: 0.05, // The wave height as a percentage of the radius of the wave circle.
16+
waveCount: 1, // The number of full waves per width of the wave circle.
17+
waveRiseTime: 1000, // The amount of time in milliseconds for the wave to rise from 0 to it's final height.
18+
waveAnimateTime: 18000, // The amount of time in milliseconds for a full wave to enter the wave circle.
19+
waveRise: true, // Control if the wave should rise from 0 to it's full height, or start at it's full height.
20+
waveHeightScaling: true, // Controls wave size scaling at low and high fill percentages. When true, wave height reaches it's maximum at 50% fill, and minimum at 0% and 100% fill. This helps to prevent the wave from making the wave circle from appear totally full or empty when near it's minimum or maximum fill.
21+
waveAnimate: true, // Controls if the wave scrolls or is static.
22+
waveColor: "#178BCA", // The color of the fill wave.
23+
waveOffset: 0, // The amount to initially offset the wave. 0 = no offset. 1 = offset of one full wave.
24+
textVertPosition: .5, // The height at which to display the percentage text withing the wave circle. 0 = bottom, 1 = top.
25+
textSize: 1, // The relative height of the text to display in the wave circle. 1 = 50%
26+
valueCountUp: true, // If true, the displayed value counts up from 0 to it's final value upon loading. If false, the final value is displayed.
27+
displayPercent: true, // If true, a % symbol is displayed after the value.
28+
textColor: "#045681", // The color of the value text when the wave does not overlap it.
29+
waveTextColor: "#A4DBf8" // The color of the value text when the wave overlaps it.
30+
};
31+
}
32+
33+
function loadLiquidFillGauge(elementId, value, config) {
34+
if(config == null) config = liquidFillGaugeDefaultSettings();
35+
36+
var gauge = d3.select("#" + elementId);
37+
var radius = Math.min(parseInt(gauge.style("width")), parseInt(gauge.style("height")))/2;
38+
var locationX = parseInt(gauge.style("width"))/2 - radius;
39+
var locationY = parseInt(gauge.style("height"))/2 - radius;
40+
var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value))/config.maxValue;
41+
42+
var waveHeightScale;
43+
if(config.waveHeightScaling){
44+
waveHeightScale = d3.scale.linear()
45+
.range([0,config.waveHeight,0])
46+
.domain([0,50,100]);
47+
} else {
48+
waveHeightScale = d3.scale.linear()
49+
.range([config.waveHeight,config.waveHeight])
50+
.domain([0,100]);
51+
}
52+
53+
var textPixels = (config.textSize*radius/2);
54+
var textFinalValue = parseFloat(value).toFixed(2);
55+
var textStartValue = config.valueCountUp?config.minValue:textFinalValue;
56+
var percentText = config.displayPercent?"%":"";
57+
var circleThickness = config.circleThickness * radius;
58+
var circleFillGap = config.circleFillGap * radius;
59+
var fillCircleMargin = circleThickness + circleFillGap;
60+
var fillCircleRadius = radius - fillCircleMargin;
61+
var waveHeight = fillCircleRadius*waveHeightScale(fillPercent*100);
62+
63+
var waveLength = fillCircleRadius*2/config.waveCount;
64+
var waveClipCount = 1+config.waveCount;
65+
var waveClipWidth = waveLength*waveClipCount;
66+
67+
// Rounding functions so that the correct number of decimal places is always displayed as the value counts up.
68+
var textRounder = function(value){ return Math.round(value); };
69+
if(parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))){
70+
textRounder = function(value){ return parseFloat(value).toFixed(1); };
71+
}
72+
if(parseFloat(textFinalValue) != parseFloat(textRounder(textFinalValue))){
73+
textRounder = function(value){ return parseFloat(value).toFixed(2); };
74+
}
75+
76+
// Data for building the clip wave area.
77+
var data = [];
78+
for(var i = 0; i <= 40*waveClipCount; i++){
79+
data.push({x: i/(40*waveClipCount), y: (i/(40))});
80+
}
81+
82+
// Scales for drawing the outer circle.
83+
var gaugeCircleX = d3.scale.linear().range([0,2*Math.PI]).domain([0,1]);
84+
var gaugeCircleY = d3.scale.linear().range([0,radius]).domain([0,radius]);
85+
86+
// Scales for controlling the size of the clipping path.
87+
var waveScaleX = d3.scale.linear().range([0,waveClipWidth]).domain([0,1]);
88+
var waveScaleY = d3.scale.linear().range([0,waveHeight]).domain([0,1]);
89+
90+
// Scales for controlling the position of the clipping path.
91+
var waveRiseScale = d3.scale.linear()
92+
// The clipping area size is the height of the fill circle + the wave height, so we position the clip wave
93+
// such that the it will overlap the fill circle at all when at 0%, and will totally cover the fill
94+
// circle at 100%.
95+
.range([(fillCircleMargin+fillCircleRadius*2+waveHeight),(fillCircleMargin-waveHeight)])
96+
.domain([0,1]);
97+
var waveAnimateScale = d3.scale.linear()
98+
.range([0, waveClipWidth-fillCircleRadius*2]) // Push the clip area one full wave then snap back.
99+
.domain([0,1]);
100+
101+
// Scale for controlling the position of the text within the gauge.
102+
var textRiseScaleY = d3.scale.linear()
103+
.range([fillCircleMargin+fillCircleRadius*2,(fillCircleMargin+textPixels*0.7)])
104+
.domain([0,1]);
105+
106+
// Center the gauge within the parent SVG.
107+
var gaugeGroup = gauge.append("g")
108+
.attr('transform','translate('+locationX+','+locationY+')');
109+
110+
// Draw the outer circle.
111+
var gaugeCircleArc = d3.svg.arc()
112+
.startAngle(gaugeCircleX(0))
113+
.endAngle(gaugeCircleX(1))
114+
.outerRadius(gaugeCircleY(radius))
115+
.innerRadius(gaugeCircleY(radius-circleThickness));
116+
gaugeGroup.append("path")
117+
.attr("d", gaugeCircleArc)
118+
.style("fill", config.circleColor)
119+
.attr('transform','translate('+radius+','+radius+')');
120+
121+
// Text where the wave does not overlap.
122+
var text1 = gaugeGroup.append("text")
123+
.text(textRounder(textStartValue) + percentText)
124+
.attr("class", "liquidFillGaugeText")
125+
.attr("text-anchor", "middle")
126+
.attr("font-size", textPixels + "px")
127+
.style("fill", config.textColor)
128+
.attr('transform','translate('+radius+','+textRiseScaleY(config.textVertPosition)+')');
129+
130+
// The clipping wave area.
131+
var clipArea = d3.svg.area()
132+
.x(function(d) { return waveScaleX(d.x); } )
133+
.y0(function(d) { return waveScaleY(Math.sin(Math.PI*2*config.waveOffset*-1 + Math.PI*2*(1-config.waveCount) + d.y*2*Math.PI));} )
134+
.y1(function(d) { return (fillCircleRadius*2 + waveHeight); } );
135+
var waveGroup = gaugeGroup.append("defs")
136+
.append("clipPath")
137+
.attr("id", "clipWave" + elementId);
138+
var wave = waveGroup.append("path")
139+
.datum(data)
140+
.attr("d", clipArea)
141+
.attr("T", 0);
142+
143+
// The inner circle with the clipping wave attached.
144+
var fillCircleGroup = gaugeGroup.append("g")
145+
.attr("clip-path", "url(#clipWave" + elementId + ")");
146+
fillCircleGroup.append("circle")
147+
.attr("cx", radius)
148+
.attr("cy", radius)
149+
.attr("r", fillCircleRadius)
150+
.style("fill", config.waveColor);
151+
152+
// Text where the wave does overlap.
153+
var text2 = fillCircleGroup.append("text")
154+
.text(textRounder(textStartValue) + percentText)
155+
.attr("class", "liquidFillGaugeText")
156+
.attr("text-anchor", "middle")
157+
.attr("font-size", textPixels + "px")
158+
.style("fill", config.waveTextColor)
159+
.attr('transform','translate('+radius+','+textRiseScaleY(config.textVertPosition)+')');
160+
161+
// Make the value count up.
162+
if(config.valueCountUp){
163+
var textTween = function(){
164+
var i = d3.interpolate(this.textContent, textFinalValue);
165+
return function(t) { this.textContent = textRounder(i(t)) + percentText; }
166+
};
167+
text1.transition()
168+
.duration(config.waveRiseTime)
169+
.tween("text", textTween);
170+
text2.transition()
171+
.duration(config.waveRiseTime)
172+
.tween("text", textTween);
173+
}
174+
175+
// Make the wave rise. wave and waveGroup are separate so that horizontal and vertical movement can be controlled independently.
176+
var waveGroupXPosition = fillCircleMargin+fillCircleRadius*2-waveClipWidth;
177+
if(config.waveRise){
178+
waveGroup.attr('transform','translate('+waveGroupXPosition+','+waveRiseScale(0)+')')
179+
.transition()
180+
.duration(config.waveRiseTime)
181+
.attr('transform','translate('+waveGroupXPosition+','+waveRiseScale(fillPercent)+')')
182+
.each("start", function(){ wave.attr('transform','translate(1,0)'); }); // This transform is necessary to get the clip wave positioned correctly when waveRise=true and waveAnimate=false. The wave will not position correctly without this, but it's not clear why this is actually necessary.
183+
} else {
184+
waveGroup.attr('transform','translate('+waveGroupXPosition+','+waveRiseScale(fillPercent)+')');
185+
}
186+
187+
if(config.waveAnimate) animateWave();
188+
189+
function animateWave() {
190+
wave.attr('transform','translate('+waveAnimateScale(wave.attr('T'))+',0)');
191+
wave.transition()
192+
.duration(config.waveAnimateTime * (1-wave.attr('T')))
193+
.ease('linear')
194+
.attr('transform','translate('+waveAnimateScale(1)+',0)')
195+
.attr('T', 1)
196+
.each('end', function(){
197+
wave.attr('T', 0);
198+
animateWave(config.waveAnimateTime);
199+
});
200+
}
201+
202+
function GaugeUpdater(){
203+
this.update = function(value){
204+
var newFinalValue = parseFloat(value).toFixed(2);
205+
var textRounderUpdater = function(value){ return Math.round(value); };
206+
if(parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))){
207+
textRounderUpdater = function(value){ return parseFloat(value).toFixed(1); };
208+
}
209+
if(parseFloat(newFinalValue) != parseFloat(textRounderUpdater(newFinalValue))){
210+
textRounderUpdater = function(value){ return parseFloat(value).toFixed(2); };
211+
}
212+
213+
var textTween = function(){
214+
var i = d3.interpolate(this.textContent, parseFloat(value).toFixed(2));
215+
return function(t) { this.textContent = textRounderUpdater(i(t)) + percentText; }
216+
};
217+
218+
text1.transition()
219+
.duration(config.waveRiseTime)
220+
.tween("text", textTween);
221+
text2.transition()
222+
.duration(config.waveRiseTime)
223+
.tween("text", textTween);
224+
225+
var fillPercent = Math.max(config.minValue, Math.min(config.maxValue, value))/config.maxValue;
226+
var waveHeight = fillCircleRadius*waveHeightScale(fillPercent*100);
227+
var waveRiseScale = d3.scale.linear()
228+
// The clipping area size is the height of the fill circle + the wave height, so we position the clip wave
229+
// such that the it will overlap the fill circle at all when at 0%, and will totally cover the fill
230+
// circle at 100%.
231+
.range([(fillCircleMargin+fillCircleRadius*2+waveHeight),(fillCircleMargin-waveHeight)])
232+
.domain([0,1]);
233+
var newHeight = waveRiseScale(fillPercent);
234+
var waveScaleX = d3.scale.linear().range([0,waveClipWidth]).domain([0,1]);
235+
var waveScaleY = d3.scale.linear().range([0,waveHeight]).domain([0,1]);
236+
var newClipArea;
237+
if(config.waveHeightScaling){
238+
newClipArea = d3.svg.area()
239+
.x(function(d) { return waveScaleX(d.x); } )
240+
.y0(function(d) { return waveScaleY(Math.sin(Math.PI*2*config.waveOffset*-1 + Math.PI*2*(1-config.waveCount) + d.y*2*Math.PI));} )
241+
.y1(function(d) { return (fillCircleRadius*2 + waveHeight); } );
242+
} else {
243+
newClipArea = clipArea;
244+
}
245+
246+
var newWavePosition = config.waveAnimate?waveAnimateScale(1):0;
247+
wave.transition()
248+
.duration(0)
249+
.transition()
250+
.duration(config.waveAnimate?(config.waveAnimateTime * (1-wave.attr('T'))):(config.waveRiseTime))
251+
.ease('linear')
252+
.attr('d', newClipArea)
253+
.attr('transform','translate('+newWavePosition+',0)')
254+
.attr('T','1')
255+
.each("end", function(){
256+
if(config.waveAnimate){
257+
wave.attr('transform','translate('+waveAnimateScale(0)+',0)');
258+
animateWave(config.waveAnimateTime);
259+
}
260+
});
261+
waveGroup.transition()
262+
.duration(config.waveRiseTime)
263+
.attr('transform','translate('+waveGroupXPosition+','+newHeight+')')
264+
}
265+
}
266+
267+
return new GaugeUpdater();
268+
}

0 commit comments

Comments
 (0)