Google Charts, Load Different Charts on One Page - charts

I am trying to load a number of google charts on one page, however I am falling over trying to add the second.
I am testing the new Google Analytics SuperProxy feature, so the Data is being drawn in from a dataSourceURL
My code is below, as is the code works, however as soon as I uncomment out the section everything fails to load and for the life of me I cannot figure out how to fix this.
Hope someone can help.
<html>
<head>
<title>jmit</title>
<script type="text/javascript"
src='https://www.google.com/jsapi?autoload={"modules":[{"name":"visualization","version":"1","packages":["corechart","geochart"]}]}'>
</script>
<script type="text/javascript">
google.setOnLoadCallback(drawCharts);
function drawCharts(){
/*var data1 = new google.visualization.ChartWrapper({
"containerId":"Chart1_div",
"dataSourceUrl":"https://alexandalexaanalytics.appspot.com/query?id=ahdzfmFsZXhhbmRhbGV4YWFuYWx5dGljc3IVCxIIQXBpUXVlcnkYgICAgIDwiwoM&format=data-table-response",
"refreshInterval":3600,
"chartType":"GeoChart",
"options":{
"width": 630,
"height": 440,
"title": "test"
}
});
data1.draw();
}
*/
var data2 = new google.visualization.ChartWrapper({
"containerId":"Chart2_div",
"dataSourceUrl":"https://alexandalexaanalytics.appspot.com/query?id=ahdzfmFsZXhhbmRhbGV4YWFuYWx5dGljc3IVCxIIQXBpUXVlcnkYgICAgIDwiwoM&format=data-table-response",
"refreshInterval":3600,
"chartType":"ColumnChart",
"options":{
"width": 630,
"height": 440,
"title": "test"
}
});
data2.draw();
}
</script>
</head>
<body>
<h1>Test</h1>
<div id="Chart1_div"></div>
<p></p>
<div id="Chart2_div"></div>
</body>
</html>

You have an extra "}" after the data1.draw(); line, which is closing the drawCharts function. Removing that allows the queries to run, but you end up with conflicts between the two charts sending out queries to the same source for the same data. Since they are both running off the same data, it makes sense to use one query that populates both charts at the same time:
function drawCharts(){
var query = new google.visualization.Query('https://alexandalexaanalytics.appspot.com/query?id=ahdzfmFsZXhhbmRhbGV4YWFuYWx5dGljc3IVCxIIQXBpUXVlcnkYgICAgIDwiwoM&format=data-table-response');
query.setRefreshInterval(3600);
query.send(function (response) {
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
var dataTable = response.getDataTable();
var data1 = new google.visualization.ChartWrapper({
"containerId":"Chart1_div",
"dataTable":dataTable,
"refreshInterval":3600,
"chartType":"GeoChart",
"options":{
"width": 630,
"height": 440,
"title": "test"
}
});
data1.draw();
var data2 = new google.visualization.ChartWrapper({
"containerId":"Chart2_div",
"dataTable":dataTable,
"refreshInterval":3600,
"chartType":"ColumnChart",
"options":{
"width": 630,
"height": 440,
"title": "test"
}
});
data2.draw();
});
}
see it working here: http://jsfiddle.net/asgallant/j5eea/

