mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-09 16:32:39 +01:00
Swap charts from gRaphael to D3
Summary: Mostly, this has just been sitting in my sandbox for a long time. I may also touch some charting stuff with subprojects/milestones, but don't have particular plans to do that. D3 seems a bit more flexible, and it's easier to push more of the style logic into CSS so you can fix my design atrocities. gRaphael also hasn't been updated in ~3+ years. Test Plan: {F1085433} {F1085434} Reviewers: chad Reviewed By: chad Subscribers: cburroughs, yelirekim Differential Revision: https://secure.phabricator.com/D15155
This commit is contained in:
parent
18f34fab73
commit
f5c686d6a4
13 changed files with 235 additions and 141 deletions
|
@ -125,6 +125,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
|
||||||
'rsrc/css/phui/phui-box.css' => '6e8ac7fd',
|
'rsrc/css/phui/phui-box.css' => '6e8ac7fd',
|
||||||
'rsrc/css/phui/phui-button.css' => 'd6ac72db',
|
'rsrc/css/phui/phui-button.css' => 'd6ac72db',
|
||||||
|
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||||
'rsrc/css/phui/phui-crumbs-view.css' => '414406b5',
|
'rsrc/css/phui/phui-crumbs-view.css' => '414406b5',
|
||||||
'rsrc/css/phui/phui-document-pro.css' => '8799acf7',
|
'rsrc/css/phui/phui-document-pro.css' => '8799acf7',
|
||||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||||
|
@ -158,6 +159,7 @@ return array(
|
||||||
'rsrc/css/sprite-login.css' => '60e8560e',
|
'rsrc/css/sprite-login.css' => '60e8560e',
|
||||||
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
'rsrc/css/sprite-menu.css' => '9dd65b92',
|
||||||
'rsrc/css/sprite-tokens.css' => '4f399012',
|
'rsrc/css/sprite-tokens.css' => '4f399012',
|
||||||
|
'rsrc/externals/d3/d3.min.js' => 'a11a5ff2',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
|
'rsrc/externals/font/aleo/aleo-bold.eot' => 'd3d3bed7',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
|
'rsrc/externals/font/aleo/aleo-bold.svg' => '45899c8e',
|
||||||
'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
|
'rsrc/externals/font/aleo/aleo-bold.ttf' => '4b08bef0',
|
||||||
|
@ -252,9 +254,6 @@ return array(
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '1bc11c4a',
|
||||||
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
|
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '6c0e62fa',
|
||||||
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
|
|
||||||
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
|
|
||||||
'rsrc/externals/raphael/raphael.js' => '51ee6b43',
|
|
||||||
'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
|
'rsrc/favicons/apple-touch-icon-120x120.png' => '43742962',
|
||||||
'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
|
'rsrc/favicons/apple-touch-icon-152x152.png' => '669eaec3',
|
||||||
'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
|
'rsrc/favicons/apple-touch-icon-76x76.png' => 'ecdef672',
|
||||||
|
@ -401,7 +400,7 @@ return array(
|
||||||
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
|
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
|
'rsrc/js/application/maniphest/behavior-batch-selector.js' => '7b98d7c5',
|
||||||
'rsrc/js/application/maniphest/behavior-line-chart.js' => '88f0c5b3',
|
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
|
||||||
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
||||||
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
|
'rsrc/js/application/maniphest/behavior-subpriorityeditor.js' => '71237763',
|
||||||
'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
|
'rsrc/js/application/owners/OwnersPathEditor.js' => 'aa1733d0',
|
||||||
|
@ -535,6 +534,7 @@ return array(
|
||||||
'conpherence-transaction-css' => '85d0974c',
|
'conpherence-transaction-css' => '85d0974c',
|
||||||
'conpherence-update-css' => 'faf6be09',
|
'conpherence-update-css' => 'faf6be09',
|
||||||
'conpherence-widget-pane-css' => '775eaaba',
|
'conpherence-widget-pane-css' => '775eaaba',
|
||||||
|
'd3' => 'a11a5ff2',
|
||||||
'differential-changeset-view-css' => 'b6b0d1bb',
|
'differential-changeset-view-css' => 'b6b0d1bb',
|
||||||
'differential-core-view-css' => '7ac3cabc',
|
'differential-core-view-css' => '7ac3cabc',
|
||||||
'differential-inline-comment-editor' => '64a5550f',
|
'differential-inline-comment-editor' => '64a5550f',
|
||||||
|
@ -615,7 +615,7 @@ return array(
|
||||||
'javelin-behavior-icon-composer' => '8499b6ab',
|
'javelin-behavior-icon-composer' => '8499b6ab',
|
||||||
'javelin-behavior-launch-icon-composer' => '48086888',
|
'javelin-behavior-launch-icon-composer' => '48086888',
|
||||||
'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
|
'javelin-behavior-lightbox-attachments' => 'f8ba29d7',
|
||||||
'javelin-behavior-line-chart' => '88f0c5b3',
|
'javelin-behavior-line-chart' => 'e4232876',
|
||||||
'javelin-behavior-load-blame' => '42126667',
|
'javelin-behavior-load-blame' => '42126667',
|
||||||
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
||||||
'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
|
'javelin-behavior-maniphest-batch-selector' => '7b98d7c5',
|
||||||
|
@ -799,6 +799,7 @@ return array(
|
||||||
'phui-calendar-day-css' => 'd1cf6f93',
|
'phui-calendar-day-css' => 'd1cf6f93',
|
||||||
'phui-calendar-list-css' => 'c1c7f338',
|
'phui-calendar-list-css' => 'c1c7f338',
|
||||||
'phui-calendar-month-css' => '476be7e0',
|
'phui-calendar-month-css' => '476be7e0',
|
||||||
|
'phui-chart-css' => '6bf6f78e',
|
||||||
'phui-crumbs-view-css' => '414406b5',
|
'phui-crumbs-view-css' => '414406b5',
|
||||||
'phui-document-summary-view-css' => '9ca48bdf',
|
'phui-document-summary-view-css' => '9ca48bdf',
|
||||||
'phui-document-view-css' => '9c71d2bf',
|
'phui-document-view-css' => '9c71d2bf',
|
||||||
|
@ -843,9 +844,6 @@ return array(
|
||||||
'policy-transaction-detail-css' => '82100a43',
|
'policy-transaction-detail-css' => '82100a43',
|
||||||
'ponder-view-css' => '7b0df4da',
|
'ponder-view-css' => '7b0df4da',
|
||||||
'project-view-css' => 'c6387c87',
|
'project-view-css' => 'c6387c87',
|
||||||
'raphael-core' => '51ee6b43',
|
|
||||||
'raphael-g' => '40dde778',
|
|
||||||
'raphael-g-line' => '40da039e',
|
|
||||||
'releeph-core' => '9b3c5733',
|
'releeph-core' => '9b3c5733',
|
||||||
'releeph-preview-branch' => 'b7a6f4a5',
|
'releeph-preview-branch' => 'b7a6f4a5',
|
||||||
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
'releeph-request-differential-create-dialog' => '8d8b92cd',
|
||||||
|
@ -1477,11 +1475,6 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'88f0c5b3' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-vector',
|
|
||||||
),
|
|
||||||
'8a41885b' => array(
|
'8a41885b' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1950,6 +1943,12 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'javelin-uri',
|
'javelin-uri',
|
||||||
),
|
),
|
||||||
|
'e4232876' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-vector',
|
||||||
|
'phui-chart-css',
|
||||||
|
),
|
||||||
'e4cc26b3' => array(
|
'e4cc26b3' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
|
@ -66,7 +66,7 @@ final class CelerityResourceTransformer extends Phobject {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some resources won't survive minification (like Raphael.js), and are
|
// Some resources won't survive minification (like d3.min.js), and are
|
||||||
// marked so as not to be minified.
|
// marked so as not to be minified.
|
||||||
if (strpos($data, '@'.'do-not-minify') !== false) {
|
if (strpos($data, '@'.'do-not-minify') !== false) {
|
||||||
return $data;
|
return $data;
|
||||||
|
|
|
@ -30,8 +30,7 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$points) {
|
if (!$points) {
|
||||||
// NOTE: Raphael crashes Safari if you hand it series with no points.
|
throw new Exception('No data to show!');
|
||||||
throw new Exception(pht('No data to show!'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit amount of data passed to browser.
|
// Limit amount of data passed to browser.
|
||||||
|
@ -56,16 +55,12 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
'div',
|
'div',
|
||||||
array(
|
array(
|
||||||
'id' => $id,
|
'id' => $id,
|
||||||
'style' => 'border: 1px solid #6f6f6f; '.
|
'style' => 'background: #ffffff; '.
|
||||||
'margin: 1em 2em; '.
|
'height: 480px; ',
|
||||||
'background: #ffffff; '.
|
|
||||||
'height: 400px; ',
|
|
||||||
),
|
),
|
||||||
'');
|
'');
|
||||||
|
|
||||||
require_celerity_resource('raphael-core');
|
require_celerity_resource('d3');
|
||||||
require_celerity_resource('raphael-g');
|
|
||||||
require_celerity_resource('raphael-g-line');
|
|
||||||
|
|
||||||
Javelin::initBehavior('line-chart', array(
|
Javelin::initBehavior('line-chart', array(
|
||||||
'hardpoint' => $id,
|
'hardpoint' => $id,
|
||||||
|
@ -75,9 +70,9 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
'colors' => array('#0000ff'),
|
'colors' => array('#0000ff'),
|
||||||
));
|
));
|
||||||
|
|
||||||
$panel = new PHUIObjectBoxView();
|
$box = id(new PHUIObjectBoxView())
|
||||||
$panel->setHeaderText(pht('Count of %s', $spec->getName()));
|
->setHeaderText(pht('Count of %s', $spec->getName()))
|
||||||
$panel->appendChild($chart);
|
->appendChild($chart);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(pht('Chart'));
|
$crumbs->addTextCrumb(pht('Chart'));
|
||||||
|
@ -85,7 +80,7 @@ final class PhabricatorFactChartController extends PhabricatorFactController {
|
||||||
return $this->buildApplicationPage(
|
return $this->buildApplicationPage(
|
||||||
array(
|
array(
|
||||||
$crumbs,
|
$crumbs,
|
||||||
$panel,
|
$box,
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'title' => pht('Chart'),
|
'title' => pht('Chart'),
|
||||||
|
|
|
@ -290,9 +290,8 @@ final class ManiphestReportController extends ManiphestController {
|
||||||
|
|
||||||
list($burn_x, $burn_y) = $this->buildSeries($data);
|
list($burn_x, $burn_y) = $this->buildSeries($data);
|
||||||
|
|
||||||
require_celerity_resource('raphael-core');
|
require_celerity_resource('d3');
|
||||||
require_celerity_resource('raphael-g');
|
require_celerity_resource('phui-chart-css');
|
||||||
require_celerity_resource('raphael-g-line');
|
|
||||||
|
|
||||||
Javelin::initBehavior('line-chart', array(
|
Javelin::initBehavior('line-chart', array(
|
||||||
'hardpoint' => $id,
|
'hardpoint' => $id,
|
||||||
|
@ -306,7 +305,11 @@ final class ManiphestReportController extends ManiphestController {
|
||||||
'yformat' => 'int',
|
'yformat' => 'int',
|
||||||
));
|
));
|
||||||
|
|
||||||
return array($filter, $chart, $panel);
|
$box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Burnup Rate'))
|
||||||
|
->appendChild($chart);
|
||||||
|
|
||||||
|
return array($filter, $box, $panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderReportFilters(array $tokens, $has_window) {
|
private function renderReportFilters(array $tokens, $has_window) {
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
|
|
||||||
"globals": {
|
"globals": {
|
||||||
"JX": false,
|
"JX": false,
|
||||||
"Raphael": false,
|
"d3": false,
|
||||||
"__DEV__": false
|
"__DEV__": false
|
||||||
},
|
},
|
||||||
|
|
||||||
"browser": true
|
"browser": true
|
||||||
}
|
}
|
||||||
|
|
54
webroot/rsrc/css/phui/phui-chart.css
Normal file
54
webroot/rsrc/css/phui/phui-chart.css
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* @provides phui-chart-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.chart .axis line,
|
||||||
|
.chart .axis path {
|
||||||
|
fill: none;
|
||||||
|
stroke: {$blueborder};
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .axis text {
|
||||||
|
fill: {$darkgreytext};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .outer,
|
||||||
|
.chart .inner {
|
||||||
|
shape-rendering: crispEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .outer {
|
||||||
|
fill: none;
|
||||||
|
stroke: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .inner {
|
||||||
|
fill: {$lightbluebackground};
|
||||||
|
stroke: {$lightblueborder};
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .line {
|
||||||
|
fill: none;
|
||||||
|
stroke: {$blue};
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart .point {
|
||||||
|
fill: {$lightblue};
|
||||||
|
stroke: {$blue};
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-tooltip {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
width: 120px;
|
||||||
|
height: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2px;
|
||||||
|
background: {$lightbluebackground};
|
||||||
|
border: 1px solid {$blueborder};
|
||||||
|
border-radius: 8px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
26
webroot/rsrc/externals/d3/LICENSE
vendored
Normal file
26
webroot/rsrc/externals/d3/LICENSE
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
Copyright (c) 2010-2014, Michael Bostock
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
* The name Michael Bostock may not be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
|
||||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
|
||||||
|
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||||
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
9
webroot/rsrc/externals/d3/README.md
vendored
Normal file
9
webroot/rsrc/externals/d3/README.md
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Data-Driven Documents
|
||||||
|
|
||||||
|
<a href="http://d3js.org"><img src="http://d3js.org/logo.svg" align="left" hspace="10" vspace="6"></a>
|
||||||
|
|
||||||
|
**D3.js** is a JavaScript library for manipulating documents based on data. **D3** helps you bring data to life using HTML, SVG and CSS. D3’s emphasis on web standards gives you the full capabilities of modern browsers without tying yourself to a proprietary framework, combining powerful visualization components and a data-driven approach to DOM manipulation.
|
||||||
|
|
||||||
|
Want to learn more? [See the wiki.](https://github.com/mbostock/d3/wiki)
|
||||||
|
|
||||||
|
For examples, [see the gallery](https://github.com/mbostock/d3/wiki/Gallery) and [mbostock’s bl.ocks](http://bl.ocks.org/mbostock).
|
9
webroot/rsrc/externals/d3/d3.min.js
vendored
Normal file
9
webroot/rsrc/externals/d3/d3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
webroot/rsrc/externals/raphael/g.raphael.js
vendored
12
webroot/rsrc/externals/raphael/g.raphael.js
vendored
File diff suppressed because one or more lines are too long
12
webroot/rsrc/externals/raphael/g.raphael.line.js
vendored
12
webroot/rsrc/externals/raphael/g.raphael.line.js
vendored
|
@ -1,12 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides raphael-g-line
|
|
||||||
* @do-not-minify
|
|
||||||
* @nolint
|
|
||||||
*/
|
|
||||||
/*!
|
|
||||||
* g.Raphael 0.5 - Charting library, based on Raphaël
|
|
||||||
*
|
|
||||||
* Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
|
|
||||||
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
|
|
||||||
*/
|
|
||||||
(function(){function a(g,n){var f=g.length/n,h=0,e=f,m=0,i=[];while(h<g.length){e--;if(e<0){m+=g[h]*(1+e);i.push(m/f);m=g[h++]*-e;e+=f}else{m+=g[h++]}}return i}function d(f,e,p,n,k,j){var h=(p-f)/2,g=(k-p)/2,q=Math.atan((p-f)/Math.abs(n-e)),o=Math.atan((k-p)/Math.abs(n-j));q=e<n?Math.PI-q:q;o=j<n?Math.PI-o:o;var i=Math.PI/2-((q+o)%(Math.PI*2))/2,s=h*Math.sin(i+q),m=h*Math.cos(i+q),r=g*Math.sin(i+o),l=g*Math.cos(i+o);return{x1:p-s,y1:n+m,x2:p+r,y2:n+l}}function b(f,P,O,e,h,A,z,J){var s=this;J=J||{};if(!f.raphael.is(A[0],"array")){A=[A]}if(!f.raphael.is(z[0],"array")){z=[z]}var q=J.gutter||10,B=Math.max(A[0].length,z[0].length),t=J.symbol||"",S=J.colors||s.colors,v=null,p=null,ad=f.set(),T=[];for(var ac=0,L=z.length;ac<L;ac++){B=Math.max(B,z[ac].length)}var ae=f.set();for(ac=0,L=z.length;ac<L;ac++){if(J.shade){ae.push(f.path().attr({stroke:"none",fill:S[ac],opacity:J.nostroke?1:0.3}))}if(z[ac].length>e-2*q){z[ac]=a(z[ac],e-2*q);B=e-2*q}if(A[ac]&&A[ac].length>e-2*q){A[ac]=a(A[ac],e-2*q)}}var W=Array.prototype.concat.apply([],A),U=Array.prototype.concat.apply([],z),u=s.snapEnds(Math.min.apply(Math,W),Math.max.apply(Math,W),A[0].length-1),E=u.from,o=u.to,N=s.snapEnds(Math.min.apply(Math,U),Math.max.apply(Math,U),z[0].length-1),C=N.from,n=N.to,Z=(e-q*2)/((o-E)||1),V=(h-q*2)/((n-C)||1);var G=f.set();if(J.axis){var m=(J.axis+"").split(/[,\s]+/);+m[0]&&G.push(s.axis(P+q,O+q,e-2*q,E,o,J.axisxstep||Math.floor((e-2*q)/20),2,f));+m[1]&&G.push(s.axis(P+e-q,O+h-q,h-2*q,C,n,J.axisystep||Math.floor((h-2*q)/20),3,f));+m[2]&&G.push(s.axis(P+q,O+h-q,e-2*q,E,o,J.axisxstep||Math.floor((e-2*q)/20),0,f));+m[3]&&G.push(s.axis(P+q,O+h-q,h-2*q,C,n,J.axisystep||Math.floor((h-2*q)/20),1,f))}var M=f.set(),aa=f.set(),r;for(ac=0,L=z.length;ac<L;ac++){if(!J.nostroke){M.push(r=f.path().attr({stroke:S[ac],"stroke-width":J.width||2,"stroke-linejoin":"round","stroke-linecap":"round","stroke-dasharray":J.dash||""}))}var g=Raphael.is(t,"array")?t[ac]:t,H=f.set();T=[];for(var ab=0,w=z[ac].length;ab<w;ab++){var l=P+q+((A[ac]||A[0])[ab]-E)*Z,k=O+h-q-(z[ac][ab]-C)*V;(Raphael.is(g,"array")?g[ab]:g)&&H.push(f[Raphael.is(g,"array")?g[ab]:g](l,k,(J.width||2)*3).attr({fill:S[ac],stroke:"none"}));if(J.smooth){if(ab&&ab!=w-1){var R=P+q+((A[ac]||A[0])[ab-1]-E)*Z,F=O+h-q-(z[ac][ab-1]-C)*V,Q=P+q+((A[ac]||A[0])[ab+1]-E)*Z,D=O+h-q-(z[ac][ab+1]-C)*V,af=d(R,F,l,k,Q,D);T=T.concat([af.x1,af.y1,l,k,af.x2,af.y2])}if(!ab){T=["M",l,k,"C",l,k]}}else{T=T.concat([ab?"L":"M",l,k])}}if(J.smooth){T=T.concat([l,k,l,k])}aa.push(H);if(J.shade){ae[ac].attr({path:T.concat(["L",l,O+h-q,"L",P+q+((A[ac]||A[0])[0]-E)*Z,O+h-q,"z"]).join(",")})}!J.nostroke&&r.attr({path:T.join(",")})}function K(an){var ak=[];for(var al=0,ap=A.length;al<ap;al++){ak=ak.concat(A[al])}ak.sort();var aq=[],ah=[];for(al=0,ap=ak.length;al<ap;al++){ak[al]!=ak[al-1]&&aq.push(ak[al])&&ah.push(P+q+(ak[al]-E)*Z)}ak=aq;ap=ak.length;var ag=an||f.set();for(al=0;al<ap;al++){var Y=ah[al]-(ah[al]-(ah[al-1]||P))/2,ao=((ah[al+1]||P+e)-ah[al])/2+(ah[al]-(ah[al-1]||P))/2,x;an?(x={}):ag.push(x=f.rect(Y-1,O,Math.max(ao+1,1),h).attr({stroke:"none",fill:"#000",opacity:0}));x.values=[];x.symbols=f.set();x.y=[];x.x=ah[al];x.axis=ak[al];for(var aj=0,am=z.length;aj<am;aj++){aq=A[aj]||A[0];for(var ai=0,y=aq.length;ai<y;ai++){if(aq[ai]==ak[al]){x.values.push(z[aj][ai]);x.y.push(O+h-q-(z[aj][ai]-C)*V);x.symbols.push(ad.symbols[aj][ai])}}}an&&an.call(x)}!an&&(v=ag)}function I(al){var ah=al||f.set(),x;for(var aj=0,an=z.length;aj<an;aj++){for(var ai=0,ak=z[aj].length;ai<ak;ai++){var ag=P+q+((A[aj]||A[0])[ai]-E)*Z,am=P+q+((A[aj]||A[0])[ai?ai-1:1]-E)*Z,y=O+h-q-(z[aj][ai]-C)*V;al?(x={}):ah.push(x=f.circle(ag,y,Math.abs(am-ag)/2).attr({stroke:"none",fill:"#000",opacity:0}));x.x=ag;x.y=y;x.value=z[aj][ai];x.line=ad.lines[aj];x.shade=ad.shades[aj];x.symbol=ad.symbols[aj][ai];x.symbols=ad.symbols[aj];x.axis=(A[aj]||A[0])[ai];al&&al.call(x)}}!al&&(p=ah)}ad.push(M,ae,aa,G,v,p);ad.lines=M;ad.shades=ae;ad.symbols=aa;ad.axis=G;ad.hoverColumn=function(j,i){!v&&K();v.mouseover(j).mouseout(i);return this};ad.clickColumn=function(i){!v&&K();v.click(i);return this};ad.hrefColumn=function(Y){var ag=f.raphael.is(arguments[0],"array")?arguments[0]:arguments;if(!(arguments.length-1)&&typeof Y=="object"){for(var j in Y){for(var y=0,X=v.length;y<X;y++){if(v[y].axis==j){v[y].attr("href",Y[j])}}}}!v&&K();for(y=0,X=ag.length;y<X;y++){v[y]&&v[y].attr("href",ag[y])}return this};ad.hover=function(j,i){!p&&I();p.mouseover(j).mouseout(i);return this};ad.click=function(i){!p&&I();p.click(i);return this};ad.each=function(i){I(i);return this};ad.eachColumn=function(i){K(i);return this};return ad}var c=function(){};c.prototype=Raphael.g;b.prototype=new c;Raphael.fn.linechart=function(f,k,g,e,j,i,h){return new b(this,f,k,g,e,j,i,h)}})();
|
|
13
webroot/rsrc/externals/raphael/raphael.js
vendored
13
webroot/rsrc/externals/raphael/raphael.js
vendored
File diff suppressed because one or more lines are too long
|
@ -3,86 +3,121 @@
|
||||||
* @requires javelin-behavior
|
* @requires javelin-behavior
|
||||||
* javelin-dom
|
* javelin-dom
|
||||||
* javelin-vector
|
* javelin-vector
|
||||||
|
* phui-chart-css
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('line-chart', function(config) {
|
JX.behavior('line-chart', function(config) {
|
||||||
|
|
||||||
|
function fn(n) {
|
||||||
|
return n + '(' + JX.$A(arguments).slice(1).join(', ') + ')';
|
||||||
|
}
|
||||||
|
|
||||||
var h = JX.$(config.hardpoint);
|
var h = JX.$(config.hardpoint);
|
||||||
var p = JX.$V(h);
|
|
||||||
var d = JX.Vector.getDim(h);
|
var d = JX.Vector.getDim(h);
|
||||||
var mx = 60;
|
|
||||||
var my = 30;
|
|
||||||
|
|
||||||
var r = new Raphael(h, d.x, d.y);
|
var padding = {
|
||||||
|
top: 24,
|
||||||
|
left: 48,
|
||||||
|
bottom: 48,
|
||||||
|
right: 32
|
||||||
|
};
|
||||||
|
|
||||||
var l = r.linechart(
|
var size = {
|
||||||
mx, my,
|
frameWidth: d.x,
|
||||||
d.x - (2 * mx), d.y - (2 * my),
|
frameHeight: d.y,
|
||||||
config.x,
|
};
|
||||||
config.y,
|
|
||||||
{
|
|
||||||
nostroke: false,
|
|
||||||
axis: '0 0 1 1',
|
|
||||||
shade: true,
|
|
||||||
gutter: 1,
|
|
||||||
colors: config.colors || ['#2980b9']
|
|
||||||
});
|
|
||||||
|
|
||||||
function format(value, type) {
|
size.width = size.frameWidth - padding.left - padding.right;
|
||||||
switch (type) {
|
size.height = size.frameHeight - padding.top - padding.bottom;
|
||||||
case 'epoch':
|
|
||||||
return new Date(parseInt(value, 10) * 1000).toLocaleDateString();
|
|
||||||
case 'int':
|
|
||||||
return parseInt(value, 10);
|
|
||||||
default:
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Format the X axis.
|
var x = d3.time.scale()
|
||||||
|
.range([0, size.width]);
|
||||||
|
|
||||||
var n = 2;
|
var y = d3.scale.linear()
|
||||||
var ii = 0;
|
.range([size.height, 0]);
|
||||||
var text = l.axis[0].text.items;
|
|
||||||
for (var k in text) {
|
|
||||||
if (ii++ % n) {
|
|
||||||
text[k].attr({text: ''});
|
|
||||||
} else {
|
|
||||||
var cur = text[k].attr('text');
|
|
||||||
var str = format(cur, config.xformat);
|
|
||||||
text[k].attr({text: str});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show values on hover.
|
var xAxis = d3.svg.axis()
|
||||||
|
.scale(x)
|
||||||
|
.orient('bottom');
|
||||||
|
|
||||||
l.hoverColumn(function() {
|
var yAxis = d3.svg.axis()
|
||||||
this.tags = r.set();
|
.scale(y)
|
||||||
for (var yy = 0; yy < config.y.length; yy++) {
|
.orient('left');
|
||||||
var yvalue = 0;
|
|
||||||
|
var svg = d3.select('#' + config.hardpoint).append('svg')
|
||||||
|
.attr('width', size.frameWidth)
|
||||||
|
.attr('height', size.frameHeight)
|
||||||
|
.attr('class', 'chart');
|
||||||
|
|
||||||
|
var g = svg.append('g')
|
||||||
|
.attr('transform', fn('translate', padding.left, padding.top));
|
||||||
|
|
||||||
|
g.append('rect')
|
||||||
|
.attr('class', 'inner')
|
||||||
|
.attr('width', size.width)
|
||||||
|
.attr('height', size.height);
|
||||||
|
|
||||||
|
var line = d3.svg.line()
|
||||||
|
.x(function(d) { return x(d.date); })
|
||||||
|
.y(function(d) { return y(d.count); });
|
||||||
|
|
||||||
|
var data = [];
|
||||||
for (var ii = 0; ii < config.x[0].length; ii++) {
|
for (var ii = 0; ii < config.x[0].length; ii++) {
|
||||||
if (config.x[0][ii] > this.axis) {
|
data.push(
|
||||||
break;
|
{
|
||||||
}
|
date: new Date(config.x[0][ii] * 1000),
|
||||||
yvalue = format(config.y[yy][ii], config.yformat);
|
count: +config.y[0][ii]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var xvalue = format(this.axis, config.xformat);
|
x.domain(d3.extent(data, function(d) { return d.date; }));
|
||||||
|
|
||||||
var tag = r.tag(
|
var yex = d3.extent(data, function(d) { return d.count; });
|
||||||
this.x,
|
yex[0] = 0;
|
||||||
this.y[yy],
|
yex[1] = yex[1] * 1.05;
|
||||||
[xvalue, yvalue].join('\n'),
|
y.domain(yex);
|
||||||
180,
|
|
||||||
24);
|
|
||||||
tag
|
|
||||||
.insertBefore(this)
|
|
||||||
.attr([{fill : '#fff'}, {fill: '#000'}]);
|
|
||||||
|
|
||||||
this.tags.push(tag);
|
g.append('path')
|
||||||
}
|
.datum(data)
|
||||||
}, function() {
|
.attr('class', 'line')
|
||||||
this.tags && this.tags.remove();
|
.attr('d', line);
|
||||||
|
|
||||||
|
g.append('g')
|
||||||
|
.attr('class', 'x axis')
|
||||||
|
.attr('transform', fn('translate', 0, size.height))
|
||||||
|
.call(xAxis);
|
||||||
|
|
||||||
|
g.append('g')
|
||||||
|
.attr('class', 'y axis')
|
||||||
|
.attr('transform', fn('translate', 0, 0))
|
||||||
|
.call(yAxis);
|
||||||
|
|
||||||
|
var div = d3.select('body')
|
||||||
|
.append('div')
|
||||||
|
.attr('class', 'chart-tooltip')
|
||||||
|
.style('opacity', 0);
|
||||||
|
|
||||||
|
g.selectAll('dot')
|
||||||
|
.data(data)
|
||||||
|
.enter()
|
||||||
|
.append('circle')
|
||||||
|
.attr('class', 'point')
|
||||||
|
.attr('r', 3)
|
||||||
|
.attr('cx', function(d) { return x(d.date); })
|
||||||
|
.attr('cy', function(d) { return y(d.count); })
|
||||||
|
.on('mouseover', function(d) {
|
||||||
|
var d_y = d.date.getFullYear();
|
||||||
|
var d_m = d.date.getMonth();
|
||||||
|
var d_d = d.date.getDate();
|
||||||
|
|
||||||
|
div
|
||||||
|
.html(d_y + '-' + d_m + '-' + d_d + ': ' + d.count)
|
||||||
|
.style('opacity', 0.9)
|
||||||
|
.style('left', (d3.event.pageX - 60) + 'px')
|
||||||
|
.style('top', (d3.event.pageY - 38) + 'px');
|
||||||
|
})
|
||||||
|
.on('mouseout', function() {
|
||||||
|
div.style('opacity', 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue