Generate pdf with pdfmake in angular js with text formatting - javascript

I am showing data response from server in a popup. I am trying to download that data as pdf using pdfmake plugin. Data is generated as pdf, but the challenging is I need to generate pdf file just like in the html page. I am attaching a sample data from the html popup.
My JS code
$scope.download_pdf = function(questions,temptitle,extid,info){
var content = [];
var today = moment().format('lll');
var docDefinition = {
content: content,
styles: {
header: {
fillColor: '#6faadc',
color:'#18b0ff',
fontSize:16,
bold: true,
margin:[5,35,0,0]
},
questionStyle: {
width: '100%',
fontSize: 13,
bold: true,
margin:[5,20,20,0],
},
answerStyle: {
width: '100%',
fontSize: 13,
margin:[20,10,0,0],
color:'#57585a'
},
date: {
color:'#57585a'
}
}
};
content.push({image:compLogo,width:100,margin:[5,0,0,0],alignment: 'left' });
content.push({text: today,style: 'date',regular: true,fontSize: 10,margin:[0,-20,0,0],alignment: 'right'})
content.push({text: $scope.labelForexternalId+": "+extid,style: 'date',regular: true,fontSize: 10,margin:[5,80,0,0],alignment: 'left'})
content.push({text: $scope.labelForadditionalInfo+": "+info,style: 'date',regular: true,fontSize: 10,margin:[5,10,0,0],alignment: 'left'})
content.push({ text: temptitle, style: 'header' })
if($scope.QuestTemplateAnswer[0]['isApproved'] == true || $scope.isVerified == true){
var validatedBy = "Approved by: "+$scope.QuestTemplateAnswer[0]['validatedBy']+ " on "+moment($scope.QuestTemplateAnswer[0]['updatedAt']).format('ll');
content.push({text: validatedBy,style: 'date',regular: true,fontSize: 9,margin:[5,0,0,0]})
}
for(var i = 0; i < questions.length; i++){
var index = i + 1
content.push({ text: index+'. '+questions[i].question, style: 'questionStyle' })
if(questions[i].answers){
for(var j = 0; j < questions[i].answers.length; j++){
content.push([{ text: questions[i].answers[j].answer, style: 'answerStyle' }])
}
}else if(questions[i].answer){
content.push({ text: questions[i].answer, style: 'answerStyle' })
}else if(questions[i].files && questions[i].files.length > 0){
for(var k = 0; k < questions[i].files.length; k++){
content.push({image:$scope.content_images[i][k],width:300,height:200,margin:[20,10,0,0],alignment: 'left'})
content.push({ text: questions[i].files[k].caption, style: 'answerStyle' })
}
}
if(questions[i].subQuestion){
content.push({ text: index+'. '+questions[i].subQuestion.question, style: 'questionStyle', margin:[15,20,20,0]})
if(questions[i].subQuestion.answers){
for(var j = 0; j < questions[i].answers.length; j++){
content.push([{ text: questions[i].subQuestion.answers[j].answer, style: 'answerStyle', margin:[30,20,20,0]}])
}
}else if(questions[i].answer){
content.push({ text: questions[i].subQuestion.answer, style: 'answerStyle', margin:[30,10,20,0]})
}
}
}
// pdfMake.createPdf(docDefinition).open();
pdfMake.createPdf(docDefinition).download(temptitle+".pdf");//Create PDF
}
I need generate pdf file exactly like the image. Please guide me how to do this.
Thanks,
Sankar.

What you want is to screenshot the popup and make it a pdf? If that is here is an example that works fine with me:
1) First you need to import :
<!--html2canvas-->
<script type="text/javascript" src="~/Scripts/html2canvas.js"></script>
<!--PdfMAKER-->
<script type="text/javascript" src="~/Scripts/pdfmake.min.js"></script>
<script src="//kendo.cdn.telerik.com/2016.3.914/js/kendo.all.min.js"></script>
2) In your html code it should be like this:
<div id="exportthis">
<!--ALL YOUR HTML CODE OF YOUR POPUP-->
</div>
3)In your javascript code:
function imprimir() {
html2canvas(document.getElementById('exportthis'), {
onrendered: function (canvas) {
var data = canvas.toDataURL();
var docDefinition = {
content: [{
image: data,
width: 500,
}]
};
pdfMake.createPdf(docDefinition).download("Titulo.pdf");
}
});
}
DESCRIPTION:
What i do is to load de with the info and de style i want and then i have a button that execute the function print().
Let me know if it works for you and if it doesn't i will check my example because maybe it forget something.

Related

I need to merge cells in a table. Exceljs library