There was a bug in the Google Analytics SuperProxy code which stopped multiple charts from running on a single page. This has now been updated, example html from githb is below.
Loads of thanks to asgallant for looking into this without his comments I wouldnt have know what to search for to answer this.
<! --
Based on demo video: https://www.youtube.com/watch?v=8Or8KIhpsqg
This shows you how to power 2 Pie Charts using the Google Analytics
superProxy. Each chart uses, as a data source, a public superProxy URL
with the format set to Data Table Response.
-->
<html>
<head>
<title>Pie!</title>
<!--Load the AJAX API-->
<script type="text/javascript"
src='https://www.google.com/jsapi?autoload={"modules":[{"name":"visualization","version":"1"}]}'>
</script>
<!-- Visualization -->
<!-- https://developers.google.com/chart/interactive/docs/reference#google.visualization.drawchart -->
<script type="text/javascript">
google.setOnLoadCallback(drawVisualization);
function drawVisualization() {
var browserWrapper = new google.visualization.ChartWrapper({
// Example Browser Share Query
"containerId": "browser",
// Example URL: http://your-application-id.appspot.com/query?id=QUERY_ID&format=data-table-response
"dataSourceUrl": "REPLACE WITH Google Analytics superProxy PUBLIC URL, DATA TABLE RESPONSE FORMAT",
"refreshInterval": REPLACE_WITH_A_TIME_INTERVAL,
"chartType": "PieChart",
"options": {
"showRowNumber" : true,
"width": 630,
"height": 440,
"is3D": true,
"title": "REPLACE WITH TITLE"
}
});
var countryWrapper = new google.visualization.ChartWrapper({
// Example Country Share Query
"containerId": "country",
"dataSourceUrl": "REPLACE WITH Google Analytics superProxy PUBLIC URL, DATA TABLE RESPONSE FORMAT",
"refreshInterval": REPLACE_WITH_A_TIME_INTERVAL,
"chartType": "PieChart",
"options": {
"showRowNumber" : true,
"width": 630,
"height": 440,
"is3D": true,
"title": "REPLACE WITH TITLE"
}
});
browserWrapper.draw();
countryWrapper.draw();
}
</script>
</head>
<body>
<h1>Pie!</h1>
<div id="browser" style="margin:auto;width:630px;"></div>
<div id="country" style="margin:auto;width:630px;"></div>
</body>
</html>

Related

How to force a close spider after adding a new group of markers?

