зеркало из https://github.com/mozilla/treeherder.git
Major refactoring of analysis code.
Adding support for both JSON API and SQL access. Adding support for Talos dashboard Making behaviour much more configurable via analysis.cfg --HG-- branch : 1.0
This commit is contained in:
Родитель
423cb4d57a
Коммит
b4de33edd0
|
@ -0,0 +1,36 @@
|
|||
[main]
|
||||
base_hg_url = http://hg.mozilla.org
|
||||
base_graph_url = http://graphs-new.mozilla.org
|
||||
warning_history = warning_history.json
|
||||
pushdates = pushdates.json
|
||||
method = graphapi
|
||||
fore_window = 5
|
||||
back_window = 30
|
||||
threshold = 9
|
||||
machine_threshold = 15
|
||||
machine_history_size = 5
|
||||
graph_dir = graphs
|
||||
dashboard_dir = dashboard
|
||||
from_email = catlee@mozilla.com
|
||||
regression_emails = catlee@mozilla.com, dev-tree-management@lists.mozilla.org
|
||||
machine_emails = catlee@mozilla.com, anodelman@mozilla.com, joduinn@mozilla.com
|
||||
json = json/warnings.json
|
||||
|
||||
[dashboard]
|
||||
tests = Tp3, Txul, Tp3 (RSS), Tp3 (Memset), Tp3 Shutdown, Ts Shutdown, Ts, SVG
|
||||
|
||||
[os]
|
||||
WINNT 6.0 = Vista
|
||||
WINNT 5.1 = XP
|
||||
MacOSX Darwin 8.8.1 = Tiger
|
||||
MacOSX Darwin 9.2.2 = Leopard
|
||||
Ubuntu 7.10 = Linux
|
||||
|
||||
[Firefox]
|
||||
repo_path = mozilla-central
|
||||
|
||||
[Firefox3.5]
|
||||
repo_path = releases/mozilla-1.9.1
|
||||
|
||||
[TraceMonkey]
|
||||
repo_path = tracemonkey
|
|
@ -125,8 +125,14 @@ class TalosAnalyzer:
|
|||
else:
|
||||
m_t = 0
|
||||
|
||||
di.t = t
|
||||
if abs(m_t) >= machine_threshold:
|
||||
l = len(good_data)-1
|
||||
while l >= 0:
|
||||
dl = good_data[l]
|
||||
if dl.machine_id != di.machine_id:
|
||||
di.last_other = dl
|
||||
break
|
||||
l -= 1
|
||||
yield di, "machine"
|
||||
elif abs(t) <= threshold:
|
||||
good_data.append(di)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
from graphsdb import db
|
||||
|
||||
from analyze import PerfDatum
|
||||
|
||||
def getTestData(branch_id, os_id, test_id, start_date):
|
||||
cursor = db.cursor()
|
||||
sql = """SELECT
|
||||
machine_id,
|
||||
ref_build_id,
|
||||
date_run,
|
||||
average,
|
||||
ref_changeset
|
||||
FROM
|
||||
test_runs INNER JOIN machines ON (machine_id = machines.id)
|
||||
INNER JOIN builds ON (build_id = builds.id)
|
||||
WHERE
|
||||
test_id = %(test_id)s AND
|
||||
os_id = %(os_id)s AND
|
||||
branch_id = %(branch_id)s AND
|
||||
date_run > %(start_date)s AND
|
||||
machines.name NOT LIKE '%%stage%%'
|
||||
"""
|
||||
cursor.execute(sql, locals())
|
||||
data = []
|
||||
for row in cursor:
|
||||
machine_id, ref_build_id, date_run, average, ref_changeset = row
|
||||
if average is None:
|
||||
continue
|
||||
t = date_run
|
||||
d = PerfDatum(machine_id, date_run, average, ref_build_id, t, ref_changeset)
|
||||
data.append(d)
|
||||
return data
|
||||
|
||||
def getTestSeries(branches, start_date, test_names):
|
||||
# Find all the Branch/OS/Test combinations
|
||||
branch_places = ",".join(["%s"] * len(branches))
|
||||
test_places = ",".join(["%s"] * len(test_names))
|
||||
sql = """SELECT DISTINCT
|
||||
branch_id,
|
||||
branches.name AS branch_name,
|
||||
os_id,
|
||||
os_list.name AS os_name,
|
||||
test_id,
|
||||
tests.name AS test_name
|
||||
FROM
|
||||
test_runs INNER JOIN machines ON (machine_id = machines.id)
|
||||
INNER JOIN builds ON (build_id = builds.id)
|
||||
INNER JOIN branches ON (branch_id = branches.id)
|
||||
INNER JOIN os_list ON (os_id = os_list.id)
|
||||
INNER JOIN tests ON (test_id = tests.id)
|
||||
WHERE
|
||||
date_run > %%s AND
|
||||
branches.name IN (%(branch_places)s) AND
|
||||
machines.name NOT LIKE '%%%%stage%%%%'
|
||||
"""
|
||||
if len(test_names) > 0:
|
||||
sql += "AND tests.name IN (%(test_places)s)"
|
||||
sql = sql % locals()
|
||||
|
||||
cursor = db.cursor()
|
||||
args = [start_date] + branches + test_names
|
||||
cursor.execute(sql, args)
|
||||
return cursor.fetchall()
|
||||
|
||||
def getMachinesForTest(branch_id, test_id, os_id):
|
||||
sql = """SELECT DISTINCT
|
||||
machine_id
|
||||
FROM
|
||||
test_runs INNER JOIN machines ON (machine_id = machines.id)
|
||||
INNER JOIN tests ON (test_id = tests.id)
|
||||
INNER JOIN builds ON (build_id = builds.id)
|
||||
WHERE
|
||||
branch_id = %(branch_id)s AND
|
||||
test_id = %(test_id)s AND
|
||||
os_id = %(os_id)s AND
|
||||
machines.name NOT LIKE '%%stage%%'
|
||||
"""
|
||||
cursor = db.cursor()
|
||||
cursor.execute(sql, locals())
|
||||
return [row[0] for row in cursor.fetchall()]
|
||||
|
||||
def getMachineName(machine_id):
|
||||
sql = """SELECT
|
||||
name
|
||||
FROM
|
||||
machines
|
||||
WHERE
|
||||
id = %(machine_id)s
|
||||
"""
|
||||
cursor = db.cursor()
|
||||
cursor.execute(sql, locals())
|
||||
return cursor.fetchall()[0][0]
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
import logging as log
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
import urllib, os, sys
|
||||
|
||||
from analyze import PerfDatum
|
||||
|
||||
class TestSeries:
|
||||
def __init__(self, branch_id, branch_name, os_id, os_name, test_id, test_name):
|
||||
self.branch_id = branch_id
|
||||
self.branch_name = branch_name
|
||||
self.os_id = os_id
|
||||
self.os_name = os_name
|
||||
self.test_id = test_id
|
||||
self.test_name = test_name
|
||||
|
||||
def __eq__(self, o):
|
||||
return (self.branch_id, self.os_id, self.test_id) == (o.branch_id, o.os_id, o.test_id)
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.branch_id, self.os_id, self.test_id))
|
||||
|
||||
class GraphAPISource:
|
||||
def __init__(self, baseurl):
|
||||
self.baseurl = baseurl
|
||||
|
||||
def getTestSeries(self, branches, start_date, test_names):
|
||||
url = "%s/%s" % (self.baseurl, "test")
|
||||
try:
|
||||
log.debug("Getting %s", url)
|
||||
req = urllib.urlopen(url)
|
||||
tests = json.load(req)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
log.exception("Couldn't load or parse %s", url)
|
||||
return []
|
||||
|
||||
if tests['stat'] != "ok":
|
||||
log.warn("Test status not ok: %s", tests['stat'])
|
||||
return []
|
||||
|
||||
retval = []
|
||||
machines_by_branch = {}
|
||||
machine_names = {}
|
||||
for test in tests['tests']:
|
||||
machine_info = test['machine']
|
||||
machine_id = machine_info['id']
|
||||
machine_name = machine_info['name']
|
||||
os_info = test['os']
|
||||
os_id = os_info['id']
|
||||
branch_info = test['branch']
|
||||
branch_id = branch_info['id']
|
||||
if branch_info['name'] not in branches:
|
||||
continue
|
||||
|
||||
test_id = test['id']
|
||||
test_name = test['name']
|
||||
|
||||
if test_names and test_name not in test_names:
|
||||
continue
|
||||
|
||||
# Skip NoChrome tests
|
||||
if "NoChrome" in test_name:
|
||||
continue
|
||||
|
||||
# Skip Fast Cycle tests
|
||||
if "Fast Cycle" in test_name:
|
||||
continue
|
||||
|
||||
# Skip GFX
|
||||
if "GFX" in test_name:
|
||||
continue
|
||||
|
||||
# Skip talos-rev2 slaves
|
||||
if "talos-rev2" in machine_name:
|
||||
continue
|
||||
|
||||
series = TestSeries(branch_id, branch_info['name'],
|
||||
os_id, os_info['name'],
|
||||
test_id, test_name)
|
||||
if series not in retval:
|
||||
retval.append(series)
|
||||
|
||||
if series not in machines_by_branch:
|
||||
machines_by_branch[series] = []
|
||||
if machine_id not in machines_by_branch[series]:
|
||||
machines_by_branch[series].append(machine_id)
|
||||
if machine_id not in machine_names:
|
||||
machine_names[machine_id] = machine_info['name']
|
||||
self.machines_by_branch = machines_by_branch
|
||||
self.machine_names = machine_names
|
||||
return retval
|
||||
|
||||
def getTestData(self, series, start_time):
|
||||
base = self.baseurl
|
||||
retval = []
|
||||
seen = {}
|
||||
for machine_id in self.machines_by_branch[series]:
|
||||
test_id = series.test_id
|
||||
branch_id = series.branch_id
|
||||
machine_id = machine_id
|
||||
url = "%(base)s/test/runs?id=%(test_id)s&branchid=%(branch_id)s&machineid=%(machine_id)s" % locals()
|
||||
try:
|
||||
log.debug("Getting %s", url)
|
||||
req = urllib.urlopen(url)
|
||||
results = json.load(req)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
log.exception("Couldn't load or parse %s", url)
|
||||
continue
|
||||
|
||||
if 'test_runs' not in results:
|
||||
log.debug("No data from %s", url)
|
||||
continue
|
||||
|
||||
for item in results['test_runs']:
|
||||
testrunid, build, date, average, run_number, annotations = item
|
||||
if average is None:
|
||||
continue
|
||||
d = PerfDatum(machine_id, date, average, build[1], date, build[2])
|
||||
d.run_number = run_number
|
||||
retval.append(d)
|
||||
t = (d.buildid, date, average, machine_id)
|
||||
#if t in seen:
|
||||
#if seen[t].run_number == run_number:
|
||||
#continue
|
||||
#log.error("%s %s %s", seen[t], seen[t].machine_id, seen[t].run_number)
|
||||
#log.error("%s %s %s", d, d.machine_id, d.run_number)
|
||||
#log.error(url)
|
||||
#else:
|
||||
#seen[t] = d
|
||||
|
||||
return retval
|
||||
|
||||
def getMachinesForTest(self, series):
|
||||
return self.machines_by_branch[series]
|
||||
|
||||
def getMachineName(self, machine_id):
|
||||
return self.machine_names[machine_id]
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,47 @@
|
|||
/* vim: set sw=2 ts=2 sts=2 tw=78 expandtab : */
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.testdiv {
|
||||
border: solid 1px black;
|
||||
-moz-border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
min-height: 300px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.platformdivwrapper {
|
||||
display: inline-block;
|
||||
;
|
||||
}
|
||||
|
||||
.platformdiv {
|
||||
width: 360px;
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align : center;
|
||||
font-size: 110%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 100%;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
#footer > p {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 150%;
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
// vim: set sw=2 ts=2 sts=2 tw=78 expandtab :
|
||||
DEFAULT_TREE = "Firefox3.5";
|
||||
gDataUrlBase = "http://graphs.mozilla.org/server/getdata.cgi?";
|
||||
|
||||
// We use Math.floor all the time
|
||||
const fl = Math.floor;
|
||||
|
||||
function init() {
|
||||
|
||||
parseArgs();
|
||||
|
||||
// Which tree are we monitoring?
|
||||
gTree = gArgs["tree"] || DEFAULT_TREE;
|
||||
gTests = gData[gTree];
|
||||
buildTreesHeaderAndFooter();
|
||||
|
||||
buildAllGraphs();
|
||||
|
||||
document.getElementById("fetchtimetext").textContent = "Data pulled: " + gFetchTime;
|
||||
}
|
||||
|
||||
function buildAllGraphs() {
|
||||
|
||||
var topLevelDiv = document.getElementById("tests");
|
||||
|
||||
for (test in gTests) {
|
||||
var testDiv = document.createElement("div");
|
||||
var test_id = String(gTests[test]._testid);
|
||||
testDiv.setAttribute("class", "testdiv");
|
||||
testDiv.setAttribute("id", test_id);
|
||||
|
||||
var header = document.createElement("h1");
|
||||
header.textContent = nicename(test);
|
||||
testDiv.appendChild(header);
|
||||
|
||||
topLevelDiv.appendChild(testDiv);
|
||||
|
||||
for (platform in gTests[test]) {
|
||||
// Skip _fields, since those aren't results, they're metadata
|
||||
if (platform.charAt(0) === '_')
|
||||
continue;
|
||||
var wrapperDiv = document.createElement("div");
|
||||
var platform_id = String(gTests[test][platform]._platformid);
|
||||
wrapperDiv.setAttribute("class", "platformdivwrapper");
|
||||
wrapperDiv.setAttribute("id", platform_id + "-" + test_id + "wrapper");
|
||||
testDiv.appendChild(wrapperDiv);
|
||||
|
||||
var h2 = document.createElement("h2");
|
||||
h2.textContent = nicename(platform);
|
||||
wrapperDiv.appendChild(h2);
|
||||
|
||||
// Construct the link to the real graph server
|
||||
var a = document.createElement("a");
|
||||
a.href = gTests[test][platform]._graphURL;
|
||||
a.title = "Click for full graph server on these data sets"
|
||||
wrapperDiv.appendChild(a);
|
||||
|
||||
// Construct the graph with flot
|
||||
var flotDiv = document.createElement("div");
|
||||
flotDiv.setAttribute("class", "platformdiv");
|
||||
flotDiv.setAttribute("id", platform_id+"-"+test_id);
|
||||
a.appendChild(flotDiv);
|
||||
buildGraphForSet(test, platform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function buildGraphForSet(test, platform) {
|
||||
|
||||
var results = gTests[test][platform];
|
||||
|
||||
var test_id = String(gTests[test]._testid);
|
||||
var platform_id = String(gTests[test][platform]._platformid);
|
||||
|
||||
var d = new Date();
|
||||
d.setDate(d.getDate()-7);
|
||||
var sevenDaysAgo = d.valueOf() / 1000;
|
||||
|
||||
var min = Infinity;
|
||||
var max = 0;
|
||||
var seriesCount = 0;
|
||||
|
||||
var plotSeries = [];
|
||||
|
||||
for(r in results) {
|
||||
// Skip _fields, since those aren't results, they're metadata
|
||||
if (r.charAt(0) === '_')
|
||||
continue;
|
||||
|
||||
var boxresults = results[r];
|
||||
|
||||
// Skip valid boxes that aren't returning results
|
||||
if(!boxresults.results.length)
|
||||
continue;
|
||||
|
||||
seriesCount++;
|
||||
|
||||
max = fl(Math.max(max, boxresults.stats[1]));
|
||||
min = fl(Math.min(min, boxresults.stats[2]));
|
||||
|
||||
// data is given timestamp1,data1,timestamp2,data2... which is
|
||||
// almost how flot wants it, [[time1,data1],[time2,data2]]
|
||||
var rawresults = [];
|
||||
for(i = 0; i < boxresults.results.length; i += 2) {
|
||||
rawresults.push([ boxresults.results[i]*1000, boxresults.results[i+1] ]);
|
||||
}
|
||||
plotSeries.push({
|
||||
//color: color or number,
|
||||
//label: string,
|
||||
lines: {
|
||||
lineWidth: 1
|
||||
},
|
||||
//bars: specific bars options,
|
||||
//points: specific points options,
|
||||
//xaxis: 1,
|
||||
//yaxis: 1,
|
||||
shadowSize: 2,
|
||||
data: rawresults
|
||||
});
|
||||
}
|
||||
|
||||
$.plot($("#" + platform_id + "-" + test_id), plotSeries, {
|
||||
xaxis : {
|
||||
mode: "time",
|
||||
tickSize: [1, "day"],
|
||||
timeformat: "%m/%d",
|
||||
min: Date.now() - 86400*7*1000,
|
||||
max: Date.now()
|
||||
},
|
||||
yaxis : {
|
||||
min: 0.8 * min,
|
||||
max: 1.2 * max
|
||||
},
|
||||
grid : {
|
||||
borderWidth: 0.2
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Map ugly names to nice ones
|
||||
*/
|
||||
function nicename (uglyname) {
|
||||
switch (uglyname.toUpperCase()) {
|
||||
case "TS" :
|
||||
return "Ts - Browser Startup Time (ms)"
|
||||
case "TS SHUTDOWN" :
|
||||
return "Ts Shutdown - Browser Shutdown Time after Ts test (ms)"
|
||||
case "TXUL" :
|
||||
return "Txul/Twinopen - New Window Creation Time (ms)"
|
||||
case "TP3" :
|
||||
return "Tp3 - Average Page Load Time (ms)"
|
||||
case "TP3 (RSS)" :
|
||||
return "Tp_RSS/Working Set - Memory usage during Tp runs (bytes)";
|
||||
case "TP3 SHUTDOWN" :
|
||||
return "Tp3 Shutdown - Browser Shutdown Time after Tp3 test (ms)";
|
||||
case "SVG" :
|
||||
return "Tsvg - SVG Rendering Speed"
|
||||
default :
|
||||
return uglyname;
|
||||
}
|
||||
}
|
||||
|
||||
function parseArgs() {
|
||||
gArgs = {};
|
||||
|
||||
// Loop over the args passed in, if any.
|
||||
var pairs = content.location.search.slice(1).split("&");
|
||||
for(pair in pairs) {
|
||||
var name, value;
|
||||
[name, value] = pairs[pair].split('=');
|
||||
gArgs[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
function buildTreesHeaderAndFooter() {
|
||||
|
||||
// First, update the "current tree" text in each place
|
||||
document.getElementById("header").textContent = nicename(gTree);
|
||||
document.title = nicename(gTree) + " - " + document.title;
|
||||
document.getElementById("currenttree").textContent = nicename(gTree);
|
||||
|
||||
var otherTrees = document.getElementById("othertrees");
|
||||
for(tree in gData) {
|
||||
if(tree === gTree) // Skip the current tree
|
||||
continue;
|
||||
|
||||
// Remember the params we were given, just "fix" the tree, then
|
||||
// build a URL out of it
|
||||
var argString = "?";
|
||||
for (arg in gArgs) {
|
||||
if(arg != "tree" && gArgs[arg])
|
||||
argString += arg + "=" + gArgs[arg] + "&";
|
||||
}
|
||||
if (tree != DEFAULT_TREE)
|
||||
argString += "tree=" + tree;
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.textContent = nicename(tree);
|
||||
a.href = argString;
|
||||
otherTrees.appendChild(a);
|
||||
|
||||
otherTrees.appendChild(document.createTextNode(" "));
|
||||
}
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 710 B |
|
@ -0,0 +1,49 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
|
||||
<!--
|
||||
|
||||
Hi there. Some things you might want to know:
|
||||
|
||||
- append a chs parameter to specify your own chart size, but the
|
||||
google graphs API won't let you have more than 300,000 pixels,
|
||||
which is not quite 640x480. If you like those aspect ratios, I
|
||||
suggest 600x450 or 400x300.
|
||||
|
||||
e.g. http://people.mozilla.org/~johnath/pdb2/?chs=200x150
|
||||
|
||||
- The source, if you want to hack on it, is in a public hg
|
||||
repository here:
|
||||
|
||||
http://hg.mozilla.org/users/jnightingale_mozilla.com/dashboard/
|
||||
|
||||
Hope you like it.
|
||||
|
||||
Johnathan (johnath@mozilla.com)
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Perf Dashboard v2.0</title>
|
||||
<meta http-equiv="refresh" content="300">
|
||||
<link rel="stylesheet" href="dashboard.css" type="text/css" media="all"/>
|
||||
|
||||
<!-- Favicon courtesy of FAMFAMFAM silk - http://famfamfam.com/ -->
|
||||
<link rel="shortcut icon" href="favicon.png"/>
|
||||
<script src="jquery/jquery-1.3.1.min.js" type="text/javascript"></script>
|
||||
<script src="flot/jquery.flot.pack.js" type="text/javascript"></script>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="flot/excanvas.pack.js"></script><![endif]-->
|
||||
<script src="dashboard.js"></script>
|
||||
<script src="testdata.js"></script>
|
||||
</head>
|
||||
<body onload="init();">
|
||||
<h1 class="title" id="header"></h1>
|
||||
<div id="tests">
|
||||
</div>
|
||||
<div id="footer">
|
||||
<p id="trees"><b>Currently viewing data for: <span id="currenttree"></span></b> — Other trees: <span id="othertrees"></span></p>
|
||||
<p id="legend">All graphs show last 7 days — Coloured lines represent different talos machines — Click through for graph server — Refreshes every 5 minutes</p>
|
||||
<p id="fetchtimetext"></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,701 @@
|
|||
Flot Reference
|
||||
--------------
|
||||
|
||||
Consider a call to the plot function:
|
||||
|
||||
var plot = $.plot(placeholder, data, options)
|
||||
|
||||
The placeholder is a jQuery object that the plot will be put into.
|
||||
This placeholder needs to have its width and height set as explained
|
||||
in the README (go read that now if you haven't, it's short). The plot
|
||||
will modify some properties of the placeholder so it's recommended you
|
||||
simply pass in a div that you don't use for anything else.
|
||||
|
||||
The format of the data is documented below, as is the available
|
||||
options. The "plot" object returned has some methods you can call.
|
||||
These are documented separately below.
|
||||
|
||||
Note that in general Flot gives no guarantees if you change any of the
|
||||
objects you pass in to the plot function or get out of it since
|
||||
they're not necessarily deep-copied.
|
||||
|
||||
|
||||
Data Format
|
||||
-----------
|
||||
|
||||
The data is an array of data series:
|
||||
|
||||
[ series1, series2, ... ]
|
||||
|
||||
A series can either be raw data or an object with properties. The raw
|
||||
data format is an array of points:
|
||||
|
||||
[ [x1, y1], [x2, y2], ... ]
|
||||
|
||||
E.g.
|
||||
|
||||
[ [1, 3], [2, 14.01], [3.5, 3.14] ]
|
||||
|
||||
Note that to simplify the internal logic in Flot both the x and y
|
||||
values must be numbers, even if specifying time series (see below for
|
||||
how to do this). This is a common problem because you might retrieve
|
||||
data from the database and serialize them directly to JSON without
|
||||
noticing the wrong type.
|
||||
|
||||
If a null is specified as a point or if one of the coordinates is null
|
||||
or couldn't be converted to a number, the point is ignored when
|
||||
drawing. As a special case, a null value for lines is interpreted as a
|
||||
line segment end, i.e. the point before and after the null value are
|
||||
not connected.
|
||||
|
||||
The format of a single series object is as follows:
|
||||
|
||||
{
|
||||
color: color or number,
|
||||
data: rawdata,
|
||||
label: string,
|
||||
lines: specific lines options,
|
||||
bars: specific bars options,
|
||||
points: specific points options,
|
||||
xaxis: 1 or 2,
|
||||
yaxis: 1 or 2,
|
||||
shadowSize: number
|
||||
}
|
||||
|
||||
You don't have to specify any of them except the data, the rest are
|
||||
options that will get default values. Typically you'd only specify
|
||||
label and data, like this:
|
||||
|
||||
{
|
||||
label: "y = 3",
|
||||
data: [[0, 3], [10, 3]]
|
||||
}
|
||||
|
||||
The label is used for the legend, if you don't specify one, the series
|
||||
will not show up in the legend.
|
||||
|
||||
If you don't specify color, the series will get a color from the
|
||||
auto-generated colors. The color is either a CSS color specification
|
||||
(like "rgb(255, 100, 123)") or an integer that specifies which of
|
||||
auto-generated colors to select, e.g. 0 will get color no. 0, etc.
|
||||
|
||||
The latter is mostly useful if you let the user add and remove series,
|
||||
in which case you can hard-code the color index to prevent the colors
|
||||
from jumping around between the series.
|
||||
|
||||
The "xaxis" and "yaxis" options specify which axis to use, specify 2
|
||||
to get the secondary axis (x axis at top or y axis to the right).
|
||||
E.g., you can use this to make a dual axis plot by specifying
|
||||
{ yaxis: 2 } for one data series.
|
||||
|
||||
The rest of the options are all documented below as they are the same
|
||||
as the default options passed in via the options parameter in the plot
|
||||
commmand. When you specify them for a specific data series, they will
|
||||
override the default options for the plot for that data series.
|
||||
|
||||
Here's a complete example of a simple data specification:
|
||||
|
||||
[ { label: "Foo", data: [ [10, 1], [17, -14], [30, 5] ] },
|
||||
{ label: "Bar", data: [ [11, 13], [19, 11], [30, -7] ] } ]
|
||||
|
||||
|
||||
Plot Options
|
||||
------------
|
||||
|
||||
All options are completely optional. They are documented individually
|
||||
below, to change them you just specify them in an object, e.g.
|
||||
|
||||
var options = {
|
||||
lines: { show: true },
|
||||
points: { show: true }
|
||||
};
|
||||
|
||||
$.plot(placeholder, data, options);
|
||||
|
||||
|
||||
Customizing the legend
|
||||
======================
|
||||
|
||||
legend: {
|
||||
show: boolean
|
||||
labelFormatter: null or (fn: string -> string)
|
||||
labelBoxBorderColor: color
|
||||
noColumns: number
|
||||
position: "ne" or "nw" or "se" or "sw"
|
||||
margin: number of pixels
|
||||
backgroundColor: null or color
|
||||
backgroundOpacity: number in 0.0 - 1.0
|
||||
container: null or jQuery object
|
||||
}
|
||||
|
||||
The legend is generated as a table with the data series labels and
|
||||
small label boxes with the color of the series. If you want to format
|
||||
the labels in some way, e.g. make them to links, you can pass in a
|
||||
function for "labelFormatter". Here's an example that makes them
|
||||
clickable:
|
||||
|
||||
labelFormatter: function(label) {
|
||||
return '<a href="' + label + '">' + label + '</a>';
|
||||
}
|
||||
|
||||
"noColumns" is the number of columns to divide the legend table into.
|
||||
"position" specifies the overall placement of the legend within the
|
||||
plot (top-right, top-left, etc.) and margin the distance to the plot
|
||||
edge. "backgroundColor" and "backgroundOpacity" specifies the
|
||||
background. The default is a partly transparent auto-detected
|
||||
background.
|
||||
|
||||
If you want the legend to appear somewhere else in the DOM, you can
|
||||
specify "container" as a jQuery object to put the legend table into.
|
||||
The "position" and "margin" etc. options will then be ignored. Note
|
||||
that it will overwrite the contents of the container.
|
||||
|
||||
|
||||
|
||||
Customizing the axes
|
||||
====================
|
||||
|
||||
xaxis, yaxis, x2axis, y2axis: {
|
||||
mode: null or "time"
|
||||
min: null or number
|
||||
max: null or number
|
||||
autoscaleMargin: null or number
|
||||
labelWidth: null or number
|
||||
labelHeight: null or number
|
||||
|
||||
ticks: null or number or ticks array or (fn: range -> ticks array)
|
||||
tickSize: number or array
|
||||
minTickSize: number or array
|
||||
tickFormatter: (fn: number, object -> string) or string
|
||||
tickDecimals: null or number
|
||||
}
|
||||
|
||||
The axes have the same kind of options. The "mode" option
|
||||
determines how the data is interpreted, the default of null means as
|
||||
decimal numbers. Use "time" for time series data, see the next section.
|
||||
|
||||
The options "min"/"max" are the precise minimum/maximum value on the
|
||||
scale. If you don't specify either of them, a value will automatically
|
||||
be chosen based on the minimum/maximum data values.
|
||||
|
||||
The "autoscaleMargin" is a bit esoteric: it's the fraction of margin
|
||||
that the scaling algorithm will add to avoid that the outermost points
|
||||
ends up on the grid border. Note that this margin is only applied
|
||||
when a min or max value is not explicitly set. If a margin is
|
||||
specified, the plot will furthermore extend the axis end-point to the
|
||||
nearest whole tick. The default value is "null" for the x axis and
|
||||
0.02 for the y axis which seems appropriate for most cases.
|
||||
|
||||
"labelWidth" and "labelHeight" specifies the maximum size of the tick
|
||||
labels in pixels. They're useful in case you need to align several
|
||||
plots.
|
||||
|
||||
The rest of the options deal with the ticks.
|
||||
|
||||
If you don't specify any ticks, a tick generator algorithm will make
|
||||
some for you. The algorithm has two passes. It first estimates how
|
||||
many ticks would be reasonable and uses this number to compute a nice
|
||||
round tick interval size. Then it generates the ticks.
|
||||
|
||||
You can specify how many ticks the algorithm aims for by setting
|
||||
"ticks" to a number. The algorithm always tries to generate reasonably
|
||||
round tick values so even if you ask for three ticks, you might get
|
||||
five if that fits better with the rounding. If you don't want ticks,
|
||||
set "ticks" to 0 or an empty array.
|
||||
|
||||
Another option is to skip the rounding part and directly set the tick
|
||||
interval size with "tickSize". If you set it to 2, you'll get ticks at
|
||||
2, 4, 6, etc. Alternatively, you can specify that you just don't want
|
||||
ticks at a size less than a specific tick size with "minTickSize".
|
||||
Note that for time series, the format is an array like [2, "month"],
|
||||
see the next section.
|
||||
|
||||
If you want to completely override the tick algorithm, you can specify
|
||||
an array for "ticks", either like this:
|
||||
|
||||
ticks: [0, 1.2, 2.4]
|
||||
|
||||
Or like this (you can mix the two if you like):
|
||||
|
||||
ticks: [[0, "zero"], [1.2, "one mark"], [2.4, "two marks"]]
|
||||
|
||||
For extra flexibility you can specify a function as the "ticks"
|
||||
parameter. The function will be called with an object with the axis
|
||||
min and max and should return a ticks array. Here's a simplistic tick
|
||||
generator that spits out intervals of pi, suitable for use on the x
|
||||
axis for trigonometric functions:
|
||||
|
||||
function piTickGenerator(axis) {
|
||||
var res = [], i = Math.floor(axis.min / Math.PI);
|
||||
do {
|
||||
var v = i * Math.PI;
|
||||
res.push([v, i + "\u03c0"]);
|
||||
++i;
|
||||
} while (v < axis.max);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
You can control how the ticks look like with "tickDecimals", the
|
||||
number of decimals to display (default is auto-detected).
|
||||
|
||||
Alternatively, for ultimate control over how ticks look like you can
|
||||
provide a function to "tickFormatter". The function is passed two
|
||||
parameters, the tick value and an "axis" object with information, and
|
||||
should return a string. The default formatter looks like this:
|
||||
|
||||
function formatter(val, axis) {
|
||||
return val.toFixed(axis.tickDecimals);
|
||||
}
|
||||
|
||||
The axis object has "min" and "max" with the range of the axis,
|
||||
"tickDecimals" with the number of decimals to round the value to and
|
||||
"tickSize" with the size of the interval between ticks as calculated
|
||||
by the automatic axis scaling algorithm (or specified by you). Here's
|
||||
an example of a custom formatter:
|
||||
|
||||
function suffixFormatter(val, axis) {
|
||||
if (val > 1000000)
|
||||
return (val / 1000000).toFixed(axis.tickDecimals) + " MB";
|
||||
else if (val > 1000)
|
||||
return (val / 1000).toFixed(axis.tickDecimals) + " kB";
|
||||
else
|
||||
return val.toFixed(axis.tickDecimals) + " B";
|
||||
}
|
||||
|
||||
|
||||
Time series data
|
||||
================
|
||||
|
||||
Time series are a bit more difficult than scalar data because
|
||||
calendars don't follow a simple base 10 system. For many cases, Flot
|
||||
abstracts most of this away, but it can still be a bit difficult to
|
||||
get the data into Flot. So we'll first discuss the data format.
|
||||
|
||||
The time series support in Flot is based on Javascript timestamps,
|
||||
i.e. everywhere a time value is expected or handed over, a Javascript
|
||||
timestamp number is used. This is a number, not a Date object. A
|
||||
Javascript timestamp is the number of milliseconds since January 1,
|
||||
1970 00:00:00 UTC. This is almost the same as Unix timestamps, except it's
|
||||
in milliseconds, so remember to multiply by 1000!
|
||||
|
||||
You can see a timestamp like this
|
||||
|
||||
alert((new Date()).getTime())
|
||||
|
||||
Normally you want the timestamps to be displayed according to a
|
||||
certain time zone, usually the time zone in which the data has been
|
||||
produced. However, Flot always displays timestamps according to UTC.
|
||||
It has to as the only alternative with core Javascript is to interpret
|
||||
the timestamps according to the time zone that the visitor is in,
|
||||
which means that the ticks will shift unpredictably with the time zone
|
||||
and daylight savings of each visitor.
|
||||
|
||||
So given that there's no good support for custom time zones in
|
||||
Javascript, you'll have to take care of this server-side.
|
||||
|
||||
The easiest way to think about it is to pretend that the data
|
||||
production time zone is UTC, even if it isn't. So if you have a
|
||||
datapoint at 2002-02-20 08:00, you can generate a timestamp for eight
|
||||
o'clock UTC even if it really happened eight o'clock UTC+0200.
|
||||
|
||||
In PHP you can get an appropriate timestamp with
|
||||
'strtotime("2002-02-20 UTC") * 1000', in Python with
|
||||
'calendar.timegm(datetime_object.timetuple()) * 1000', in .NET with
|
||||
something like:
|
||||
|
||||
public static int GetJavascriptTimestamp(System.DateTime input)
|
||||
{
|
||||
System.TimeSpan span = new System.TimeSpan(System.DateTime.Parse("1/1/1970").Ticks);
|
||||
System.DateTime time = input.Subtract(span);
|
||||
return (long)(time.Ticks / 10000);
|
||||
}
|
||||
|
||||
Javascript also has some support for parsing date strings, so it is
|
||||
possible to generate the timestamps manually client-side.
|
||||
|
||||
If you've already got the real UTC timestamp, it's too late to use the
|
||||
pretend trick described above. But you can fix up the timestamps by
|
||||
adding the time zone offset, e.g. for UTC+0200 you would add 2 hours
|
||||
to the UTC timestamp you got. Then it'll look right on the plot. Most
|
||||
programming environments have some means of getting the timezone
|
||||
offset for a specific date.
|
||||
|
||||
Once you've got the timestamps into the data and specified "time" as
|
||||
the axis mode, Flot will automatically generate relevant ticks and
|
||||
format them. As always, you can tweak the ticks via the "ticks" option
|
||||
- just remember that the values should be timestamps (numbers), not
|
||||
Date objects.
|
||||
|
||||
Tick generation and formatting can also be controlled separately
|
||||
through the following axis options:
|
||||
|
||||
xaxis, yaxis: {
|
||||
minTickSize
|
||||
timeformat: null or format string
|
||||
monthNames: null or array of size 12 of strings
|
||||
}
|
||||
|
||||
Here "timeformat" is a format string to use. You might use it like
|
||||
this:
|
||||
|
||||
xaxis: {
|
||||
mode: "time",
|
||||
timeformat: "%y/%m/%d"
|
||||
}
|
||||
|
||||
This will result in tick labels like "2000/12/24". The following
|
||||
specifiers are supported
|
||||
|
||||
%h': hours
|
||||
%H': hours (left-padded with a zero)
|
||||
%M': minutes (left-padded with a zero)
|
||||
%S': seconds (left-padded with a zero)
|
||||
%d': day of month (1-31)
|
||||
%m': month (1-12)
|
||||
%y': year (four digits)
|
||||
%b': month name (customizable)
|
||||
|
||||
You can customize the month names with the "monthNames" option. For
|
||||
instance, for Danish you might specify:
|
||||
|
||||
monthName: ["jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec"]
|
||||
|
||||
If everything else fails, you can control the formatting by specifying
|
||||
a custom tick formatter function as usual. Here's a simple example
|
||||
which will format December 24 as 24/12:
|
||||
|
||||
tickFormatter: function (val, axis) {
|
||||
var d = new Date(val);
|
||||
return d.getUTCDate() + "/" + (d.getUTCMonth() + 1);
|
||||
}
|
||||
|
||||
Note that for the time mode "tickSize" and "minTickSize" are a bit
|
||||
special in that they are arrays on the form "[value, unit]" where unit
|
||||
is one of "second", "minute", "hour", "day", "month" and "year". So
|
||||
you can specify
|
||||
|
||||
minTickSize: [1, "month"]
|
||||
|
||||
to get a tick interval size of at least 1 month and correspondingly,
|
||||
if axis.tickSize is [2, "day"] in the tick formatter, the ticks have
|
||||
been produced with two days in-between.
|
||||
|
||||
|
||||
|
||||
Customizing the data series
|
||||
===========================
|
||||
|
||||
lines, points, bars: {
|
||||
show: boolean
|
||||
lineWidth: number
|
||||
fill: boolean or number
|
||||
fillColor: color
|
||||
}
|
||||
|
||||
points: {
|
||||
radius: number
|
||||
}
|
||||
|
||||
bars: {
|
||||
barWidth: number
|
||||
align: "left" or "center"
|
||||
}
|
||||
|
||||
colors: [ color1, color2, ... ]
|
||||
|
||||
shadowSize: number
|
||||
|
||||
The most important options are "lines", "points" and "bars" that
|
||||
specifies whether and how lines, points and bars should be shown for
|
||||
each data series. You can specify them independently of each other,
|
||||
and Flot will happily draw each of them in turn, e.g.
|
||||
|
||||
var options = {
|
||||
lines: { show: true, fill: true, fillColor: "rgba(255, 255, 255, 0.8)" },
|
||||
points: { show: true, fill: false }
|
||||
};
|
||||
|
||||
"lineWidth" is the thickness of the line or outline in pixels.
|
||||
|
||||
"fill" is whether the shape should be filled. For lines, this produces
|
||||
area graphs. You can use "fillColor" to specify the color of the fill.
|
||||
If "fillColor" evaluates to false (default for everything except
|
||||
points), the fill color is auto-set to the color of the data series.
|
||||
You can adjust the opacity of the fill by setting fill to a number
|
||||
between 0 (fully transparent) and 1 (fully opaque).
|
||||
|
||||
"barWidth" is the width of the bars in units of the x axis, contrary
|
||||
to most other measures that are specified in pixels. For instance, for
|
||||
time series the unit is milliseconds so 24 * 60 * 60 * 1000 produces
|
||||
bars with the width of a day. "align" specifies whether a bar should
|
||||
be left-aligned (default) or centered on top of the value it
|
||||
represents.
|
||||
|
||||
The "colors" array specifies a default color theme to get colors for
|
||||
the data series from. You can specify as many colors as you like, like
|
||||
this:
|
||||
|
||||
colors: ["#d18b2c", "#dba255", "#919733"]
|
||||
|
||||
If there are more data series than colors, Flot will try to generate
|
||||
extra colors by lightening and darkening colors in the theme.
|
||||
|
||||
"shadowSize" is the default size of shadows in pixels. Set it to 0 to
|
||||
remove shadows.
|
||||
|
||||
|
||||
Customizing the grid
|
||||
====================
|
||||
|
||||
grid: {
|
||||
color: color
|
||||
backgroundColor: color or null
|
||||
tickColor: color
|
||||
labelMargin: number
|
||||
markings: array of markings or (fn: axes -> array of markings)
|
||||
borderWidth: number
|
||||
clickable: boolean
|
||||
hoverable: boolean
|
||||
autoHighlight: boolean
|
||||
mouseActiveRadius: number
|
||||
}
|
||||
|
||||
The grid is the thing with the axes and a number of ticks. "color"
|
||||
is the color of the grid itself whereas "backgroundColor" specifies
|
||||
the background color inside the grid area. The default value of null
|
||||
means that the background is transparent. You should only need to set
|
||||
backgroundColor if you want the grid area to be a different color from the
|
||||
page color. Otherwise you might as well just set the background color
|
||||
of the page with CSS.
|
||||
|
||||
"tickColor" is the color of the ticks and "labelMargin" is the spacing
|
||||
between tick labels and the grid. Note that you can style the tick
|
||||
labels with CSS, e.g. to change the color. They have class "tickLabel".
|
||||
"borderWidth" is the width of the border around the plot. Set it to 0
|
||||
to disable the border.
|
||||
|
||||
"markings" is used to draw simple lines and rectangular areas in the
|
||||
background of the plot. You can either specify an array of ranges on
|
||||
the form { xaxis: { from, to }, yaxis: { from, to } } (secondary axis
|
||||
coordinates with x2axis/y2axis) or with a function that returns such
|
||||
an array given the axes for the plot in an object as the first
|
||||
parameter.
|
||||
|
||||
You can set the color of markings by specifying "color" in the ranges
|
||||
object. Here's an example array:
|
||||
|
||||
markings: [ { xaxis: { from: 0, to: 2 }, yaxis: { from: 10, to: 10 }, color: "#bb0000" }, ... ]
|
||||
|
||||
If you leave out one of the values, that value is assumed to go to the
|
||||
border of the plot. So for example if you only specify { xaxis: {
|
||||
from: 0, to: 2 } } it means an area that extends from the top to the
|
||||
bottom of the plot in the x range 0-2.
|
||||
|
||||
A line is drawn if from and to are the same, e.g.
|
||||
|
||||
markings: [ { yaxis: { from: 1, to: 1 } }, ... ]
|
||||
|
||||
would draw a line parallel to the x axis at y = 1. You can control the
|
||||
line width with "lineWidth" in the ranges objects.
|
||||
|
||||
An example function might look like this:
|
||||
|
||||
markings: function (axes) {
|
||||
var markings = [];
|
||||
for (var x = Math.floor(axes.xaxis.min); x < axes.xaxis.max; x += 2)
|
||||
markings.push({ xaxis: { from: x, to: x + 1 } });
|
||||
return markings;
|
||||
}
|
||||
|
||||
|
||||
If you set "clickable" to true, the plot will listen for click events
|
||||
on the plot area and fire a "plotclick" event on the placeholder with
|
||||
a position and a nearby data item object as parameters. The coordinates
|
||||
are available both in the unit of the axes (not in pixels) and in
|
||||
global screen coordinates.
|
||||
|
||||
Likewise, if you set "hoverable" to true, the plot will listen for
|
||||
mouse move events on the plot area and fire a "plothover" event with
|
||||
the same parameters as the "plotclick" event. If "autoHighlight" is
|
||||
true (the default), nearby data items are highlighted automatically.
|
||||
If needed, you can disable highlighting and control it yourself with
|
||||
the highlight/unhighlight plot methods described elsewhere.
|
||||
|
||||
You can use "plotclick" and "plothover" events like this:
|
||||
|
||||
$.plot($("#placeholder"), [ d ], { grid: { clickable: true } });
|
||||
|
||||
$("#placeholder").bind("plotclick", function (event, pos, item) {
|
||||
alert("You clicked at " + pos.x + ", " + pos.y);
|
||||
// secondary axis coordinates if present are in pos.x2, pos.y2,
|
||||
// if you need global screen coordinates, they are pos.pageX, pos.pageY
|
||||
|
||||
if (item) {
|
||||
highlight(item.series, item.datapoint);
|
||||
alert("You clicked a point!");
|
||||
}
|
||||
});
|
||||
|
||||
The item object in this example is either null or a nearby object on the form:
|
||||
|
||||
item: {
|
||||
datapoint: the point as you specified it in the data, e.g. [0, 2]
|
||||
dataIndex: the index of the point in the data array
|
||||
series: the series object
|
||||
seriesIndex: the index of the series
|
||||
pageX, pageY: the global screen coordinates of the point
|
||||
}
|
||||
|
||||
For instance, if you have specified the data like this
|
||||
|
||||
$.plot($("#placeholder"), [ { label: "Foo", data: [[0, 10], [7, 3]] } ], ...);
|
||||
|
||||
and the mouse is near the point (7, 3), "datapoint" is the [7, 3] we
|
||||
specified, "dataIndex" will be 1, "series" is a normalized series
|
||||
object with among other things the "Foo" label in series.label and the
|
||||
color in series.color, and "seriesIndex" is 0.
|
||||
|
||||
If you use the above events to update some other information and want
|
||||
to clear out that info in case the mouse goes away, you'll probably
|
||||
also need to listen to "mouseout" events on the placeholder div.
|
||||
|
||||
"mouseActiveRadius" specifies how far the mouse can be from an item
|
||||
and still activate it. If there are two or more points within this
|
||||
radius, Flot chooses the closest item. For bars, the top-most bar
|
||||
(from the latest specified data series) is chosen.
|
||||
|
||||
|
||||
Customizing the selection
|
||||
=========================
|
||||
|
||||
selection: {
|
||||
mode: null or "x" or "y" or "xy",
|
||||
color: color
|
||||
}
|
||||
|
||||
You enable selection support by setting the mode to one of "x", "y" or
|
||||
"xy". In "x" mode, the user will only be able to specify the x range,
|
||||
similarly for "y" mode. For "xy", the selection becomes a rectangle
|
||||
where both ranges can be specified. "color" is color of the selection.
|
||||
|
||||
When selection support is enabled, a "plotselected" event will be emitted
|
||||
on the DOM element you passed into the plot function. The event
|
||||
handler gets one extra parameter with the ranges selected on the axes,
|
||||
like this:
|
||||
|
||||
placeholder.bind("plotselected", function(event, ranges) {
|
||||
alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
|
||||
// similar for yaxis, secondary axes are in x2axis
|
||||
// and y2axis if present
|
||||
});
|
||||
|
||||
|
||||
Plot Methods
|
||||
------------
|
||||
|
||||
The Plot object returned from the plot function has some methods you
|
||||
can call:
|
||||
|
||||
- clearSelection()
|
||||
|
||||
Clear the selection rectangle.
|
||||
|
||||
|
||||
- setSelection(ranges, preventEvent)
|
||||
|
||||
Set the selection rectangle. The passed in ranges is on the same
|
||||
form as returned in the "plotselected" event. If the selection
|
||||
mode is "x", you should put in either an xaxis (or x2axis) object,
|
||||
if the mode is "y" you need to put in an yaxis (or y2axis) object
|
||||
and both xaxis/x2axis and yaxis/y2axis if the selection mode is
|
||||
"xy", like this:
|
||||
|
||||
setSelection({ xaxis: { from: 0, to: 10 }, yaxis: { from: 40, to: 60 } });
|
||||
|
||||
setSelection will trigger the "plotselected" event when called. If
|
||||
you don't want that to happen, e.g. if you're inside a
|
||||
"plotselected" handler, pass true as the second parameter.
|
||||
|
||||
|
||||
- highlight(series, datapoint)
|
||||
|
||||
Highlight a specific datapoint in the data series. You can either
|
||||
specify the actual objects, e.g. if you got them from a
|
||||
"plotclick" event, or you can specify the indices, e.g.
|
||||
highlight(1, 3) to highlight the fourth point in the second series.
|
||||
|
||||
|
||||
- unhighlight(series, datapoint)
|
||||
|
||||
Remove the highlighting of the point, same parameters as highlight.
|
||||
|
||||
|
||||
- setData(data)
|
||||
|
||||
You can use this to reset the data used. Note that axis scaling,
|
||||
ticks, legend etc. will not be recomputed (use setupGrid() to do
|
||||
that). You'll probably want to call draw() afterwards.
|
||||
|
||||
You can use this function to speed up redrawing a plot if you know
|
||||
that the axes won't change. Put in the new data with
|
||||
setData(newdata) and call draw() afterwards, and you're good to
|
||||
go.
|
||||
|
||||
|
||||
- setupGrid()
|
||||
|
||||
Recalculate and set axis scaling, ticks, legend etc.
|
||||
|
||||
Note that because of the drawing model of the canvas, this
|
||||
function will immediately redraw (actually reinsert in the DOM)
|
||||
the labels and the legend, but not the actual tick lines because
|
||||
they're drawn on the canvas. You need to call draw() to get the
|
||||
canvas redrawn.
|
||||
|
||||
- draw()
|
||||
|
||||
Redraws the canvas.
|
||||
|
||||
|
||||
There are also some members that let you peek inside the internal
|
||||
workings of Flot which in some cases is useful. Note that if you change
|
||||
something in the objects returned, you're changing the objects used by
|
||||
Flot to keep track of its state, so be careful.
|
||||
|
||||
- getData()
|
||||
|
||||
Returns an array of the data series currently used on normalized
|
||||
form with missing settings filled in according to the global
|
||||
options. So for instance to find out what color Flot has assigned
|
||||
to the data series, you could do this:
|
||||
|
||||
var series = plot.getData();
|
||||
for (var i = 0; i < series.length; ++i)
|
||||
alert(series[i].color);
|
||||
|
||||
|
||||
- getAxes()
|
||||
|
||||
Gets an object with the axes settings as { xaxis, yaxis, x2axis,
|
||||
y2axis }. Various things are stuffed inside an axis object, e.g.
|
||||
you could use getAxes().xaxis.ticks to find out what the ticks are
|
||||
for the xaxis.
|
||||
|
||||
|
||||
- getCanvas()
|
||||
|
||||
Returns the canvas used for drawing in case you need to hack on it
|
||||
yourself. You'll probably need to get the plot offset too.
|
||||
|
||||
|
||||
- getPlotOffset()
|
||||
|
||||
Gets the offset that the grid has within the canvas as an object
|
||||
with distances from the canvas edges as "left", "right", "top",
|
||||
"bottom". I.e., if you draw a circle on the canvas with the center
|
||||
placed at (left, top), its center will be at the top-most, left
|
||||
corner of the grid.
|
||||
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
Flot 0.5
|
||||
--------
|
||||
|
||||
Backwards API change summary: Timestamps are now in UTC. Also
|
||||
"selected" event -> becomes "plotselected" with new data, the
|
||||
parameters for setSelection are now different (but backwards
|
||||
compatibility hooks are in place), coloredAreas becomes markings with
|
||||
a new interface (but backwards compatibility hooks are in place).
|
||||
|
||||
|
||||
Interactivity: added a new "plothover" event and this and the
|
||||
"plotclick" event now returns the closest data item (based on patch by
|
||||
/david, patch by Mark Byers for bar support). See the revamped
|
||||
"interacting with the data" example for some hints on what you can do.
|
||||
|
||||
Highlighting: you can now highlight points and datapoints are
|
||||
autohighlighted when you hover over them (if hovering is turned on).
|
||||
|
||||
Support for dual axis has been added (based on patch by someone who's
|
||||
annoyed and /david). For each data series you can specify which axes
|
||||
it belongs to, and there are two more axes, x2axis and y2axis, to
|
||||
customize. This affects the "selected" event which has been renamed to
|
||||
"plotselected" and spews out { xaxis: { from: -10, to: 20 } ... },
|
||||
setSelection in which the parameters are on a new form (backwards
|
||||
compatible hooks are in place so old code shouldn't break) and
|
||||
markings (formerly coloredAreas).
|
||||
|
||||
Timestamps in time mode are now displayed according to
|
||||
UTC instead of the time zone of the visitor. This affects the way the
|
||||
timestamps should be input; you'll probably have to offset the
|
||||
timestamps according to your local time zone. It also affects any
|
||||
custom date handling code (which basically now should use the
|
||||
equivalent UTC date mehods, e.g. .setUTCMonth() instead of
|
||||
.setMonth().
|
||||
|
||||
Added support for specifying the size of tick labels (axis.labelWidth,
|
||||
axis.labelHeight). Useful for specifying a max label size to keep
|
||||
multiple plots aligned.
|
||||
|
||||
Markings, previously coloredAreas, are now specified as ranges on the
|
||||
axes, like { xaxis: { from: 0, to: 10 }}. Furthermore with markings
|
||||
you can now draw horizontal/vertical lines by setting from and to to
|
||||
the same coordinate (idea from line support patch by by Ryan Funduk).
|
||||
|
||||
The "fill" option can now be a number that specifies the opacity of
|
||||
the fill.
|
||||
|
||||
You can now specify a coordinate as null (like [2, null]) and Flot
|
||||
will take the other coordinate into account when scaling the axes
|
||||
(based on patch by joebno).
|
||||
|
||||
New option for bars "align". Set it to "center" to center the bars on
|
||||
the value they represent.
|
||||
|
||||
setSelection now takes a second parameter which you can use to prevent
|
||||
the method from firing the "plotselected" handler.
|
||||
|
||||
Using the "container" option in legend now overwrites the container
|
||||
element instead of just appending to it (fixes infinite legend bug,
|
||||
reported by several people, fix by Brad Dewey).
|
||||
|
||||
Fixed a bug in calculating spacing around the plot (reported by
|
||||
timothytoe). Fixed a bug in finding max values for all-negative data
|
||||
sets. Prevent the possibility of eternal looping in tick calculations.
|
||||
Fixed a bug when borderWidth is set to 0 (reported by
|
||||
Rob/sanchothefat). Fixed a bug with drawing bars extending below 0
|
||||
(reported by James Hewitt, patch by Ryan Funduk). Fixed a
|
||||
bug with line widths of bars (reported by MikeM). Fixed a bug with
|
||||
'nw' and 'sw' legend positions. Improved the handling of axis
|
||||
auto-scaling with bars. Fixed a bug with multi-line x-axis tick
|
||||
labels (reported by Luca Ciano). IE-fix help by Savage Zhang.
|
||||
|
||||
|
||||
Flot 0.4
|
||||
--------
|
||||
|
||||
API changes: deprecated axis.noTicks in favor of just specifying the
|
||||
number as axis.ticks. So "xaxis: { noTicks: 10 }" becomes
|
||||
"xaxis: { ticks: 10 }"
|
||||
|
||||
Time series support. Specify axis.mode: "time", put in Javascript
|
||||
timestamps as data, and Flot will automatically spit out sensible
|
||||
ticks. Take a look at the two new examples. The format can be
|
||||
customized with axis.timeformat and axis.monthNames, or if that fails
|
||||
with axis.tickFormatter.
|
||||
|
||||
Support for colored background areas via grid.coloredAreas. Specify an
|
||||
array of { x1, y1, x2, y2 } objects or a function that returns these
|
||||
given { xmin, xmax, ymin, ymax }.
|
||||
|
||||
More members on the plot object (report by Chris Davies and others).
|
||||
"getData" for inspecting the assigned settings on data series (e.g.
|
||||
color) and "setData", "setupGrid" and "draw" for updating the contents
|
||||
without a total replot.
|
||||
|
||||
The default number of ticks to aim for is now dependent on the size of
|
||||
the plot in pixels. Support for customizing tick interval sizes
|
||||
directly with axis.minTickSize and axis.tickSize.
|
||||
|
||||
Cleaned up the automatic axis scaling algorithm and fixed how it
|
||||
interacts with ticks. Also fixed a couple of tick-related corner case
|
||||
bugs (one reported by mainstreetmark, another reported by timothytoe).
|
||||
|
||||
The option axis.tickFormatter now takes a function with two
|
||||
parameters, the second parameter is an optional object with
|
||||
information about the axis. It has min, max, tickDecimals, tickSize.
|
||||
|
||||
Added support for segmented lines (based on patch from Michael
|
||||
MacDonald) and for ignoring null and bad values (suggestion from Nick
|
||||
Konidaris and joshwaihi).
|
||||
|
||||
Added support for changing the border width (joebno and safoo).
|
||||
Label colors can be changed via CSS by selecting the tickLabel class.
|
||||
|
||||
Fixed a bug in handling single-item bar series (reported by Emil
|
||||
Filipov). Fixed erratic behaviour when interacting with the plot
|
||||
with IE 7 (reported by Lau Bech Lauritzen). Prevent IE/Safari text
|
||||
selection when selecting stuff on the canvas.
|
||||
|
||||
|
||||
|
||||
Flot 0.3
|
||||
--------
|
||||
|
||||
This is mostly a quick-fix release because jquery.js wasn't included
|
||||
in the previous zip/tarball.
|
||||
|
||||
Support clicking on the plot. Turn it on with grid: { clickable: true },
|
||||
then you get a "plotclick" event on the graph placeholder with the
|
||||
position in units of the plot.
|
||||
|
||||
Fixed a bug in dealing with data where min = max, thanks to Michael
|
||||
Messinides.
|
||||
|
||||
Include jquery.js in the zip/tarball.
|
||||
|
||||
|
||||
Flot 0.2
|
||||
--------
|
||||
|
||||
Added support for putting a background behind the default legend. The
|
||||
default is the partly transparent background color. Added
|
||||
backgroundColor and backgroundOpacity to the legend options to control
|
||||
this.
|
||||
|
||||
The ticks options can now be a callback function that takes one
|
||||
parameter, an object with the attributes min and max. The function
|
||||
should return a ticks array.
|
||||
|
||||
Added labelFormatter option in legend, useful for turning the legend
|
||||
labels into links.
|
||||
|
||||
Fixed a couple of bugs.
|
||||
|
||||
The API should now be fully documented.
|
||||
|
||||
Patch from Guy Fraser to make parts of the code smaller.
|
||||
|
||||
API changes: Moved labelMargin option to grid from x/yaxis.
|
||||
|
||||
|
||||
Flot 0.1
|
||||
--------
|
||||
|
||||
First public release.
|
|
@ -0,0 +1,80 @@
|
|||
About
|
||||
-----
|
||||
|
||||
Flot is a Javascript plotting library for jQuery. Read more at the
|
||||
website:
|
||||
|
||||
http://code.google.com/p/flot/
|
||||
|
||||
Take a look at the examples linked from above, they should give a good
|
||||
impression of what Flot can do and the source code of the examples is
|
||||
probably the fastest way to learn how to use Flot.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Just include the Javascript file after you've included jQuery.
|
||||
|
||||
Note that you need to get a version of Excanvas (I currently suggest
|
||||
you take the one bundled with Flot as it contains a bugfix for drawing
|
||||
filled shapes) which is canvas emulation on Internet Explorer. You can
|
||||
include the excanvas script like this:
|
||||
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="excanvas.pack.js"></script><![endif]-->
|
||||
|
||||
If it's not working on your development IE 6.0, check that it has
|
||||
support for VML which excanvas is relying on. It appears that some
|
||||
stripped down versions used for test environments on virtual machines
|
||||
lack the VML support.
|
||||
|
||||
Also note that you need at least jQuery 1.2.1.
|
||||
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
Create a placeholder div to put the graph in:
|
||||
|
||||
<div id="placeholder"></div>
|
||||
|
||||
You need to set the width and height of this div, otherwise the plot
|
||||
library doesn't know how to scale the graph. You can do it inline like
|
||||
this:
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px"></div>
|
||||
|
||||
You can also do it with an external stylesheet. Make sure that the
|
||||
placeholder isn't within something with a display:none CSS property -
|
||||
in that case, Flot has trouble measuring label dimensions which
|
||||
results in garbled looks and might have trouble measuring the
|
||||
placeholder dimensions which is fatal (it'll throw an exception).
|
||||
|
||||
Then when the div is ready in the DOM, which is usually on document
|
||||
ready, run the plot function:
|
||||
|
||||
$.plot($("#placeholder"), data, options);
|
||||
|
||||
Here, data is an array of data series and options is an object with
|
||||
settings if you want to customize the plot. Take a look at the
|
||||
examples for some ideas of what to put in or look at the reference
|
||||
in the file "API.txt". Here's a quick example that'll draw a line from
|
||||
(0, 0) to (1, 1):
|
||||
|
||||
$.plot($("#placeholder"), [ [[0, 0], [1, 1]] ], { yaxis: { max: 1 } });
|
||||
|
||||
The plot function immediately draws the chart and then returns a Plot
|
||||
object with a couple of methods.
|
||||
|
||||
|
||||
What's with the name?
|
||||
---------------------
|
||||
|
||||
First: it's pronounced with a short o, like "plot". Not like "flawed".
|
||||
|
||||
So "Flot" is like "Plot".
|
||||
|
||||
And if you look up "flot" in a Danish-to-English dictionary, some up
|
||||
the words that come up are "good-looking", "attractive", "stylish",
|
||||
"smart", "impressive", "extravagant". One of the main goals with Flot
|
||||
is pretty looks. Flot is supposed to be "flot".
|
|
@ -0,0 +1,36 @@
|
|||
These are mostly ideas, that they're written down here is no guarantee
|
||||
that they'll ever be done. If you want something done, feel free to
|
||||
say why or come up with a patch. :-)
|
||||
|
||||
pending
|
||||
- split out autoscaleMargin into a snapToTicks
|
||||
|
||||
grid configuration
|
||||
- how ticks look like
|
||||
- consider setting default grid colors from each other?
|
||||
|
||||
selection
|
||||
- user should be able to cancel selection with escape
|
||||
|
||||
interactive zooming
|
||||
- convenience zoom(x1, y1, x2, y2)? and zoomOut() (via zoom stack)?
|
||||
- auto-zoom mode?
|
||||
- auto-margins
|
||||
|
||||
legend
|
||||
- interactive auto-highlight of graph?
|
||||
- ability to specify noRows instead of just noColumns
|
||||
|
||||
labels
|
||||
- labels on bars, data points
|
||||
- interactive "label this point" command/tooltip support
|
||||
|
||||
error margin indicators
|
||||
- for scientific/statistical purposes
|
||||
|
||||
hi-low bars
|
||||
|
||||
non-xy based graph types
|
||||
- figure out how to integrate them with the rest of the plugin
|
||||
- pie charts
|
||||
- bar charts, keys instead of x values
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px;"></div>
|
||||
|
||||
<p>Simple example. You don't need to specify much to get an
|
||||
attractive look. Put in a placeholder, make sure you set its
|
||||
dimensions (otherwise the plot library will barf) and call the
|
||||
plot function with the data. The axes are automatically
|
||||
scaled.</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var d1 = [];
|
||||
for (var i = 0; i < 14; i += 0.5)
|
||||
d1.push([i, Math.sin(i)]);
|
||||
|
||||
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
|
||||
|
||||
// a null signifies separate line segments
|
||||
var d3 = [[0, 12], [7, 12], null, [7, 2.5], [12, 2.5]];
|
||||
|
||||
$.plot($("#placeholder"), [ d1, d2, d3 ]);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px"></div>
|
||||
|
||||
<p>Flot supports lines, points, filled areas, bars and any
|
||||
combinations of these, in the same plot and even on the same data
|
||||
series.</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var d1 = [];
|
||||
for (var i = 0; i < 14; i += 0.5)
|
||||
d1.push([i, Math.sin(i)]);
|
||||
|
||||
var d2 = [[0, 3], [4, 8], [8, 5], [9, 13]];
|
||||
|
||||
var d3 = [];
|
||||
for (var i = 0; i < 14; i += 0.5)
|
||||
d3.push([i, Math.cos(i)]);
|
||||
|
||||
var d4 = [];
|
||||
for (var i = 0; i < 14; i += 0.1)
|
||||
d4.push([i, Math.sqrt(i * 10)]);
|
||||
|
||||
var d5 = [];
|
||||
for (var i = 0; i < 14; i += 0.5)
|
||||
d5.push([i, Math.sqrt(i)]);
|
||||
|
||||
$.plot($("#placeholder"), [
|
||||
{
|
||||
data: d1,
|
||||
lines: { show: true, fill: true }
|
||||
},
|
||||
{
|
||||
data: d2,
|
||||
bars: { show: true }
|
||||
},
|
||||
{
|
||||
data: d3,
|
||||
points: { show: true }
|
||||
},
|
||||
{
|
||||
data: d4,
|
||||
lines: { show: true }
|
||||
},
|
||||
{
|
||||
data: d5,
|
||||
lines: { show: true },
|
||||
points: { show: true }
|
||||
}
|
||||
]);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<p>Here are some examples for <a href="http://code.google.com/p/flot/">Flot</a>:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="basic.html">Basic example</a></li>
|
||||
<li><a href="graph-types.html">Different graph types</a> and <a href="setting-options.html">setting various options</a></li>
|
||||
<li><a href="turning-series.html">Turning series on/off</a></li>
|
||||
<li><a href="selection.html">Selection support and zooming</a> and <a href="zooming.html">zooming with overview</a></li>
|
||||
<li><a href="time.html">Plotting time series</a> and <a href="visitors.html">visitors per day with zooming and weekends</a></li>
|
||||
<li><a href="dual-axis.html">Dual axis support</a></li>
|
||||
<li><a href="interacting.html">Interacting with the data</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,92 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px"></div>
|
||||
|
||||
<p>One of the goals of Flot is to support user interactions. Try
|
||||
pointing and clicking on the points.</p>
|
||||
|
||||
<p id="hoverdata">Mouse hovers at
|
||||
(<span id="x">0</span>, <span id="y">0</span>). <span id="clickdata"></span></p>
|
||||
|
||||
<p>A tooltip is easy to build with a bit of jQuery code and the
|
||||
data returned from the plot.</p>
|
||||
|
||||
<p><input id="enableTooltip" type="checkbox">Enable tooltip</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var sin = [], cos = [];
|
||||
for (var i = 0; i < 14; i += 0.5) {
|
||||
sin.push([i, Math.sin(i)]);
|
||||
cos.push([i, Math.cos(i)]);
|
||||
}
|
||||
|
||||
var plot = $.plot($("#placeholder"),
|
||||
[ { data: sin, label: "sin(x)"}, { data: cos, label: "cos(x)" } ],
|
||||
{ lines: { show: true },
|
||||
points: { show: true },
|
||||
selection: { mode: "xy" },
|
||||
grid: { hoverable: true, clickable: true },
|
||||
yaxis: { min: -1.2, max: 1.2 }
|
||||
});
|
||||
|
||||
function showTooltip(x, y, contents) {
|
||||
$('<div id="tooltip">' + contents + '</div>').css( {
|
||||
position: 'absolute',
|
||||
display: 'none',
|
||||
top: y + 5,
|
||||
left: x + 5,
|
||||
border: '1px solid #fdd',
|
||||
padding: '2px',
|
||||
'background-color': '#fee',
|
||||
opacity: 0.80
|
||||
}).appendTo("body").fadeIn(200);
|
||||
}
|
||||
|
||||
var previousPoint = null;
|
||||
$("#placeholder").bind("plothover", function (event, pos, item) {
|
||||
$("#x").text(pos.x.toFixed(2));
|
||||
$("#y").text(pos.y.toFixed(2));
|
||||
|
||||
if ($("#enableTooltip:checked").length > 0) {
|
||||
if (item) {
|
||||
if (previousPoint != item.datapoint) {
|
||||
previousPoint = item.datapoint;
|
||||
|
||||
$("#tooltip").remove();
|
||||
var x = item.datapoint[0].toFixed(2),
|
||||
y = item.datapoint[1].toFixed(2);
|
||||
|
||||
showTooltip(item.pageX, item.pageY,
|
||||
item.series.label + " of " + x + " = " + y);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$("#tooltip").remove();
|
||||
previousPoint = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#placeholder").bind("plotclick", function (event, pos, item) {
|
||||
if (item) {
|
||||
$("#clickdata").text("You clicked point " + item.dataIndex + " in " + item.series.label + ".");
|
||||
plot.highlight(item.series, item.datapoint);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,5 @@
|
|||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 16px;
|
||||
margin: 50px;
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px"></div>
|
||||
|
||||
<p>1000 kg. CO<sub>2</sub> emissions per year per capita for various countries (source: <a href="http://en.wikipedia.org/wiki/List_of_countries_by_carbon_dioxide_emissions_per_capita">Wikipedia</a>).</p>
|
||||
|
||||
<p>Flot supports selections. You can enable
|
||||
rectangular selection
|
||||
or one-dimensional selection if the user should only be able to
|
||||
select on one axis. Try left-clicking and drag on the plot above
|
||||
where selection on the x axis is enabled.</p>
|
||||
|
||||
<p>You selected: <span id="selection"></span></p>
|
||||
|
||||
<p>The plot command returns a Plot object you can use to control
|
||||
the selection. Try clicking the buttons below.</p>
|
||||
|
||||
<p><input id="clearSelection" type="button" value="Clear selection" />
|
||||
<input id="setSelection" type="button" value="Select year 1994" /></p>
|
||||
|
||||
<p>Selections are really useful for zooming. Just replot the
|
||||
chart with min and max values for the axes set to the values
|
||||
in the "plotselected" event triggered. Try enabling the checkbox
|
||||
below and select a region again.</p>
|
||||
|
||||
<p><input id="zoom" type="checkbox">Zoom to selection.</input></p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var data = [
|
||||
{
|
||||
label: "United States",
|
||||
data: [[1990, 18.9], [1991, 18.7], [1992, 18.4], [1993, 19.3], [1994, 19.5], [1995, 19.3], [1996, 19.4], [1997, 20.2], [1998, 19.8], [1999, 19.9], [2000, 20.4], [2001, 20.1], [2002, 20.0], [2003, 19.8], [2004, 20.4]]
|
||||
},
|
||||
{
|
||||
label: "Russia",
|
||||
data: [[1992, 13.4], [1993, 12.2], [1994, 10.6], [1995, 10.2], [1996, 10.1], [1997, 9.7], [1998, 9.5], [1999, 9.7], [2000, 9.9], [2001, 9.9], [2002, 9.9], [2003, 10.3], [2004, 10.5]]
|
||||
},
|
||||
{
|
||||
label: "United Kingdom",
|
||||
data: [[1990, 10.0], [1991, 11.3], [1992, 9.9], [1993, 9.6], [1994, 9.5], [1995, 9.5], [1996, 9.9], [1997, 9.3], [1998, 9.2], [1999, 9.2], [2000, 9.5], [2001, 9.6], [2002, 9.3], [2003, 9.4], [2004, 9.79]]
|
||||
},
|
||||
{
|
||||
label: "Germany",
|
||||
data: [[1990, 12.4], [1991, 11.2], [1992, 10.8], [1993, 10.5], [1994, 10.4], [1995, 10.2], [1996, 10.5], [1997, 10.2], [1998, 10.1], [1999, 9.6], [2000, 9.7], [2001, 10.0], [2002, 9.7], [2003, 9.8], [2004, 9.79]]
|
||||
},
|
||||
{
|
||||
label: "Denmark",
|
||||
data: [[1990, 9.7], [1991, 12.1], [1992, 10.3], [1993, 11.3], [1994, 11.7], [1995, 10.6], [1996, 12.8], [1997, 10.8], [1998, 10.3], [1999, 9.4], [2000, 8.7], [2001, 9.0], [2002, 8.9], [2003, 10.1], [2004, 9.80]]
|
||||
},
|
||||
{
|
||||
label: "Sweden",
|
||||
data: [[1990, 5.8], [1991, 6.0], [1992, 5.9], [1993, 5.5], [1994, 5.7], [1995, 5.3], [1996, 6.1], [1997, 5.4], [1998, 5.4], [1999, 5.1], [2000, 5.2], [2001, 5.4], [2002, 6.2], [2003, 5.9], [2004, 5.89]]
|
||||
},
|
||||
{
|
||||
label: "Norway",
|
||||
data: [[1990, 8.3], [1991, 8.3], [1992, 7.8], [1993, 8.3], [1994, 8.4], [1995, 5.9], [1996, 6.4], [1997, 6.7], [1998, 6.9], [1999, 7.6], [2000, 7.4], [2001, 8.1], [2002, 12.5], [2003, 9.9], [2004, 19.0]]
|
||||
}
|
||||
];
|
||||
|
||||
var options = {
|
||||
lines: { show: true },
|
||||
points: { show: true },
|
||||
legend: { noColumns: 2 },
|
||||
xaxis: { tickDecimals: 0 },
|
||||
yaxis: { min: 0 },
|
||||
selection: { mode: "x" }
|
||||
};
|
||||
|
||||
var placeholder = $("#placeholder");
|
||||
|
||||
placeholder.bind("plotselected", function (event, ranges) {
|
||||
$("#selection").text(ranges.xaxis.from.toFixed(1) + " to " + ranges.xaxis.to.toFixed(1));
|
||||
|
||||
var zoom = $("#zoom").attr("checked");
|
||||
if (zoom)
|
||||
plot = $.plot(placeholder, data,
|
||||
$.extend(true, {}, options, {
|
||||
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
|
||||
}));
|
||||
});
|
||||
|
||||
var plot = $.plot(placeholder, data, options);
|
||||
|
||||
$("#clearSelection").click(function () {
|
||||
plot.clearSelection();
|
||||
});
|
||||
|
||||
$("#setSelection").click(function () {
|
||||
plot.setSelection({ x1: 1994, x2: 1995 });
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px"></div>
|
||||
|
||||
<p>There are plenty of options you can set to control the precise
|
||||
looks of your plot. You can control the axes, the legend, the
|
||||
default graph type, the look of grid, etc.</p>
|
||||
|
||||
<p>The idea is that Flot goes to great lengths to provide <b>sensible
|
||||
defaults</b> which you can then customize as needed for your
|
||||
particular application. If you've found a use case where the
|
||||
defaults can be improved, please don't hesitate to give your
|
||||
feedback.</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var d1 = [];
|
||||
for (var i = 0; i < Math.PI * 2; i += 0.25)
|
||||
d1.push([i, Math.sin(i)]);
|
||||
|
||||
var d2 = [];
|
||||
for (var i = 0; i < Math.PI * 2; i += 0.25)
|
||||
d2.push([i, Math.cos(i)]);
|
||||
|
||||
var d3 = [];
|
||||
for (var i = 0; i < Math.PI * 2; i += 0.1)
|
||||
d3.push([i, Math.tan(i)]);
|
||||
|
||||
$.plot($("#placeholder"), [
|
||||
{ label: "sin(x)", data: d1},
|
||||
{ label: "cos(x)", data: d2},
|
||||
{ label: "tan(x)", data: d3}
|
||||
], {
|
||||
lines: { show: true },
|
||||
points: { show: true },
|
||||
xaxis: {
|
||||
ticks: [0, [Math.PI/2, "\u03c0/2"], [Math.PI, "\u03c0"], [Math.PI * 3/2, "3\u03c0/2"], [Math.PI * 2, "2\u03c0"]]
|
||||
},
|
||||
yaxis: {
|
||||
ticks: 10,
|
||||
min: -2,
|
||||
max: 2
|
||||
},
|
||||
grid: {
|
||||
backgroundColor: "#fffaff"
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -0,0 +1,96 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px;"></div>
|
||||
|
||||
<p>Here is an example with real data: military budgets for
|
||||
various countries in constant (2005) million US dollars (source: <a href="http://www.sipri.org/">SIPRI</a>).</p>
|
||||
|
||||
<p>Since all data is available client-side, it's pretty easy to
|
||||
make the plot interactive. Try turning countries on/off with the
|
||||
checkboxes below.</p>
|
||||
|
||||
<p id="choices">Show:</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var datasets = {
|
||||
"usa": {
|
||||
label: "USA",
|
||||
data: [[1988, 483994], [1989, 479060], [1990, 457648], [1991, 401949], [1992, 424705], [1993, 402375], [1994, 377867], [1995, 357382], [1996, 337946], [1997, 336185], [1998, 328611], [1999, 329421], [2000, 342172], [2001, 344932], [2002, 387303], [2003, 440813], [2004, 480451], [2005, 504638], [2006, 528692]]
|
||||
},
|
||||
"russia": {
|
||||
label: "Russia",
|
||||
data: [[1988, 218000], [1989, 203000], [1990, 171000], [1992, 42500], [1993, 37600], [1994, 36600], [1995, 21700], [1996, 19200], [1997, 21300], [1998, 13600], [1999, 14000], [2000, 19100], [2001, 21300], [2002, 23600], [2003, 25100], [2004, 26100], [2005, 31100], [2006, 34700]]
|
||||
},
|
||||
"uk": {
|
||||
label: "UK",
|
||||
data: [[1988, 62982], [1989, 62027], [1990, 60696], [1991, 62348], [1992, 58560], [1993, 56393], [1994, 54579], [1995, 50818], [1996, 50554], [1997, 48276], [1998, 47691], [1999, 47529], [2000, 47778], [2001, 48760], [2002, 50949], [2003, 57452], [2004, 60234], [2005, 60076], [2006, 59213]]
|
||||
},
|
||||
"germany": {
|
||||
label: "Germany",
|
||||
data: [[1988, 55627], [1989, 55475], [1990, 58464], [1991, 55134], [1992, 52436], [1993, 47139], [1994, 43962], [1995, 43238], [1996, 42395], [1997, 40854], [1998, 40993], [1999, 41822], [2000, 41147], [2001, 40474], [2002, 40604], [2003, 40044], [2004, 38816], [2005, 38060], [2006, 36984]]
|
||||
},
|
||||
"denmark": {
|
||||
label: "Denmark",
|
||||
data: [[1988, 3813], [1989, 3719], [1990, 3722], [1991, 3789], [1992, 3720], [1993, 3730], [1994, 3636], [1995, 3598], [1996, 3610], [1997, 3655], [1998, 3695], [1999, 3673], [2000, 3553], [2001, 3774], [2002, 3728], [2003, 3618], [2004, 3638], [2005, 3467], [2006, 3770]]
|
||||
},
|
||||
"sweden": {
|
||||
label: "Sweden",
|
||||
data: [[1988, 6402], [1989, 6474], [1990, 6605], [1991, 6209], [1992, 6035], [1993, 6020], [1994, 6000], [1995, 6018], [1996, 3958], [1997, 5780], [1998, 5954], [1999, 6178], [2000, 6411], [2001, 5993], [2002, 5833], [2003, 5791], [2004, 5450], [2005, 5521], [2006, 5271]]
|
||||
},
|
||||
"norway": {
|
||||
label: "Norway",
|
||||
data: [[1988, 4382], [1989, 4498], [1990, 4535], [1991, 4398], [1992, 4766], [1993, 4441], [1994, 4670], [1995, 4217], [1996, 4275], [1997, 4203], [1998, 4482], [1999, 4506], [2000, 4358], [2001, 4385], [2002, 5269], [2003, 5066], [2004, 5194], [2005, 4887], [2006, 4891]]
|
||||
}
|
||||
};
|
||||
|
||||
// hard-code color indices to prevent them from shifting as
|
||||
// countries are turned on/off
|
||||
var i = 0;
|
||||
$.each(datasets, function(key, val) {
|
||||
val.color = i;
|
||||
++i;
|
||||
});
|
||||
|
||||
// insert checkboxes
|
||||
var choiceContainer = $("#choices");
|
||||
$.each(datasets, function(key, val) {
|
||||
choiceContainer.append('<br/><input type="checkbox" name="' + key +
|
||||
'" checked="checked" >' + val.label + '</input>');
|
||||
});
|
||||
choiceContainer.find("input").click(plotAccordingToChoices);
|
||||
|
||||
|
||||
function plotAccordingToChoices() {
|
||||
var data = [];
|
||||
|
||||
choiceContainer.find("input:checked").each(function () {
|
||||
var key = $(this).attr("name");
|
||||
if (key && datasets[key])
|
||||
data.push(datasets[key]);
|
||||
});
|
||||
|
||||
if (data.length > 0)
|
||||
$.plot($("#placeholder"), data, {
|
||||
yaxis: { min: 0 },
|
||||
xaxis: { tickDecimals: 0 }
|
||||
});
|
||||
}
|
||||
|
||||
plotAccordingToChoices();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,87 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div id="placeholder" style="width:600px;height:300px;"></div>
|
||||
|
||||
<p>Visitors per day to the Flot homepage. Weekends are colored. Try zooming.
|
||||
The plot below shows an overview.</p>
|
||||
|
||||
<div id="overview" style="margin-left:50px;margin-top:20px;width:400px;height:50px"></div>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
var d = [[1196463600000, 0], [1196550000000, 0], [1196636400000, 0], [1196722800000, 77], [1196809200000, 3636], [1196895600000, 3575], [1196982000000, 2736], [1197068400000, 1086], [1197154800000, 676], [1197241200000, 1205], [1197327600000, 906], [1197414000000, 710], [1197500400000, 639], [1197586800000, 540], [1197673200000, 435], [1197759600000, 301], [1197846000000, 575], [1197932400000, 481], [1198018800000, 591], [1198105200000, 608], [1198191600000, 459], [1198278000000, 234], [1198364400000, 1352], [1198450800000, 686], [1198537200000, 279], [1198623600000, 449], [1198710000000, 468], [1198796400000, 392], [1198882800000, 282], [1198969200000, 208], [1199055600000, 229], [1199142000000, 177], [1199228400000, 374], [1199314800000, 436], [1199401200000, 404], [1199487600000, 253], [1199574000000, 218], [1199660400000, 476], [1199746800000, 462], [1199833200000, 448], [1199919600000, 442], [1200006000000, 403], [1200092400000, 204], [1200178800000, 194], [1200265200000, 327], [1200351600000, 374], [1200438000000, 507], [1200524400000, 546], [1200610800000, 482], [1200697200000, 283], [1200783600000, 221], [1200870000000, 483], [1200956400000, 523], [1201042800000, 528], [1201129200000, 483], [1201215600000, 452], [1201302000000, 270], [1201388400000, 222], [1201474800000, 439], [1201561200000, 559], [1201647600000, 521], [1201734000000, 477], [1201820400000, 442], [1201906800000, 252], [1201993200000, 236], [1202079600000, 525], [1202166000000, 477], [1202252400000, 386], [1202338800000, 409], [1202425200000, 408], [1202511600000, 237], [1202598000000, 193], [1202684400000, 357], [1202770800000, 414], [1202857200000, 393], [1202943600000, 353], [1203030000000, 364], [1203116400000, 215], [1203202800000, 214], [1203289200000, 356], [1203375600000, 399], [1203462000000, 334], [1203548400000, 348], [1203634800000, 243], [1203721200000, 126], [1203807600000, 157], [1203894000000, 288]];
|
||||
|
||||
// first correct the timestamps - they are recorded as the daily
|
||||
// midnights in UTC+0100, but Flot always displays dates in UTC
|
||||
// so we have to add one hour to hit the midnights in the plot
|
||||
for (var i = 0; i < d.length; ++i)
|
||||
d[i][0] += 60 * 60 * 1000;
|
||||
|
||||
// helper for returning the weekends in a period
|
||||
function weekendAreas(axes) {
|
||||
var markings = [];
|
||||
var d = new Date(axes.xaxis.min);
|
||||
// go to the first Saturday
|
||||
d.setUTCDate(d.getUTCDate() - ((d.getUTCDay() + 1) % 7))
|
||||
d.setUTCSeconds(0);
|
||||
d.setUTCMinutes(0);
|
||||
d.setUTCHours(0);
|
||||
var i = d.getTime();
|
||||
do {
|
||||
// when we don't set yaxis the rectangle automatically
|
||||
// extends to infinity upwards and downwards
|
||||
markings.push({ xaxis: { from: i, to: i + 2 * 24 * 60 * 60 * 1000 } });
|
||||
i += 7 * 24 * 60 * 60 * 1000;
|
||||
} while (i < axes.xaxis.max);
|
||||
|
||||
return markings;
|
||||
}
|
||||
|
||||
var options = {
|
||||
xaxis: { mode: "time" },
|
||||
selection: { mode: "x" },
|
||||
grid: { markings: weekendAreas }
|
||||
};
|
||||
|
||||
var plot = $.plot($("#placeholder"), [d], options);
|
||||
|
||||
var overview = $.plot($("#overview"), [d], {
|
||||
lines: { show: true, lineWidth: 1 },
|
||||
shadowSize: 0,
|
||||
xaxis: { ticks: [], mode: "time" },
|
||||
yaxis: { ticks: [], min: 0, max: 4000 },
|
||||
selection: { mode: "x" }
|
||||
});
|
||||
|
||||
// now connect the two
|
||||
|
||||
$("#placeholder").bind("plotselected", function (event, ranges) {
|
||||
// do the zooming
|
||||
plot = $.plot($("#placeholder"), [d],
|
||||
$.extend(true, {}, options, {
|
||||
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to }
|
||||
}));
|
||||
|
||||
// don't fire event on the overview to prevent eternal loop
|
||||
overview.setSelection(ranges, true);
|
||||
});
|
||||
|
||||
$("#overview").bind("plotselected", function (event, ranges) {
|
||||
plot.setSelection(ranges);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,91 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Flot Examples</title>
|
||||
<link href="layout.css" rel="stylesheet" type="text/css"></link>
|
||||
<!--[if IE]><script language="javascript" type="text/javascript" src="../excanvas.pack.js"></script><![endif]-->
|
||||
<script language="javascript" type="text/javascript" src="../jquery.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="../jquery.flot.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Flot Examples</h1>
|
||||
|
||||
<div style="float:left">
|
||||
<div id="placeholder" style="width:500px;height:300px"></div>
|
||||
</div>
|
||||
|
||||
<div id="miniature" style="float:left;margin-left:20px;margin-top:50px">
|
||||
<div id="overview" style="width:166px;height:100px"></div>
|
||||
|
||||
<p id="overviewLegend" style="margin-left:10px"></p>
|
||||
</div>
|
||||
|
||||
<p style="clear:left"> The selection support makes
|
||||
pretty advanced zooming schemes possible. With a few lines of code,
|
||||
the small overview plot to the right has been connected to the large
|
||||
plot. Try selecting a rectangle on either of them.</p>
|
||||
|
||||
<script id="source" language="javascript" type="text/javascript">
|
||||
$(function () {
|
||||
// setup plot
|
||||
function getData(x1, x2) {
|
||||
var d = [];
|
||||
for (var i = x1; i < x2; i += (x2 - x1) / 100)
|
||||
d.push([i, Math.sin(i * Math.sin(i))]);
|
||||
|
||||
return [
|
||||
{ label: "sin(x sin(x))", data: d }
|
||||
];
|
||||
}
|
||||
|
||||
var options = {
|
||||
legend: { show: false },
|
||||
lines: { show: true },
|
||||
points: { show: true },
|
||||
yaxis: { ticks: 10 },
|
||||
selection: { mode: "xy" }
|
||||
};
|
||||
|
||||
var startData = getData(0, 3 * Math.PI);
|
||||
|
||||
var plot = $.plot($("#placeholder"), startData, options);
|
||||
|
||||
// setup overview
|
||||
var overview = $.plot($("#overview"), startData, {
|
||||
legend: { show: true, container: $("#overviewLegend") },
|
||||
lines: { show: true, lineWidth: 1 },
|
||||
shadowSize: 0,
|
||||
xaxis: { ticks: 4 },
|
||||
yaxis: { ticks: 3, min: -2, max: 2 },
|
||||
grid: { color: "#999" },
|
||||
selection: { mode: "xy" }
|
||||
});
|
||||
|
||||
// now connect the two
|
||||
|
||||
$("#placeholder").bind("plotselected", function (event, ranges) {
|
||||
// clamp the zooming to prevent eternal zoom
|
||||
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
|
||||
ranges.xaxis.to = ranges.xaxis.from + 0.00001;
|
||||
if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
|
||||
ranges.yaxis.to = ranges.yaxis.from + 0.00001;
|
||||
|
||||
// do the zooming
|
||||
plot = $.plot($("#placeholder"), getData(ranges.xaxis.from, ranges.xaxis.to),
|
||||
$.extend(true, {}, options, {
|
||||
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
|
||||
yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
|
||||
}));
|
||||
|
||||
// don't fire event on the overview to prevent eternal loop
|
||||
overview.setSelection(ranges, true);
|
||||
});
|
||||
$("#overview").bind("plotselected", function (event, ranges) {
|
||||
plot.setSelection(ranges);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,785 @@
|
|||
// Copyright 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Known Issues:
|
||||
//
|
||||
// * Patterns are not implemented.
|
||||
// * Radial gradient are not implemented. The VML version of these look very
|
||||
// different from the canvas one.
|
||||
// * Clipping paths are not implemented.
|
||||
// * Coordsize. The width and height attribute have higher priority than the
|
||||
// width and height style values which isn't correct.
|
||||
// * Painting mode isn't implemented.
|
||||
// * Canvas width/height should is using content-box by default. IE in
|
||||
// Quirks mode will draw the canvas using border-box. Either change your
|
||||
// doctype to HTML5
|
||||
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
|
||||
// or use Box Sizing Behavior from WebFX
|
||||
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
|
||||
// * Optimize. There is always room for speed improvements.
|
||||
|
||||
// only add this code if we do not already have a canvas implementation
|
||||
if (!window.CanvasRenderingContext2D) {
|
||||
|
||||
(function () {
|
||||
|
||||
// alias some functions to make (compiled) code shorter
|
||||
var m = Math;
|
||||
var mr = m.round;
|
||||
var ms = m.sin;
|
||||
var mc = m.cos;
|
||||
|
||||
// this is used for sub pixel precision
|
||||
var Z = 10;
|
||||
var Z2 = Z / 2;
|
||||
|
||||
var G_vmlCanvasManager_ = {
|
||||
init: function (opt_doc) {
|
||||
var doc = opt_doc || document;
|
||||
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
|
||||
var self = this;
|
||||
doc.attachEvent("onreadystatechange", function () {
|
||||
self.init_(doc);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
init_: function (doc) {
|
||||
if (doc.readyState == "complete") {
|
||||
// create xmlns
|
||||
if (!doc.namespaces["g_vml_"]) {
|
||||
doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
|
||||
}
|
||||
|
||||
// setup default css
|
||||
var ss = doc.createStyleSheet();
|
||||
ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
|
||||
// default size is 300x150 in Gecko and Opera
|
||||
"text-align:left;width:300px;height:150px}" +
|
||||
"g_vml_\\:*{behavior:url(#default#VML)}";
|
||||
|
||||
// find all canvas elements
|
||||
var els = doc.getElementsByTagName("canvas");
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
if (!els[i].getContext) {
|
||||
this.initElement(els[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fixElement_: function (el) {
|
||||
// in IE before version 5.5 we would need to add HTML: to the tag name
|
||||
// but we do not care about IE before version 6
|
||||
var outerHTML = el.outerHTML;
|
||||
|
||||
var newEl = el.ownerDocument.createElement(outerHTML);
|
||||
// if the tag is still open IE has created the children as siblings and
|
||||
// it has also created a tag with the name "/FOO"
|
||||
if (outerHTML.slice(-2) != "/>") {
|
||||
var tagName = "/" + el.tagName;
|
||||
var ns;
|
||||
// remove content
|
||||
while ((ns = el.nextSibling) && ns.tagName != tagName) {
|
||||
ns.removeNode();
|
||||
}
|
||||
// remove the incorrect closing tag
|
||||
if (ns) {
|
||||
ns.removeNode();
|
||||
}
|
||||
}
|
||||
el.parentNode.replaceChild(newEl, el);
|
||||
return newEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Public initializes a canvas element so that it can be used as canvas
|
||||
* element from now on. This is called automatically before the page is
|
||||
* loaded but if you are creating elements using createElement you need to
|
||||
* make sure this is called on the element.
|
||||
* @param {HTMLElement} el The canvas element to initialize.
|
||||
* @return {HTMLElement} the element that was created.
|
||||
*/
|
||||
initElement: function (el) {
|
||||
el = this.fixElement_(el);
|
||||
el.getContext = function () {
|
||||
if (this.context_) {
|
||||
return this.context_;
|
||||
}
|
||||
return this.context_ = new CanvasRenderingContext2D_(this);
|
||||
};
|
||||
|
||||
// do not use inline function because that will leak memory
|
||||
el.attachEvent('onpropertychange', onPropertyChange);
|
||||
el.attachEvent('onresize', onResize);
|
||||
|
||||
var attrs = el.attributes;
|
||||
if (attrs.width && attrs.width.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setWidth_(attrs.width.nodeValue);
|
||||
el.style.width = attrs.width.nodeValue + "px";
|
||||
} else {
|
||||
el.width = el.clientWidth;
|
||||
}
|
||||
if (attrs.height && attrs.height.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setHeight_(attrs.height.nodeValue);
|
||||
el.style.height = attrs.height.nodeValue + "px";
|
||||
} else {
|
||||
el.height = el.clientHeight;
|
||||
}
|
||||
//el.getContext().setCoordsize_()
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
||||
function onPropertyChange(e) {
|
||||
var el = e.srcElement;
|
||||
|
||||
switch (e.propertyName) {
|
||||
case 'width':
|
||||
el.style.width = el.attributes.width.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
case 'height':
|
||||
el.style.height = el.attributes.height.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onResize(e) {
|
||||
var el = e.srcElement;
|
||||
if (el.firstChild) {
|
||||
el.firstChild.style.width = el.clientWidth + 'px';
|
||||
el.firstChild.style.height = el.clientHeight + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
G_vmlCanvasManager_.init();
|
||||
|
||||
// precompute "00" to "FF"
|
||||
var dec2hex = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
function createMatrixIdentity() {
|
||||
return [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
function matrixMultiply(m1, m2) {
|
||||
var result = createMatrixIdentity();
|
||||
|
||||
for (var x = 0; x < 3; x++) {
|
||||
for (var y = 0; y < 3; y++) {
|
||||
var sum = 0;
|
||||
|
||||
for (var z = 0; z < 3; z++) {
|
||||
sum += m1[x][z] * m2[z][y];
|
||||
}
|
||||
|
||||
result[x][y] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function copyState(o1, o2) {
|
||||
o2.fillStyle = o1.fillStyle;
|
||||
o2.lineCap = o1.lineCap;
|
||||
o2.lineJoin = o1.lineJoin;
|
||||
o2.lineWidth = o1.lineWidth;
|
||||
o2.miterLimit = o1.miterLimit;
|
||||
o2.shadowBlur = o1.shadowBlur;
|
||||
o2.shadowColor = o1.shadowColor;
|
||||
o2.shadowOffsetX = o1.shadowOffsetX;
|
||||
o2.shadowOffsetY = o1.shadowOffsetY;
|
||||
o2.strokeStyle = o1.strokeStyle;
|
||||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
}
|
||||
|
||||
function processStyle(styleString) {
|
||||
var str, alpha = 1;
|
||||
|
||||
styleString = String(styleString);
|
||||
if (styleString.substring(0, 3) == "rgb") {
|
||||
var start = styleString.indexOf("(", 3);
|
||||
var end = styleString.indexOf(")", start + 1);
|
||||
var guts = styleString.substring(start + 1, end).split(",");
|
||||
|
||||
str = "#";
|
||||
for (var i = 0; i < 3; i++) {
|
||||
str += dec2hex[Number(guts[i])];
|
||||
}
|
||||
|
||||
if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
|
||||
alpha = guts[3];
|
||||
}
|
||||
} else {
|
||||
str = styleString;
|
||||
}
|
||||
|
||||
return [str, alpha];
|
||||
}
|
||||
|
||||
function processLineCap(lineCap) {
|
||||
switch (lineCap) {
|
||||
case "butt":
|
||||
return "flat";
|
||||
case "round":
|
||||
return "round";
|
||||
case "square":
|
||||
default:
|
||||
return "square";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements CanvasRenderingContext2D interface as described by
|
||||
* the WHATWG.
|
||||
* @param {HTMLElement} surfaceElement The element that the 2D context should
|
||||
* be associated with
|
||||
*/
|
||||
function CanvasRenderingContext2D_(surfaceElement) {
|
||||
this.m_ = createMatrixIdentity();
|
||||
|
||||
this.mStack_ = [];
|
||||
this.aStack_ = [];
|
||||
this.currentPath_ = [];
|
||||
|
||||
// Canvas context properties
|
||||
this.strokeStyle = "#000";
|
||||
this.fillStyle = "#000";
|
||||
|
||||
this.lineWidth = 1;
|
||||
this.lineJoin = "miter";
|
||||
this.lineCap = "butt";
|
||||
this.miterLimit = Z * 1;
|
||||
this.globalAlpha = 1;
|
||||
this.canvas = surfaceElement;
|
||||
|
||||
var el = surfaceElement.ownerDocument.createElement('div');
|
||||
el.style.width = surfaceElement.clientWidth + 'px';
|
||||
el.style.height = surfaceElement.clientHeight + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
el.style.position = 'absolute';
|
||||
surfaceElement.appendChild(el);
|
||||
|
||||
this.element_ = el;
|
||||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
}
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
contextPrototype.clearRect = function() {
|
||||
this.element_.innerHTML = "";
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.beginPath = function() {
|
||||
// TODO: Branch current matrix so that save/restore has no effect
|
||||
// as per safari docs.
|
||||
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.moveTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "moveTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.lineTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "lineTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
|
||||
aCP2x, aCP2y,
|
||||
aX, aY) {
|
||||
this.currentPath_.push({type: "bezierCurveTo",
|
||||
cp1x: aCP1x,
|
||||
cp1y: aCP1y,
|
||||
cp2x: aCP2x,
|
||||
cp2y: aCP2y,
|
||||
x: aX,
|
||||
y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
|
||||
// the following is lifted almost directly from
|
||||
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
|
||||
var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
|
||||
var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
|
||||
var cp2x = cp1x + (aX - this.currentX_) / 3.0;
|
||||
var cp2y = cp1y + (aY - this.currentY_) / 3.0;
|
||||
this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
|
||||
};
|
||||
|
||||
contextPrototype.arc = function(aX, aY, aRadius,
|
||||
aStartAngle, aEndAngle, aClockwise) {
|
||||
aRadius *= Z;
|
||||
var arcType = aClockwise ? "at" : "wa";
|
||||
|
||||
var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
|
||||
var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
|
||||
|
||||
var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
|
||||
var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
|
||||
|
||||
// IE won't render arches drawn counter clockwise if xStart == xEnd.
|
||||
if (xStart == xEnd && !aClockwise) {
|
||||
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
|
||||
// that can be represented in binary
|
||||
}
|
||||
|
||||
this.currentPath_.push({type: arcType,
|
||||
x: aX,
|
||||
y: aY,
|
||||
radius: aRadius,
|
||||
xStart: xStart,
|
||||
yStart: yStart,
|
||||
xEnd: xEnd,
|
||||
yEnd: yEnd});
|
||||
|
||||
};
|
||||
|
||||
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
};
|
||||
|
||||
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.stroke();
|
||||
};
|
||||
|
||||
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.fill();
|
||||
};
|
||||
|
||||
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
|
||||
var gradient = new CanvasGradient_("gradient");
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.createRadialGradient = function(aX0, aY0,
|
||||
aR0, aX1,
|
||||
aY1, aR1) {
|
||||
var gradient = new CanvasGradient_("gradientradial");
|
||||
gradient.radius1_ = aR0;
|
||||
gradient.radius2_ = aR1;
|
||||
gradient.focus_.x = aX0;
|
||||
gradient.focus_.y = aY0;
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.drawImage = function (image, var_args) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
|
||||
// to find the original width we overide the width and height
|
||||
var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
image.runtimeStyle.width = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
|
||||
// and remove overides
|
||||
image.runtimeStyle.width = oldRuntimeWidth;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
sx = sy = 0;
|
||||
sw = dw = w;
|
||||
sh = dh = h;
|
||||
} else if (arguments.length == 5) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
dw = arguments[3];
|
||||
dh = arguments[4];
|
||||
sx = sy = 0;
|
||||
sw = w;
|
||||
sh = h;
|
||||
} else if (arguments.length == 9) {
|
||||
sx = arguments[1];
|
||||
sy = arguments[2];
|
||||
sw = arguments[3];
|
||||
sh = arguments[4];
|
||||
dx = arguments[5];
|
||||
dy = arguments[6];
|
||||
dw = arguments[7];
|
||||
dh = arguments[8];
|
||||
} else {
|
||||
throw "Invalid number of arguments";
|
||||
}
|
||||
|
||||
var d = this.getCoords_(dx, dy);
|
||||
|
||||
var w2 = sw / 2;
|
||||
var h2 = sh / 2;
|
||||
|
||||
var vmlStr = [];
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
// For some reason that I've now forgotten, using divs didn't work
|
||||
vmlStr.push(' <g_vml_:group',
|
||||
' coordsize="', Z * W, ',', Z * H, '"',
|
||||
' coordorigin="0,0"' ,
|
||||
' style="width:', W, ';height:', H, ';position:absolute;');
|
||||
|
||||
// If filters are necessary (rotation exists), create them
|
||||
// filters are bog-slow, so only create them if abbsolutely necessary
|
||||
// The following check doesn't account for skews (which don't exist
|
||||
// in the canvas spec (yet) anyway.
|
||||
|
||||
if (this.m_[0][0] != 1 || this.m_[0][1]) {
|
||||
var filter = [];
|
||||
|
||||
// Note the 12/21 reversal
|
||||
filter.push("M11='", this.m_[0][0], "',",
|
||||
"M12='", this.m_[1][0], "',",
|
||||
"M21='", this.m_[0][1], "',",
|
||||
"M22='", this.m_[1][1], "',",
|
||||
"Dx='", mr(d.x / Z), "',",
|
||||
"Dy='", mr(d.y / Z), "'");
|
||||
|
||||
// Bounding box calculation (need to minimize displayed area so that
|
||||
// filters don't waste time on unused pixels.
|
||||
var max = d;
|
||||
var c2 = this.getCoords_(dx + dw, dy);
|
||||
var c3 = this.getCoords_(dx, dy + dh);
|
||||
var c4 = this.getCoords_(dx + dw, dy + dh);
|
||||
|
||||
max.x = Math.max(max.x, c2.x, c3.x, c4.x);
|
||||
max.y = Math.max(max.y, c2.y, c3.y, c4.y);
|
||||
|
||||
vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
|
||||
"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
|
||||
filter.join(""), ", sizingmethod='clip');");
|
||||
} else {
|
||||
vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;");
|
||||
}
|
||||
|
||||
vmlStr.push(' ">' ,
|
||||
'<g_vml_:image src="', image.src, '"',
|
||||
' style="width:', Z * dw, ';',
|
||||
' height:', Z * dh, ';"',
|
||||
' cropleft="', sx / w, '"',
|
||||
' croptop="', sy / h, '"',
|
||||
' cropright="', (w - sx - sw) / w, '"',
|
||||
' cropbottom="', (h - sy - sh) / h, '"',
|
||||
' />',
|
||||
'</g_vml_:group>');
|
||||
|
||||
this.element_.insertAdjacentHTML("BeforeEnd",
|
||||
vmlStr.join(""));
|
||||
};
|
||||
|
||||
contextPrototype.stroke = function(aFill) {
|
||||
var lineStr = [];
|
||||
var lineOpen = false;
|
||||
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
|
||||
var color = a[0];
|
||||
var opacity = a[1] * this.globalAlpha;
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
lineStr.push('<g_vml_:shape',
|
||||
' fillcolor="', color, '"',
|
||||
' filled="', Boolean(aFill), '"',
|
||||
' style="position:absolute;width:', W, ';height:', H, ';"',
|
||||
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
|
||||
' stroked="', !aFill, '"',
|
||||
' strokeweight="', this.lineWidth, '"',
|
||||
' strokecolor="', color, '"',
|
||||
' path="');
|
||||
|
||||
var newSeq = false;
|
||||
var min = {x: null, y: null};
|
||||
var max = {x: null, y: null};
|
||||
|
||||
for (var i = 0; i < this.currentPath_.length; i++) {
|
||||
var p = this.currentPath_[i];
|
||||
|
||||
if (p.type == "moveTo") {
|
||||
lineStr.push(" m ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "lineTo") {
|
||||
lineStr.push(" l ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "close") {
|
||||
lineStr.push(" x ");
|
||||
} else if (p.type == "bezierCurveTo") {
|
||||
lineStr.push(" c ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var c1 = this.getCoords_(p.cp1x, p.cp1y);
|
||||
var c2 = this.getCoords_(p.cp2x, p.cp2y);
|
||||
lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
|
||||
mr(c2.x), ",", mr(c2.y), ",",
|
||||
mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "at" || p.type == "wa") {
|
||||
lineStr.push(" ", p.type, " ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var cStart = this.getCoords_(p.xStart, p.yStart);
|
||||
var cEnd = this.getCoords_(p.xEnd, p.yEnd);
|
||||
|
||||
lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y - this.arcScaleY_ * p.radius), " ",
|
||||
mr(c.x + this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y + this.arcScaleY_ * p.radius), " ",
|
||||
mr(cStart.x), ",", mr(cStart.y), " ",
|
||||
mr(cEnd.x), ",", mr(cEnd.y));
|
||||
}
|
||||
|
||||
|
||||
// TODO: Following is broken for curves due to
|
||||
// move to proper paths.
|
||||
|
||||
// Figure out dimensions so we can do gradient fills
|
||||
// properly
|
||||
if(c) {
|
||||
if (min.x == null || c.x < min.x) {
|
||||
min.x = c.x;
|
||||
}
|
||||
if (max.x == null || c.x > max.x) {
|
||||
max.x = c.x;
|
||||
}
|
||||
if (min.y == null || c.y < min.y) {
|
||||
min.y = c.y;
|
||||
}
|
||||
if (max.y == null || c.y > max.y) {
|
||||
max.y = c.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
lineStr.push(' ">');
|
||||
|
||||
if (typeof this.fillStyle == "object") {
|
||||
var focus = {x: "50%", y: "50%"};
|
||||
var width = (max.x - min.x);
|
||||
var height = (max.y - min.y);
|
||||
var dimension = (width > height) ? width : height;
|
||||
|
||||
focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
|
||||
focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
|
||||
|
||||
var colors = [];
|
||||
|
||||
// inside radius (%)
|
||||
if (this.fillStyle.type_ == "gradientradial") {
|
||||
var inside = (this.fillStyle.radius1_ / dimension * 100);
|
||||
|
||||
// percentage that outside radius exceeds inside radius
|
||||
var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
|
||||
} else {
|
||||
var inside = 0;
|
||||
var expansion = 100;
|
||||
}
|
||||
|
||||
var insidecolor = {offset: null, color: null};
|
||||
var outsidecolor = {offset: null, color: null};
|
||||
|
||||
// We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
|
||||
// won't interpret it correctly
|
||||
this.fillStyle.colors_.sort(function (cs1, cs2) {
|
||||
return cs1.offset - cs2.offset;
|
||||
});
|
||||
|
||||
for (var i = 0; i < this.fillStyle.colors_.length; i++) {
|
||||
var fs = this.fillStyle.colors_[i];
|
||||
|
||||
colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
|
||||
|
||||
if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
|
||||
insidecolor.offset = fs.offset;
|
||||
insidecolor.color = fs.color;
|
||||
}
|
||||
|
||||
if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
|
||||
outsidecolor.offset = fs.offset;
|
||||
outsidecolor.color = fs.color;
|
||||
}
|
||||
}
|
||||
colors.pop();
|
||||
|
||||
lineStr.push('<g_vml_:fill',
|
||||
' color="', outsidecolor.color, '"',
|
||||
' color2="', insidecolor.color, '"',
|
||||
' type="', this.fillStyle.type_, '"',
|
||||
' focusposition="', focus.x, ', ', focus.y, '"',
|
||||
' colors="', colors.join(""), '"',
|
||||
' opacity="', opacity, '" />');
|
||||
} else if (aFill) {
|
||||
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
|
||||
} else {
|
||||
lineStr.push(
|
||||
'<g_vml_:stroke',
|
||||
' opacity="', opacity,'"',
|
||||
' joinstyle="', this.lineJoin, '"',
|
||||
' miterlimit="', this.miterLimit, '"',
|
||||
' endcap="', processLineCap(this.lineCap) ,'"',
|
||||
' weight="', this.lineWidth, 'px"',
|
||||
' color="', color,'" />'
|
||||
);
|
||||
}
|
||||
|
||||
lineStr.push("</g_vml_:shape>");
|
||||
|
||||
this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
|
||||
|
||||
//this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.fill = function() {
|
||||
this.stroke(true);
|
||||
};
|
||||
|
||||
contextPrototype.closePath = function() {
|
||||
this.currentPath_.push({type: "close"});
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
contextPrototype.getCoords_ = function(aX, aY) {
|
||||
return {
|
||||
x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
|
||||
y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
|
||||
}
|
||||
};
|
||||
|
||||
contextPrototype.save = function() {
|
||||
var o = {};
|
||||
copyState(this, o);
|
||||
this.aStack_.push(o);
|
||||
this.mStack_.push(this.m_);
|
||||
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.restore = function() {
|
||||
copyState(this.aStack_.pop(), this);
|
||||
this.m_ = this.mStack_.pop();
|
||||
};
|
||||
|
||||
contextPrototype.translate = function(aX, aY) {
|
||||
var m1 = [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[aX, aY, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.rotate = function(aRot) {
|
||||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.scale = function(aX, aY) {
|
||||
this.arcScaleX_ *= aX;
|
||||
this.arcScaleY_ *= aY;
|
||||
var m1 = [
|
||||
[aX, 0, 0],
|
||||
[0, aY, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
/******** STUBS ********/
|
||||
contextPrototype.clip = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.arcTo = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.createPattern = function() {
|
||||
return new CanvasPattern_;
|
||||
};
|
||||
|
||||
// Gradient / Pattern Stubs
|
||||
function CanvasGradient_(aType) {
|
||||
this.type_ = aType;
|
||||
this.radius1_ = 0;
|
||||
this.radius2_ = 0;
|
||||
this.colors_ = [];
|
||||
this.focus_ = {x: 0, y: 0};
|
||||
}
|
||||
|
||||
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
|
||||
aColor = processStyle(aColor);
|
||||
this.colors_.push({offset: 1-aOffset, color: aColor});
|
||||
};
|
||||
|
||||
function CanvasPattern_() {}
|
||||
|
||||
// set up externs
|
||||
G_vmlCanvasManager = G_vmlCanvasManager_;
|
||||
CanvasRenderingContext2D = CanvasRenderingContext2D_;
|
||||
CanvasGradient = CanvasGradient_;
|
||||
CanvasPattern = CanvasPattern_;
|
||||
|
||||
})();
|
||||
|
||||
} // if
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Загрузка…
Ссылка в новой задаче