There was a problem.
I create worksheet in library EXCELJS
function save_export(){
var workbook = new ExcelJS.Workbook();
var worksheet = workbook.addWorksheet('sheet', {
pageSetup:{paperSize: 9, orientation:'portrait',fitToPage: true,fitToHeight : 99 , fitToWidth : 1, horizontalCentered: true}
});
var tfoot = [];
$(element).each(function(index_tfoot, element_tfoot) {
$($(element_tfoot).find("#tfoot tr")).each(function(index_tr, element_tr) {
$(this.cells).each(function(index_td, element_td) {
tfoot.push($(element_td).text());
});
var row_foot = worksheet.addRow(tfoot);
row_foot.eachCell(function(cell,rowNumber) {
cell.font = { name: 'Verdana', size: 11};
cell.alignment = {horizontal: 'center' };
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" }
};
<!-- if(cell._value.model.value === 'ИТОГО'){ -->
<!-- var c = 'A'; -->
<!-- var r = cell._row._number; -->
<!-- var sum = c+r; -->
<!-- worksheet.mergeCells(sum + ': B31'); -->
<!-- } // ПОЛУЧИЛ ИТОГО -->
});
tfoot = [];
});
});
});
workbook.xlsx.writeBuffer().then(function (data) {
var blob = new Blob([data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
saveAs(blob, "Исходная выборка.xlsx");
});
};
when I merge cells, my merged rows over cells as in the screenshot "before". You see what number 238 disappear.
I need the cells to move.
[1]: https://i.stack.imgur.com/kyquZ.png - before
[2]: https://i.stack.imgur.com/0DeRc.png - after
How we see in screenshots, cells merge and value ​​disappear ((
need to shift(
I have faced the same problem.The data needs to be added after merging the cells. Please refer the documentation https://github.com/exceljs/exceljs#merged-cells

Graph 1 hangs when function called second time

I want to generate graphs dynamically when I click on a button. My problem is when I click the button second time, the first graphs blocks displaying data.
The problem is with setInterval used within addChart function but I can't figure out how to rectify the issue. Is there some sanity in my logic or not?
<script>
function addChart() {
chartCount++;
var name = document.getElementById("chartName").value;
chartDict[name] = chartCount;
chart[chartCount] = new CanvasJS.Chart("chartContainer" + chartCount, {
title: {
text: name
},
axisY: {
includeZero: false,
maximum: 70
},
axisX: {
valueFormatString: "hh:mm tt"
},
data: [{
type: "line",
dataPoints: dps[chartCount]
}]
});
updateChart[chartCount] = function(count) {
count = count || 1;
for (var j = 0; j < count; j++) {
dps[chartCount].push({
x: xVal[chartCount],
y: yVal[chartCount]
});
xVal[chartCount] = new Date()
//console.log(xVal)
//xVal++;
}
if (dps[chartCount].length > dataLength) {
dps[chartCount].shift();
}
chart[chartCount].render();
};
intervals[chartCount] = setInterval(updateChart[chartCount], 1000)
}
</script>
You can try below workaround :
Remove the chart's inner content from DOM before reassigning the new values to chart element
Below is link to use remove() method of jquery:
https://api.jquery.com/remove/

Leaflet Tag Filter Button and MarkerCluster.LayerSupport Plugins - Integration to make filters on click update the clusters

I have 2 plugins that I want to work together: Leaflet Tag Filter Button made by maydemirx and MarkerCluster.LayerSupport made by ghybs (both awesome people and plugins btw). What I want to happen is when I click a filter on the tag filter button, I would like the marker clusters to update based on the new filter (like this). So a cluster of 5 points becomes 2, or a cluster of 10 becomes 1 marker. I succeeded in adding the layerSupported cluster to my map, so there's no hiccup there. I am unsure, though, how to integrate the supported cluster with the tag filter buttons, as they are two separate entities.
The Leaflet Tag Filter Button does support an update() method and an enablePruneCluster method, both of which sound like they could be used to achieve what I'm looking for. Yet, when I apply them individually to the filter buttons, they don't work. I'm either applying the filter button methods incorrectly, creating the layerSupported cluster inaccurately, and/or the plugins were not made to be compatible with each other.
Here is my code for generating the layer supported marker cluster group:
var clusters = L.markerClusterGroup.layerSupport({maxClusterRadius:75}),
group1 = L.layerGroup();
var getjson = $.getJSON("map-v2.geojson",function(data){
var bev = L.geoJson(data,{
pointToLayer: function(feature,latlng){
var marker = L.marker(latlng, { tags: feature.properties.Genres.concat(feature.properties.Creator)});
marker.bindPopup('<p align=center>' + '<strong>Title: </strong>' + feature.properties.Title + '<br/><img src="' + feature.properties.Thumbnail_URL + '"/><br/>' + '<strong>Date: </strong>' + feature.properties.Date + '<br/>' + '<strong>Creator: </strong>' + feature.properties.Creator, {minWidth : 250});
return marker;
}
});
bev.addTo(group1);
clusters.addLayer(group1);
map.addLayer(clusters);
});
// Here is where I add the layer supported clusters to the map.
clusters.checkIn(group1);
clusters.addTo(map);
Here is the section where I generate the tag filter buttons:
// Here is the code block for the Tag Filter Button. I start by accessing a tags file that has the data that I use for filter options. I should note that the genres.addToRelated is not working (it is supposed to link the 2 buttons together to work in conjunction with each other).
$.getJSON('tags.json', function(data) {
var genres = L.control.tagFilterButton({
data: data.genres,
filterOnEveryClick: true,
icon: '<i class="fas fa-tags"></i>',
}).addTo(map);
var creators = L.control.tagFilterButton({
data: data.creators,
filterOnEveryClick: true,
icon: '<i class="fas fa-user-edit"></i>',
}).addTo(map);
jQuery('.easy-button-button').click(function() {
target = jQuery('.easy-button-button').not(this);
target.parent().find('.tag-filter-tags-container').css({
'display' : 'none',
});
});
genres.addToRelated(creators);
genres.update(clusters);
genres.enablePruneCluster(clusters);
});
If you'd like to see it all in action, here is a plunker of the code.
Strangely the Leaflet Tag Filter Button plugin and/or latest Leaflet version look to have some bugs / listeners that may pause the script (hence the browser) when the Web Console is open.
Once those bugs are fixed, there are still bugs with the "addToReleated" method. Since I do not know what it is supposed to do, I will just ignore it for now, and let you possibly fix it with the plugin author.
As for integration with the Leaflet.markercluster plugin, it really does not look like the 1st plugin is supposed to support it. PruneCluster plugin (for which the enablePruneCluster method of Tag Filter Button is intended) works very differently from Leaflet.markercluster.
By having a look into the source code of Tag Filter Button, it seems that you could implement it by adapting the enablePruneCluster code and the hide function of the call to registerCustomSource in the default _prepareLayerSources. The idea is to avoid using directly the _map, and use an MCG instead.
Since you can directly handle calls to MCG addLayers and removeLayers within the hide function, there is really no need for the Leaflet.MarkerCluster.LayerSupport plugin at all.
Here is a quick and dirty implementation, called "enableMCG":
////////////////////////////////////////////////
// Quick and dirty implementation of enableMCG
////////////////////////////////////////////////
L.Control.TagFilterButton.include({
// Goal: read from MCG instead of from _map
enableMCG: function(mcgInstance) {
this.registerCustomSource({
name: 'mcg',
source: {
mcg: mcgInstance,
hide: function(layerSource) {
var releatedLayers = [];
for (
var r = 0; r < this._releatedFilterButtons.length; r++
) {
releatedLayers = releatedLayers.concat(
this._releatedFilterButtons[r].getInvisibles()
);
}
var toBeRemovedFromInvisibles = [],
i,
toAdd = [];
for (var i = 0; i < this._invisibles.length; i++) {
if (releatedLayers.indexOf(this._invisibles[i]) == -1) {
for (
var j = 0; j < this._invisibles[i].options.tags.length; j++
) {
if (
this._selectedTags.length == 0 ||
this._selectedTags.indexOf(
this._invisibles[i].options.tags[j]
) !== -1
) {
//this._map.addLayer(this._invisibles[i]);
toAdd.push(this._invisibles[i]);
toBeRemovedFromInvisibles.push(i);
break;
}
}
}
}
// Batch add into MCG
layerSource.mcg.addLayers(toAdd);
while (toBeRemovedFromInvisibles.length > 0) {
this._invisibles.splice(
toBeRemovedFromInvisibles.pop(),
1
);
}
var removedMarkers = [];
var totalCount = 0;
if (this._selectedTags.length > 0) {
//this._map.eachLayer(
layerSource.mcg.eachLayer(
function(layer) {
if (
layer &&
layer.options &&
layer.options.tags
) {
totalCount++;
if (releatedLayers.indexOf(layer) == -1) {
var found = false;
for (
var i = 0; i < layer.options.tags.length; i++
) {
found =
this._selectedTags.indexOf(
layer.options.tags[i]
) !== -1;
if (found) {
break;
}
}
if (!found) {
removedMarkers.push(layer);
}
}
}
}.bind(this)
);
for (i = 0; i < removedMarkers.length; i++) {
//this._map.removeLayer(removedMarkers[i]);
this._invisibles.push(removedMarkers[i]);
}
// Batch remove from MCG
layerSource.mcg.removeLayers(removedMarkers);
}
return totalCount - removedMarkers.length;
},
},
});
this.layerSources.currentSource = this.layerSources.sources[
'mcg'
];
},
});
////////////////////////////////////////////////
// Fix for TagFilterButton
////////////////////////////////////////////////
L.Control.TagFilterButton.include({
_prepareLayerSources: function() {
this.layerSources = new Object();
this.layerSources['sources'] = new Object();
this.registerCustomSource({
name: 'default',
source: {
hide: function() {
var releatedLayers = [];
for (var r = 0; r < this._releatedFilterButtons.length; r++) {
releatedLayers = releatedLayers.concat(
this._releatedFilterButtons[r].getInvisibles()
);
}
var toBeRemovedFromInvisibles = [],
i;
// "Fix": add var
for (var i = 0; i < this._invisibles.length; i++) {
if (releatedLayers.indexOf(this._invisibles[i]) == -1) {
// "Fix": add var
for (var j = 0; j < this._invisibles[i].options.tags.length; j++) {
if (
this._selectedTags.length == 0 ||
this._selectedTags.indexOf(
this._invisibles[i].options.tags[j]
) !== -1
) {
this._map.addLayer(this._invisibles[i]);
toBeRemovedFromInvisibles.push(i);
break;
}
}
}
}
while (toBeRemovedFromInvisibles.length > 0) {
this._invisibles.splice(toBeRemovedFromInvisibles.pop(), 1);
}
var removedMarkers = [];
var totalCount = 0;
if (this._selectedTags.length > 0) {
this._map.eachLayer(
function(layer) {
if (layer && layer.options && layer.options.tags) {
totalCount++;
if (releatedLayers.indexOf(layer) == -1) {
var found = false;
for (var i = 0; i < layer.options.tags.length; i++) {
found =
this._selectedTags.indexOf(layer.options.tags[i]) !==
-1;
if (found) {
break;
}
}
if (!found) {
removedMarkers.push(layer);
}
}
}
}.bind(this)
);
for (i = 0; i < removedMarkers.length; i++) {
this._map.removeLayer(removedMarkers[i]);
this._invisibles.push(removedMarkers[i]);
}
}
return totalCount - removedMarkers.length;
},
},
});
this.layerSources.currentSource = this.layerSources.sources['default'];
},
});
////////////////////////////////////////////////
// Adapted from TagFilterButton demo
// https://github.com/maydemirx/leaflet-tag-filter-button/blob/0.0.4/docs/assets/js/main.js
////////////////////////////////////////////////
var osmUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
osmAttrib =
'© OpenStreetMap contributors',
osm = L.tileLayer(osmUrl, {
maxZoom: 18,
attribution: osmAttrib,
});
// initialize the map on the "map" div with a given center and zoom
var releatedUsageMap = L.map('releated-usage-map')
.setView([50.5, 30.5], 12)
.addLayer(osm);
var mcg = L.markerClusterGroup().addTo(releatedUsageMap);
L.marker([50.521, 30.52], {
tags: ['tomato', 'active']
})
.bindPopup('tomato, active')
.addTo(mcg);
L.marker([50.487, 30.54], {
tags: ['tomato', 'ended']
})
.bindPopup('tomato, ended')
.addTo(mcg);
L.marker([50.533, 30.5], {
tags: ['tomato', 'ended']
})
.bindPopup('tomato, ended')
.addTo(mcg);
L.marker([50.54, 30.48], {
tags: ['strawberry', 'active']
})
.bindPopup('strawberry, active')
.addTo(mcg);
L.marker([50.505, 30.46], {
tags: ['strawberry', 'ended']
})
.bindPopup('strawberry, ended')
.addTo(mcg);
L.marker([50.5, 30.43], {
tags: ['cherry', 'active']
})
.bindPopup('cherry, active')
.addTo(mcg);
L.marker([50.48, 30.5], {
tags: ['cherry', 'ended']
})
.bindPopup('cherry, ended')
.addTo(mcg);
var statusFilterButton = L.control
.tagFilterButton({
data: ['active', 'ended'],
filterOnEveryClick: true,
icon: '<span>suitcase</span>',
})
.addTo(releatedUsageMap);
// Enable MCG integration
statusFilterButton.enableMCG(mcg);
/*var foodFilterButton = L.control
.tagFilterButton({
data: ['tomato', 'cherry', 'strawberry'],
filterOnEveryClick: true,
icon: '<i class="fa fa-pagelines"></i>',
})
.addTo(releatedUsageMap);
foodFilterButton.addToReleated(statusFilterButton);*/
html,
body,
#releated-usage-map {
height: 100%;
margin: 0;
}
<!-- Leaflet -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.css" media="screen, print" rel="stylesheet" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="">
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.js' integrity="sha512-tAGcCfR4Sc5ZP5ZoVz0quoZDYX5aCtEm/eu1KhSLj2c9eFrylXZknQYmxUssFaVJKvvc0dJQixhGjG2yXWiV9Q==" crossorigin=""></script>
<!-- MarkerCluster Plugin -->
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.3.0/MarkerCluster.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.3.0/MarkerCluster.Default.css" />
<script type='text/javascript' src='https://cdnjs.cloudflare.com/ajax/libs/leaflet.markercluster/1.3.0/leaflet.markercluster.js'></script>
<!-- EasyButton Plugin (compatibility for tagFilterButton) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-easybutton#2/src/easy-button.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton#2/src/easy-button.js"></script>
<!-- tagFilterButton Plugin -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-tag-filter-button#0.0.4/src/leaflet-tag-filter-button.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet-tag-filter-button#0.0.4/src/leaflet-tag-filter-button.js"></script>
<div id="releated-usage-map"></div>

How to join geojson + CSV file with omnivore leaflet in Mapbox to map data as polygons?

I have two files:
1 geojson file with the country shapes I want for a specific group of countries.
1 csv file with the data I want to map using mapbox.
I'm trying to figure out how can I join these two files so that the geojson country info name matches the countries in my CSV file. I'd like to use omnivore to convert my csv to json and then figure out how to pull that data out to bind in a popup.
There is an example of joining two geojson files (one for shapes and another for data) here: https://www.mapbox.com/mapbox.js/example/v1.0.0/choropleth-joined-data-multiple-variables/
But I'd like to use Omnivore to parse my csv file so that I can convert CSV first.
I've managed to separately load my geojson country file and load my CSV file making it ready for my highcharts pop-up, but I can't figure out how to join the two by name.
Here's how I've separately called my geojson layer:
function popup(feature, layer) {
if (feature.properties && feature.properties.name) {
}
}
$.ajax({
dataType: "json",
url: "countries.geojson",
success: function(data) {
$(data.features).each(function(key, data) {
//transitpipes.addData(data);
var countries = new L.geoJson(data, {
onEachFeature: popup,
style: countriesStyle,
}).addTo(map);
});
}
}).error(function() {});
});
And here's what I'm trying to accomplish with my CSV data:
var ckeyOrder = []
var csvGrab2 = $.get('countries.csv',function (response) {
Papa.parse(response, {
complete: function(results) {
var cHeaderRow = results.data[0];
for (var i = 7; i < cHeaderRow.length; i++) {
ckeyOrder.push(cHeaderRow[i])
}
}
});
})
csvGrab2.done(function (csvString) {
var countriesLayer = omnivore.csv.parse(csvString)
countriesLayer.eachLayer(function(marker) {
var pieChartOptions = {
title: {
text: null
},
legend: {
enabled: false
},
credits: {
enabled: false
},
exporting: {
enabled: false
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>',
backgroundColor: {
linearGradient: [0, 0, 0, 60],
stops: [
[0, '#FFFFFF'],
[1, '#E0E0E0']
]
},
borderWidth: 1,
useHTML: true,
borderColor: '#AAA'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
connectNulls: false,
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %',
style: {
color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || 'black'
}
}
}
}
};
pieChartOptions.tooltip.formatter = function() {
var y = "" + this.y;
return '<center>' + y + '</center>';
};
var cData = [];
for (var i = 0; i < ckeyOrder.length; i++) {
cData.push(parseInt(marker.feature.properties[ckeyOrder[i]]))
}
var lastColumn = ckeyOrder.length;
pieChartOptions.series = [{
data: cData
}];
// HTML content for country pop-up
var countryContent = '<div id="popup_template">' +
'<div>' +marker.toGeoJSON().properties.Name +'</div>' +
'<div><p>'+marker.toGeoJSON().properties.Production+'</p></div>'+
'<div id="piechart"></div>';
var ccontainer = $('<div id="popup_template"/>');
ccontainer.html( '<div>' +marker.toGeoJSON().properties.Name +'</div>' +
'<div><p>'+marker.toGeoJSON().properties.Production +'</p></div>' +
'<div id="piechart"></div>');
// Delegate all event handling for the container itself and its contents to the container
ccontainer.find('#piechart').highcharts(lineChartOptions);
marker.bindPopup(ccontainer[0]);
Is there a way to join my geojson countries with my omnivore parsed CSV code so that I can map my CSV data this way?
My incredible bad attempt at this mix of code is here: https://jsfiddle.net/t8qsbzs0/
Here is my CSV structure (one row):
Country,Production,Gas demand,Total imports,of which LNG,Total exports,Total storage capacity,Share of gas in TPES (%),Self sufficiency (%),Electricity and heat,Industry,Residential,Services,Other
France,0.3,44,47.9,7.8,5,12.1,15.1,0.7,16,26,33,18,7
and my countries.geojson file is structured like this:
{"type":"Feature","properties":{"name":"France","iso_a2":"FR","iso_a3":"FRA","iso_n3":"250"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-52.6,2.5],[-52.9,2.1],[-53.4,2.1],[-53.6,2.3],[-53.8,2.4],[-54.1,2.1],[-54.5,2.3],[-54.3,2.7],[-54.2,3.2],[-54,3.6],[-54.4,4.2],[-54.5,4.9],[-54,5.8],[-53.6,5.6],[-52.9,5.4],[-51.8,4.6],[-51.7,4.2],[-52.2,3.2],[-52.6,2.5]]],[[[9.6,42.2],[9.2,41.4],[8.8,41.6],[8.5,42.3],[8.7,42.6],[9.4,43],[9.6,42.2]]],[[[3.6,50.4],[4.3,49.9],[4.8,50],[5.7,49.5],[5.9,49.4],[6.2,49.5],[6.7,49.2],[8.1,49],[7.6,48.3],[7.5,47.6],[7.2,47.4],[6.7,47.5],[6.8,47.3],[6,46.7],[6,46.3],[6.5,46.4],[6.8,46],[6.8,45.7],[7.1,45.3],[6.7,45],[7,44.3],[7.5,44.1],[7.4,43.7],[6.5,43.1],[4.6,43.4],[3.1,43.1],[3,42.5],[1.8,42.3],[0.7,42.8],[0.3,42.6],[-1.5,43],[-1.9,43.4],[-1.4,44],[-1.2,46],[-2.2,47.1],[-3,47.6],[-4.5,48],[-4.6,48.7],[-3.3,48.9],[-1.6,48.6],[-1.9,49.8],[-1,49.3],[1.3,50.1],[1.6,50.9],[2.5,51.1],[2.7,50.8],[3.1,50.8],[3.6,50.4]]]]}},
Okay, so yes, once the content of pie chart is decided..this "joining" is pretty easy.
Entire code block is here in a JSFiddle - https://jsfiddle.net/j4fLj5gm/1/
Won't work because of the CORS issue mentioned in comments, but posting it here for anyway.
The join happens with a loop through actual data, and then a search through the Leaflet feature layer to find the match for the country name.
for (var countryIndex = 0; countryIndex < countryData.length; countryIndex++) {
var marker;
var thisDataRow = countryData[countryIndex];
var thisCountry = thisDataRow[0];
countries.eachLayer(function(country) {
if (country.feature.properties.name === thisCountry) {
marker = country;
}
})
if (typeof marker == 'undefined') {
continue;
}
Data prep for the pie chart is quite easy inside each country's loop.
var cData = [];
var innerIndex = 0;
for (var i = 9; i < 14; i++) {
cData.push({name: ckeyOrder[innerIndex],y: parseInt(thisDataRow[i])})
innerIndex++;
}
pieChartOptions.series = [{
Name: "Production Types",
data: cData
}];
Results in below screenshot..You will probably want to control a dimensions a bit.

JavaScript function not running (console returns that the function "is not a function")

I created a .aspx web page for SharePoint 2010 that uses JQuery to extract list item data from a SharePoint list and then uses that data to populate a Simile Timeline.
The page includes a filter, which I believe uses some form of AJAX, updates/filters what events display on the timeline based on what is entered in a text box (see example here). For some reason, when I implement all of this on SharePoint (by simply placing the .aspx file and other necessary files in a SharePoint library), everything works except the filter. When I check the console log (using Firebug), the error given is that
"onKeyPress is not a function".
onKeyPress is one of the functions involved in implementing the filter, but I have no idea why the browser is not seeing this function- plenty of other functions from the same .js file (including the function that calls onKeyPress to begin with) run just fine. To make matters even more strange, if I put a break point right before the function runs and then allow it to continue after stopping, everything works fine.
This lead me to believe that it may be a timing related issue, but I've been tinkering around with window.onload, $(document).ready, and a SharePoint specific function called _spBodyOnLoadFunctionName for about a day and seen no impact on the code's behavior.
The files that seem to be most relevant to the situation are listdetails.aspx (below)
<%# Page Language="C#" MasterPageFile="~masterurl/default.master" inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" %>
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderAdditionalPageHead">
<script>
Timeline_ajax_url="timeline/timeline_ajax/simile-ajax-api.js";
Timeline_urlPrefix='timeline/timeline_js/';
Timeline_parameters='bundle=true';
</script>
<script src="timeline/timeline_js/timeline-api.js" type="text/javascript"></script>
<script src="examples.js" type="text/javascript"></script>
<script> <script type="text/javascript" src="jquery-1.3.2.min.js"></script>
<script src="tline.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
// Build the URL of the Lists.asmx web service.
// This is done by stripping the last two parts (/doclib/page) of the URL.
var hrefParts = window.location.href.split('/');
var wsURL = "";
for (i = 0; i < (hrefParts.length - 2); i++) {
if (i > 0)
wsURL += "/";
wsURL += hrefParts[i];
}
wsURL += "/_vti_bin/lists.asmx";
// The SOAP Envelope to send to the Lists.asmx web service.
// Tip: this XML can be copied from /_vti_bin/lists.asmx?op=GetListCollection
var soapEnv =
"<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'> \
<soapenv:Body xmlns='http://schemas.microsoft.com/sharepoint/soap/'> \
<GetListItems> \
<listName>SimileData</listName> \
</GetListItems> \
</soapenv:Body> \
</soapenv:Envelope>";
// Do the web service call async.
$.ajax({
url: wsURL,
type: "POST",
dataType: "xml",
data: soapEnv,
complete: processResult,
contentType: "text/xml; charset=\"utf-8\""
});
});
window.onresize=onResize;
</script>
</asp:Content>
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderMain">
<ul id="data">
</ul>
<div id="body">
<h1>Simile Timeline Demo</h1>
<p>Timeline version <span id='tl_ver'></span>.</p>
<script>Timeline.writeVersion('tl_ver')</script>
<div id="tl" class="timeline-default" style="height: 300px;">
</div>
<div class="footnotes">
<br /> <br />
</div>
<div class="controls" id="controls">
</div>
<div class="theButton" id="theButton">
<button type="button">Load Timeline</button>
</div>
</div>
</asp:Content>
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea">
List Details
</asp:Content>
<asp:Content runat="server" ContentPlaceHolderID="PlaceHolderPageTitle">
List Details
</asp:Content>
tline.js (below),
var tl;
var eventSource = new Timeline.DefaultEventSource();
function processResult(xData, status) {
var tStart, tEnd, tLate, tEarly, tTitle, tText, tLink;
var tInst = true;
var evt;
$(xData.responseXML).find("z\\:row").each(function() {
tTitle = $(this).attr("ows_Title0").split("#")[1];
tStart = Timeline.DateTime.parseGregorianDateTime($(this).attr("ows_Delivery_x0020_Date").split(" ")[0]);
tText = $(this).attr("ows_Description_x0020_of_x0020_Chang");
tLink = $(this).attr("ows_ID");
tLink = "some_link_stuff" + tLink + "some_link_stuff";
var evt = new Timeline.DefaultEventSource.Event(tStart, tEnd, tLate, tEarly, tInst, tTitle, tText);
evt._start = tStart;
evt._end = tStart;
evt._earliestEnd = tStart;
evt._latestStart = tStart;
evt._description = tText;
evt._title = tTitle;
evt._text = tTitle;
evt._link = tLink;
eventSource.add(evt);
});
onLoad();
}
function onLoad() {
var theme = Timeline.ClassicTheme.create();
theme.event.bubble.width = 250;
theme.mouseWheel = 'zoom';
var rightNow = new Date();
var theDay = rightNow.getDate();
theDay = theDay.toString();
if(theDay.length < 2){
theDay = "0" + theDay;
}
var theYear = rightNow.getFullYear();
var theMonth = rightNow.getMonth() + 1;
theMonth = theMonth.toString();
if(theMonth.length < 2){
theMonth = "0" + theMonth;
}
var theDate = theYear + "-" + theMonth + "-" + theDay;
var date = Timeline.DateTime.parseGregorianDateTime(theDate);
var bandInfos = [
Timeline.createBandInfo({
width: "80%",
intervalUnit: Timeline.DateTime.MONTH,
intervalPixels: 600,
eventSource: eventSource,
zoomIndex: 10,
zoomSteps: new Array(
{pixelsPerInterval: 280, unit: Timeline.DateTime.HOUR},
{pixelsPerInterval: 140, unit: Timeline.DateTime.HOUR},
{pixelsPerInterval: 70, unit: Timeline.DateTime.HOUR},
{pixelsPerInterval: 35, unit: Timeline.DateTime.HOUR},
{pixelsPerInterval: 400, unit: Timeline.DateTime.DAY},
{pixelsPerInterval: 200, unit: Timeline.DateTime.DAY},
{pixelsPerInterval: 100, unit: Timeline.DateTime.DAY},
{pixelsPerInterval: 50, unit: Timeline.DateTime.DAY},
{pixelsPerInterval: 1000, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 800, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 600, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 400, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 200, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 100, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 50, unit: Timeline.DateTime.MONTH},
{pixelsPerInterval: 400, unit: Timeline.DateTime.YEAR},
{pixelsPerInterval: 200, unit: Timeline.DateTime.YEAR},
{pixelsPerInterval: 100, unit: Timeline.DateTime.YEAR},
{pixelsPerInterval: 50, unit: Timeline.DateTime.YEAR},
{pixelsPerInterval: 400, unit: Timeline.DateTime.DECADE},
{pixelsPerInterval: 200, unit: Timeline.DateTime.DECADE},
{pixelsPerInterval: 100, unit: Timeline.DateTime.DECADE},
{pixelsPerInterval: 50, unit: Timeline.DateTime.DECADE}
),
date: date,
theme: theme
}),
Timeline.createBandInfo({
width: "20%",
intervalUnit: Timeline.DateTime.MONTH,
intervalPixels: 200,
eventSource: eventSource,
date: date,
overview: true,
theme: theme
})
];
bandInfos[1].syncWith = 0;
bandInfos[1].highlight = true;
tl = Timeline.create(document.getElementById("tl"), bandInfos, Timeline.HORIZONTAL);
// tl.loadXML("https://portal.gtri.gatech.edu/gtri/elsys/joshtest/experiment/elsys.xml", function(xml, url) { eventSource.loadXML(xml, url); });
setupFilterHighlightControls(document.getElementById("controls"), tl, [0,1], theme);
}
var resizeTimerID = null;
function onResize() {
if (resizeTimerID == null) {
resizeTimerID = window.setTimeout(function() {
resizeTimerID = null;
tl.layout();
}, 500);
}
}
and examples.js (below)
function centerSimileAjax(date) {
tl.getBand(0).setCenterVisibleDate(SimileAjax.DateTime.parseGregorianDateTime(date));
}
function setupFilterHighlightControls(div, timeline, bandIndices, theme) {
var table = document.createElement("table");
var tr = table.insertRow(0);
var td = tr.insertCell(0);
td.innerHTML = "Filter:";
td = tr.insertCell(1);
td.innerHTML = "Highlight:";
var handler = function(elmt, evt, target) {
onKeyPress(timeline, bandIndices, table);
};
tr = table.insertRow(1);
tr.style.verticalAlign = "top";
td = tr.insertCell(0);
var input = document.createElement("input");
input.type = "text";
SimileAjax.DOM.registerEvent(input, "keypress", handler);
td.appendChild(input);
for (var i = 0; i < theme.event.highlightColors.length; i++) {
td = tr.insertCell(i + 1);
input = document.createElement("input");
input.type = "text";
SimileAjax.DOM.registerEvent(input, "keypress", handler);
td.appendChild(input);
var divColor = document.createElement("div");
divColor.style.height = "0.5em";
divColor.style.background = theme.event.highlightColors[i];
td.appendChild(divColor);
}
td = tr.insertCell(tr.cells.length);
var button = document.createElement("button");
button.innerHTML = "Clear All";
SimileAjax.DOM.registerEvent(button, "click", function() {
clearAll(timeline, bandIndices, table);
});
td.appendChild(button);
div.appendChild(table);
}
var timerID = null;
function onKeyPress(timeline, bandIndices, table) {
if (timerID != null) {
window.clearTimeout(timerID);
}
timerID = window.setTimeout(function() {
performFiltering(timeline, bandIndices, table);
}, 300);
}
function cleanString(s) {
return s.replace(/^\s+/, '').replace(/\s+$/, '');
}
function performFiltering(timeline, bandIndices, table) {
timerID = null;
var tr = table.rows[1];
var text = cleanString(tr.cells[0].firstChild.value);
var filterMatcher = null;
if (text.length > 0) {
var regex = new RegExp(text, "i");
filterMatcher = function(evt) {
return regex.test(evt.getText()) || regex.test(evt.getDescription());
};
}
var regexes = [];
var hasHighlights = false;
for (var x = 1; x < tr.cells.length - 1; x++) {
var input = tr.cells[x].firstChild;
var text2 = cleanString(input.value);
if (text2.length > 0) {
hasHighlights = true;
regexes.push(new RegExp(text2, "i"));
} else {
regexes.push(null);
}
}
var highlightMatcher = hasHighlights ? function(evt) {
var text = evt.getText();
var description = evt.getDescription();
for (var x = 0; x < regexes.length; x++) {
var regex = regexes[x];
if (regex != null && (regex.test(text) || regex.test(description))) {
return x;
}
}
return -1;
} : null;
for (var i = 0; i < bandIndices.length; i++) {
var bandIndex = bandIndices[i];
timeline.getBand(bandIndex).getEventPainter().setFilterMatcher(filterMatcher);
timeline.getBand(bandIndex).getEventPainter().setHighlightMatcher(highlightMatcher);
}
timeline.paint();
}
function clearAll(timeline, bandIndices, table) {
var tr = table.rows[1];
for (var x = 0; x < tr.cells.length - 1; x++) {
tr.cells[x].firstChild.value = "";
}
for (var i = 0; i < bandIndices.length; i++) {
var bandIndex = bandIndices[i];
timeline.getBand(bandIndex).getEventPainter().setFilterMatcher(null);
timeline.getBand(bandIndex).getEventPainter().setHighlightMatcher(null);
}
timeline.paint();
}
The function that isn't loading (onKeyPress) is in examples.js (above). Thank you in advance for all of your help.
I tried changing the name of the function and that somehow fixed it, so thanks dtryan. However, I really don't understand why the name mattered because, as user1090190 observed, the name doesn't matter in any other context: until I put it inside of SharePoint, the code all worked perfectly fine, and on top of that my searches for a function with the same name - a built in browser function or even a SharePoint function - returned nothing, so I'm not sure where this issue came from. SharePoint does have functions called OnKeyPress and onkeypress, so perhaps they also have some other undocumented function called onKeyPress. I hope this helps anyone else that may have this issue, but if anyone has a better explanation for why the name didn't work, please comment.
Thank you for all of your help.

Categories

Resources