I use the following libraries:
leaflet.js
leaflet.markercluster.js
leaflet.subgroup.js
and my own script.
I have 3 marker types, all in a different subgroups. 11 Markers are on the same location. When I open the cluster the spider is shown, on unchecking 1 marker type, the spider is closed and the counter shows the right value. When I reopen the cluster, modified spider is shown.
Checking the marker type does not put the extra spider-marker on the screen, but also does not close the spider.
How can I force the spider to be closed?
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Bee-Idees kaart</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.3.0/dist/MarkerCluster.css" />
<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster#1.3.0/dist/MarkerCluster.Default.css" />
<script type='text/javascript' src='https://unpkg.com/leaflet#1.3.1/dist/leaflet.js'></script>
<script type='text/javascript' src='https://unpkg.com/leaflet.markercluster#1.3.0/dist/leaflet.markercluster.js'></script>
<script type='text/javascript' src='https://unpkg.com/leaflet.featuregroup.subgroup#1.0.2/dist/leaflet.featuregroup.subgroup.js'></script>
</head>
<body>
<div id="map" style="height: 580px; border: 1px solid #AAA;"></div>
</body>
<script>
markers = [
{
"name": "Moordrecht",
"lat": 52.019716,
"lng": 5.183973,
"marker": "green"
},
{
"name": "Zoetermeer",
"lat": 52.046985,
"lng": 4.478968,
"marker": "red"
},
{
"name": "Gouda",
"lat": 52.021616,
"lng": 4.687917,
"marker": "green"
},
{
"name": "Gouda 2",
"lat": 52.021616,
"lng": 4.687917,
"marker": "blue"
},
{
"name": "Gouda 3",
"lat": 52.021616,
"lng": 4.687917,
"marker": "red"
}
];
</script>
<script>
var iconBase = 'css\\icons\\';
// Standard fields
var groupLabel = [];
groupLabel[0] = "Red";
groupLabel[1] = "Blue";
groupLabel[2] = "Green";
var map = L.map('map', {
center: [52.021616, 4.85],
zoom: 10
});
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
subdomains: ['a', 'b', 'c']
}).addTo(map);
var mcg = L.markerClusterGroup(),
group0 = L.featureGroup.subGroup(mcg),
group1 = L.featureGroup.subGroup(mcg),
group2 = L.featureGroup.subGroup(mcg),
control = L.control.layers(null, null, { collapsed: false }), i, a, title, m;
mcg.addTo(map);
var markerClusters = L.markerClusterGroup();
for (var i = 0; i < markers.length; ++i) {
var iconName = iconBase + markers[i].marker + ".png";
var myIcon = L.icon({ iconUrl: iconName, iconAnchor: [20, 40] })
var popup = '<h2>' + markers[i].name + '</h2>';
var m = L.marker( [markers[i].lat, markers[i].lng], {icon: myIcon, title: markers[i].name} )
.bindPopup( popup );
if (markers[i].marker === "red") { m.addTo(group0);}
else if (markers[i].marker === "blue") { m.addTo(group1); }
else if (markers[i].marker === "green") { m.addTo(group2); }
}
control.addOverlay(group0, groupLabel[0]);
control.addOverlay(group1, groupLabel[1]);
control.addOverlay(group2, groupLabel[2]);
control.addTo(map);
group0.addTo(map);
group1.addTo(map);
group2.addTo(map);
map.addLayer(mcg);
map.on('overlayadd', function() {
// How can I close the spider
});
map.on('overlayremove', function() {
});
</script>
</html>
Pasted the code provided in your question into Plunker for a live demo: https://plnkr.co/edit/ZzKRs5NnrJeWfBKWOA3R?p=preview
Hum looks like you are hitting a bug in Leaflet.markercluster.
When you tick in your SubGroup in the Layers Control, the former will use the parent group's (mcg in your case) addLayers method if it exists.
While MCG addLayer (single) correctly unspiderfies, this step is missing in addLayers (batch).
Reproducing the bug with a more simple example: https://plnkr.co/edit/G4GAjax5fi3LUqJRLLPN?p=preview (without Leaflet.FeatureGroup.SubGroup plugin)
Then it should be quite simple to fix this bug: simply unspiderfy, similarly to how it is done in addLayer:
if (this._map && this._unspiderfy) {
this._unspiderfy();
}
Updated simple example: https://plnkr.co/edit/lgvRtiRo7nh9VaWuI0RM?p=preview
Updated your code: https://plnkr.co/edit/CjjcHlZW6vpJ6075Ma3F?p=preview

Stacked and grouped column google charts

I have a set of
var data = new google.visualization.arrayToDataTable([
['Month', 'Rate', 'Interest', 'Principle', 'Balance','New Balance'],
['Jan', 321, 600, 816, 319, 890],
['Feb', 163, 231, 539, 594, 678],
]);
I want to Stack and Group the above data 2 at a time (Rate,Interest), (Principle, Balance) and last one New Balance as a separate Bar. How can this be achieved in Google charts.
I am new to this whole thing..any help appreciated here is some on info to get grouping data into 2 bars how can this be improved to fit my requirement.
How to make a grouped bar stack with Google charts?
using the option isStacked, if set to true, stacks the elements for all series at each domain value.
this means the values for all columns for each row will be displayed in one stack
as such, to create separate stacks, the data will need to be separated into rows
something like the following working snippet...
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Month\nCategory', 'Bottom', 'Top'],
['Rate / Interest', 321, 600],
['Principle / Balance\nJan', 816, 319],
['New Balance', 890, null],
['Rate / Interest', 163, 231],
['Principle / Balance\nFeb', 539, 594],
['nNew Balance', 678, null],
]);
var options = {
height: 400,
isStacked: true,
legend: 'none',
width: 800
};
var chartDiv = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(chartDiv);
chart.draw(data, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
<html>
<head>
<title>Google Charts Tutorial</title>
<script type = "text/javascript" src = "https://www.gstatic.com/charts/loader.js">
</script>
<script type = "text/javascript">
google.charts.load('current', {packages: ['corechart']});
</script>
</head>
<body>
<div id = "container" style = "width: 550px; height: 400px; margin: 0 auto">
</div>
<script language = "JavaScript">
function drawChart() {
// Define the chart to be drawn.
var data = google.visualization.arrayToDataTable([
['Year', 'Asia', 'Europe'],
['2012', 900, 390],
['2013', 1000, 400],
['2014', 1170, 440],
['2015', 1250, 480],
['2016', 1530, 540]
]);
var options = {title: 'Population (in millions)', isStacked:true};
// Instantiate and draw the chart.
var chart = new google.visualization.BarChart(document.getElementById('container'));
chart.draw(data, options);
}`enter code here`
google.charts.setOnLoadCallback(drawChart);
</script>
</body>
</html>

Export google chart in a dashboard as png

I am trying to export google chart in a dashboard to png image through a button. But I get the following error-
One or more participants failed to draw()
undefined is not a function
Here is the code:
<html>
<head>
<!--Load the AJAX API-->
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
// Load the Visualization API and the controls package.
google.load('visualization', '1.0', {'packages':['controls']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawDashboard);
// Callback that creates and populates a data table,
// instantiates a dashboard, a range slider and a pie chart,
// passes in the data and draws it.
function drawDashboard() {
// Create our data table.
var data = google.visualization.arrayToDataTable([
['Name', 'Donuts eaten'],
['Michael' , 5],
['Elisa', 7],
['Robert', 3],
['John', 2],
['Jessica', 6],
['Aaron', 1],
['Margareth', 8]
]);
// Create a dashboard.
var dashboard = new google.visualization.Dashboard(
document.getElementById('dashboard_div'));
// Create a range slider, passing some options
var select = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'filter_div',
'options': {
'filterColumnLabel': 'Donuts eaten'
}
});
// Create a pie chart, passing some options
var chart = new google.visualization.ChartWrapper({
'chartType': 'ColumnChart',
'containerId': 'chart_div',
'options': {
'width': 500,
'height': 300,
'legend': 'right'
}
});
google.visualization.events.addListener(chart, 'ready', function(e) {
document.getElementById('png').outerHTML = 'Printable version';
});
// Establish dependencies, declaring that 'filter' drives 'pieChart',
// so that the pie chart will only display entries that are let through
// given the chosen slider range.
dashboard.bind(select, chart);
// Draw the dashboard.
dashboard.draw(data);
}
</script>
</head>
<body>
<div id='png'></div>
<!--Div that will hold the dashboard-->
<div id="dashboard_div">
<!--Divs that will hold each control and chart-->
<div id="filter_div"></div>
<div id="chart_div"></div>
</div>
</body>
</html>
Please help me where I am going wrong.
A fiddle always helps!
https://jsfiddle.net/kujxsn7z/
The things I changed:
Created a div to hold the PNG output Your attempt to call
getImageURI() was failing because you were applying it on a
chartWrapper, not on a chart object. So to fix this you need to call,
in your instance:
chart.getChart().getImageURI()
So you reference your chartWrapper, use getChart to refer to the chart it contains, and then execute getImageIRI() on the chart object itself.

$create(Sys.Extended.UI.ResizableControlBehavior, ...) fails with error "Unable to get property 'UI' of undefined..." unless a control already exists

There seems to be 2 separate issues behind this error. One is using scriptmanager instead of toolkitscriptmanager. I'm using toolkitscriptmanager but I get this error if I try to $create a control/behavior without already having one on the page. If I have a control on the page, without changing anything else, there's no error.
I'm using using the .net4.0 version of AjaxControlToolkit.
works:
<div id="divTest" style="height:500px; width:500px; background:#ccc">test</div>
<script>
Sys.Application.add_init(function () {
$create(
Sys.Extended.UI.ResizableControlBehavior,
{
"ClientStateFieldID": "ResizableControlExtender1_ClientState",
"HandleCssClass": "handle",
"HandleOffsetX": 0,
"HandleOffsetY": 0,
"MaximumHeight": 800,
"MaximumWidth": 800,
"MinimumHeight": 220,
"MinimumWidth": 478,
"ResizableCssClass": "resizing",
"id": "ResizableControlBehavior1"
}, null, null, $get("divTest"));
});
</script>
<div id="divWrapper" runat="server"></div>
<asp:ResizableControlExtender ID="divWrapper_ResizableControlExtender"
runat="server"
TargetControlID="divWrapper"
HandleCssClass="handle"
ResizableCssClass="resizing"
MaximumHeight="800"
MaximumWidth="800"
MinimumHeight="220"
MinimumWidth="478"
HandleOffsetX="0"
HandleOffsetY="0"></asp:ResizableControlExtender>
doesn't work:
<div id="divTest" style="height:500px; width:500px; background:#ccc">test</div>
<script>
Sys.Application.add_init(function () {
$create(
Sys.Extended.UI.ResizableControlBehavior,
{
"ClientStateFieldID": "ResizableControlExtender1_ClientState",
"HandleCssClass": "handle",
"HandleOffsetX": 0,
"HandleOffsetY": 0,
"MaximumHeight": 800,
"MaximumWidth": 800,
"MinimumHeight": 220,
"MinimumWidth": 478,
"ResizableCssClass": "resizing",
"id": "ResizableControlBehavior1"
}, null, null, $get("divTest"));
});
</script>
Problem solved!
All i needed to do was dig through the source of a working page and find this script tag and then copy it over to the broken version:
<script src="/myapp/mypage.aspx?_TSM_HiddenField_=ScriptManager1_HiddenField&_TSM_CombinedScripts_=%3b%3bAjaxControlToolkit%2c+Version%3d4.1.7.607%2c+Culture%3dneutral%2c+PublicKeyToken%3d28f01b0e84b6d53e%3aen-US%3afc974eef-02bb-4a84-98bd-02b839b496d1%3ade1feab2%3af9cec9bc%3a6beb6cd0" type="text/javascript"></script>
I'm just kidding. I switched to jquery.

How to get select's value in jqGrid when using <select> editoptions on a column

I have a couple of columns in jqGrid with edittype="select". How can I read the option value of the value currently selected in a particular row?
e.g.: When I provide the following option, how do I get "FE" for FedEx, etc.
editoption: { value: “FE:FedEx; IN:InTime; TN:TNT” }
getRowData() for the rowId/cellname returns only the text/displayed component of the select.
If I set a "change" data event on the column, the underlying fires change events only on mouse clicks, and not keyboard selects (there's numerous references to generic selects and mouse/keyboard issues).
Bottomline, when a new value is selected, I need to know the option value at the time of the change, and also prior to posting to the server.
You have to set the formatter of the column to 'select'
Example from the wiki:
colModel : [ {name:'myname',
edittype:'select', formatter:'select',
editoptions:{value:"1:One;2:Two"}} ...
]
See more here jqgridwiki
I was having the same problem and this worked like a charm
I just solved this question by using setting JqGrid unformat option and use the following function for unformatting cell value.
function Unformat_Select(cellvalue, options, cellobject)
{
var unformatValue = '';
$.each(options.colModel.editoptions.value, function (k, value)
{
if (cellvalue == value)
{
unformatValue = k;
}
});
return unformatValue;
}
The above method will be called everytime when grid need cell data like when you call "getRowData" method. However, my function only support key-paired value edit option. You need to change your data like the following pattern.
editoption:
{
value:
{
FE:'FedEx',
IN:'InTime',
TN:'TNT'
}
}
For more information about unformat option, you can see at the following link.
JqGrid Wiki - Custom Formatter
PS. It's possible to modify my function to support client-side dropdownlist value. But I think it's impossible to apply this function for server-side dropdownlist value.
Update
In the latest jqGrid 3.8.1, I just found some bug when user cancel editing row(or programmatically call "restoreRow" method), jqGrid will create label by using key of data (instead of value of data). I create the following function to fix this issue. For use this, you must it as custom formatter function of this column. This function maps cell value to value of list by comparing key or value.
function JqGridInlineEditor_SelectFormatter(cellvalue, options, rowObject)
{
var temp = '';
$.each(options.colModel.editoptions.value, function (key, value)
{
if (cellvalue == key || cellvalue == value)
{
temp = value;
return false;
}
});
return temp;
}
So, you can send key or value as column data to be rendered by the above custom formatter.
If in case you have requirement where each row has dropdown and it has values like
FE:'FedEx',
IN:'InTime',
TN:'TNT'
Now instead of saving the data to backend on dropdown change event;you want to save data on "Save" button click at row level but want to save dropdwon selected value (TN) not display text(TNT). You can create another hidden field to set selected country code on inline editing of dropdown. Here when user exit after cell inline editing afterSaveCell method will be called and you can set it as mentioned in below code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>http://stackoverflow.com/q/9655426/315935</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="stylesheet" type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/redmond/jquery-ui.css" />
<link rel="stylesheet" type="text/css" href="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.3.1/css/ui.jqgrid.css" />
<style type="text/css">
html, body { font-size: 75%; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.3.1/js/i18n/grid.locale-en.js"></script>
<script type="text/javascript">
$.jgrid.no_legacy_api = true;
$.jgrid.useJSON = true;
</script>
<script type="text/javascript" src="http://www.ok-soft-gmbh.com/jqGrid/jquery.jqGrid-4.3.1/js/jquery.jqGrid.src.js"></script>
<script type="text/javascript">
//<![CDATA[
/*global $ */
/*jslint devel: true, browser: true, plusplus: true, nomen: true, unparam: true */
$(document).ready(function () {
'use strict';
var listOptions = "CN:Canada; US:United States; FR:France; IN:India";
var mydata = [{
name: "Toronto",
country: "CN",
continent: "North America",
countrycode: "CN"
}, {
name: "New York City",
country: "US",
continent: "North America",
countrycode: "US"
}, {
name: "Silicon Valley",
country: "US",
continent: "North America",
countrycode: "US"
}, {
name: "Paris",
country: "FR",
continent: "Europe",
countrycode: "FR"
}, {
name: "Pune",
country: "IN",
continent: "Asia",
countrycode: "IN"
}]
$("#gvManageMapping").jqGrid({
data: mydata,
datatype: "local",
colNames: ["Name", "Country", "Continent", "countrycode"],
colModel: [{
name: 'name',
index: 'name',
editable: false,
},
{
name: 'country',
index: 'country',
editable: true, edittype: "select", formatter: 'select', editoptions: {
value: listOptions,
}, editrules: { required: true, integer: false }, formatter: "select"
},
{
name: 'continent',
index: 'continent',
editable: false,
},
{
name: 'countrycode',
index: 'countrycode',
editable: false
}],
afterSaveCell: function (rowid, cellname, value) {
var selectedCountryCode, $this;
if (cellname === 'country') {
$this = $(this);
selectedCountryCode = $this.jqGrid("getCell", rowid, 'country');
$this.jqGrid("setCell", rowid, 'countrycode', selectedCountryCode);
}
},
pager: '#pager',
'cellEdit': true,
'cellsubmit': 'clientArray',
editurl: 'clientArray'
});
});
//]]>
</script>
</head>
<body>
<table id="gvManageMapping"><tr><td /></tr></table>
<div id="pager"></div>
</body>
</html>
The documentation for getRowData states:
Do not use this method when you editing the row or cell. This will return the cell content and not the actuall value of the input element
Is the row still being edited when you call getRowData()?
Update
Agreed, jqGrid does not handle <select> very well. In my application I actually was able to get around this by not specifying an edit option (meaning, key/value were both "FedEx"); the translation to ID is then done on the server. This is not the right way to code this, but it worked well enough for my needs at the time.