Parse a Google spreadsheet into a Javascript array - javascript

I have a Google spreadsheet (https://docs.google.com/spreadsheets/d/e/2PACX-1vRc8Lx0N-wf3f1xAAXkNFUqQjaWPFcde3YjK02gCBqGpUrULwHC6NC0sndeLJBvOyKkA88hvtH335pR/pubhtml) which I'd like to access in a webpage to use with Google Maps API.
As per Google API'S documentation, the script should look like this:
<html>
<head>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script>
google.charts.load('current', { 'packages': ['map'] });
google.charts.setOnLoadCallback(drawMap);
function drawMap() {
var data = google.visualization.arrayToDataTable([
['Country', 'Population'],
['China', 'China: 1,363,800,000'],
['India', 'India: 1,242,620,000'],
['US', 'US: 317,842,000'],
['Indonesia', 'Indonesia: 247,424,598'],
['Brazil', 'Brazil: 201,032,714'],
['Pakistan', 'Pakistan: 186,134,000'],
['Nigeria', 'Nigeria: 173,615,000'],
['Bangladesh', 'Bangladesh: 152,518,015'],
['Russia', 'Russia: 146,019,512'],
['Japan', 'Japan: 127,120,000']
]);
var options = {
showTooltip: true,
showInfoWindow: true
};
var map = new google.visualization.Map(document.getElementById('chart_div'));
map.draw(data, options);
};
</script>
</head>
<body>
<div id="chart_div"></div>
</body>
</html>
So my goal is to dynamically replace:
[
['Country', 'Population'],
['China', '1,363,800,000'],
['India', '1,242,620,000'],
['US', '317,842,000'],
['Indonesia', '247,424,598'],
['Brazil', '201,032,714'],
['Pakistan', '186,134,000'],
['Nigeria', '173,615,000'],
['Bangladesh', '152,518,015'],
['Russia', 'Russia: 146,019,512'],
['Japan', 'Japan: 127,120,000']
]
...with the content of the Google Spreadsheet.
I am new at JS, and I'm struggling to properly convert an exported JSON from Google Spreadsheets into a JS array. Searching around, I stumbled upon an interresting script:
<!doctype html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
var spData = null;
function doData(json) {
spData = json.feed.entry;
}
function drawCell(tr, val) {
var td = $("<td/>");
tr.append(td);
td.append(val);
return td;
}
function drawRow(table, rowData) {
if (rowData == null) return null;
if (rowData.length == 0) return null;
var tr = $("<tr/>");
table.append(tr);
for(var c=0; c<rowData.length; c++) {
drawCell(tr, rowData[c]);
}
return tr;
}
function drawTable(parent) {
var table = $("<table/>");
parent.append(table);
//console.log(table);
return table;
}
function readData(parent) {
var data = spData;
var table = drawTable(parent);
var rowData = [];
for(var r=0; r<data.length; r++) {
var cell = data[r]["gs$cell"];
var val = cell["$t"];
if (cell.col == 1) {
drawRow(table, rowData);
rowData = [];
}
rowData.push(val);
}
drawRow(table, rowData);
}
$(document).ready(function(){
readData($("#data"));
});
</script>
<script src="https://spreadsheets.google.com/feeds/cells/1TTeG6mp2rb61Yxi5KO3GFmZ3qQ3RAMlB9bisLciuj-M/1/public/values?alt=json-in-script&callback=doData"></script>
<style type="text/css">
table {border-collapse: collapse; width: 100%;}
th, td {border: thin solid black; padding: 3px;}
tr.head th, tr.head td {background-color: #EDEDED; border-bottom: 4px double black;}
span.linetitle {font-weight: bold;}
div.lineclass {font-style: italic;}
.title, .result {width: 80%;}
.notes {width: 15%;}
h1 {text-align: center;}
body {margin: 12px; font-size: 12px;}
</style>
<style type="text/css" media="print">
form {display: none;}
</style>
</head>
<body>
<h1>Parse Google Spreadsheet with JavaScript</h1>
<div id="data"/>
</body>
</html>
...which fetches the spreadsheet, and turns it into a HTML table. However, I can't seem to find a way to build an array from this.
After this long context, here's my question: how can I fetch the Google spreadsheet to insert it as data in the drawMap function above?

The Google spreadsheet API you are using is supposed to be called with JSONP.
I used jQuery's simple implementation of JSONP via the $.ajax function.
You can see my solution with line by line explanations:
var spreadsheetUrl = 'https://spreadsheets.google.com/feeds/cells/1TTeG6mp2rb61Yxi5KO3GFmZ3qQ3RAMlB9bisLciuj-M/1/public/values?alt=json-in-script&callback=doData';
// The callback function the JSONP request will execute to load data from API
function doData(data) {
// Final results will be stored here
var results = [];
// Get all entries from spreadsheet
var entries = data.feed.entry;
// Set initial previous row, so we can check if the data in the current cell is from a new row
var previousRow = 0;
// Iterate all entries in the spreadsheet
for (var i = 0; i < entries.length; i++) {
// check what was the latest row we added to our result array, then load it to local variable
var latestRow = results[results.length - 1];
// get current cell
var cell = entries[i];
// get text from current cell
var text = cell.content.$t;
// get the current row
var row = cell.gs$cell.row;
// Determine if the current cell is in the latestRow or is a new row
if (row > previousRow) {
// this is a new row, create new array for this row
var newRow = [];
// add the cell text to this new row array
newRow.push(text);
// store the new row array in the final results array
results.push(newRow);
// Increment the previous row, since we added a new row to the final results array
previousRow++;
} else {
// This cell is in an existing row we already added to the results array, add text to this existing row
latestRow.push(text);
}
}
handleResults(results);
}
// Do what ever you please with the final array
function handleResults(spreadsheetArray) {
console.log(spreadsheetArray);
}
// Create JSONP Request to Google Docs API, then execute the callback function doData
$.ajax({
url: spreadsheetUrl,
jsonp: 'doData',
dataType: 'jsonp'
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

Creating multiple array lists

Re-worded objective:
I'm creating a web page using google apps script. I want to have multiple drop downs listed on the page. I know if I use <select><option>, I can create a list of hard coded options. What I would rather do is grab the options from a google sheet to display in the drop down, this way I can update it at anytime without modifying the HTML code.
The issue: While I was successful in creating a drop down selection containing Column A values from my sheet, I'm running into an issue where Apps Script will not let me create another drop down containing the values of Column B.
This is my sheet that contains names and dietary types. Each column contains the options for each drop down.
This is what it looks like on the front end. I'd like to have another drop down beside it that contains values from Column B (as seen above).
Here is my script:
var url = "google sheets URL";
function doGet(e){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Staff");
var list = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp = HtmlService.createTemplateFromFile("index");
tmp.list = list.map(function(r){ return r[0]; });
return tmp.evaluate();
}
This is the HTML for my selection list:
<select id="app" class="browser-default">
<option disabled selected>Select a teammate!</option>
<? for(var i=0;i<list.length;i++){ ?>
<option><?= list[i]; ?></option>
<? } ?>
</select>
It functions correctly at this point but when trying to replicate it so I can grab another column in Google Sheets and use that as another selection list,
I get: Referenceerror: "list" is not defined.
This is the script that's causing me to get the error.
var url = "google sheets URL";
function doGet(e){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Staff");
var list = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp = HtmlService.createTemplateFromFile("index");
tmp.list = list.map(function(r){ return r[0]; });
return tmp.evaluate();
}
function doGet(f){
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Variables");
var list2 = ws.getRange(1,1,ws.getRange("A1").getDataRegion().getLastRow(),1).getValues();
var tmp2 = HtmlService.createTemplateFromFile("index");
tmp2.list2 = list2.map(function(r){ return r[0]; });
return tmp2.evaluate();
}
Here's a simple example of getting multiple list from a dialog.
Load the code and run launchDialog(). You could also add a doGet() and use return HtmlService.createHtmlOutputFromFile('aq4');
Code.gs:
function getList(n) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
var rg=sh.getRange(2,n,sh.getLastRow()-1,1);
return rg.getValues().map(function(r){return r[0]});
}
function launchDialog() {
var userInterface=HtmlService.createHtmlOutputFromFile('aq4');
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'My List');
}
aq4.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<style>
input{margin:5px;}
td,th{border:1px solid black;}
</style>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(vA){
var select=document.getElementById('sel1');
select.options.length=0;
for(var i=0;i<vA.length;i++) {
select.options[i] = new Option(vA[i],vA[i]);
}
})
.getList(1);
});
function getAnotherList() {
google.script.run
.withSuccessHandler(function(vA){
var select=document.getElementById('sel1');
select.options.length=0;
for(var i=0;i<vA.length;i++) {
select.options[i] = new Option(vA[i],vA[i]);
}
})
.getList(Math.floor(Math.random()*23));
}
console.log("My Code");
</script>
</head>
<body>
<select id="sel1"></select>
<input type="button" value="Get Another List" onClick="getAnotherList();" />
</body>
</html>
Here's my List Spreadsheet:

Creating Table in Google App Script With Date Column

I am currently querying a table from Google sheet which has a Date column. The date column in my dashboard has time info included, which I want to remove; also the starting date in my code is 12/18/2018 but my dashboard starts with one day earlier. 12/17/2018 16.00
My Data source looks like this:
My Dashboard looks like this:
My Code Looks like this.
Code.gs:
function doGet(e) {
return HtmlService
.createTemplateFromFile("Line Chart multiple Table")
.evaluate()
.setTitle("Google Spreadsheet Chart")
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getSpreadsheetData() {
var ssID = "1jxWPxxmLHP-eUcVyKAdf5pSMW6_KtBtxZO7s15eAUag";
var sheet = SpreadsheetApp.openById(ssID).getSheets()[1];
var data1 = sheet.getRange('A2:F9').getValues();
var data2 = sheet.getRange('A2:F9').getValues();
var rows = {data1: data1, data2: data2};
var r = JSON.stringify(rows);
return r;
}
Line Chart multiple Table.html
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<div id="linechartweekly"></div>
<div id="table2"></div>
<div class = "block" id="message" style="color:red;">
<script>
google.charts.load('current', {'packages':['table']});
google.charts.load('current', {packages: ['corechart', 'line']});
google.charts.setOnLoadCallback(getSpreadsheetData);
function display_msg(msg) {
console.log("display_msg():"+msg);
document.getElementById("message").style.display = "block"; // Style of display
var div = document.getElementById('message');
div.innerHTML = msg;
}
function getSpreadsheetData() {
google.script.run.withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(r) {
// Parse back to an object
var rows = JSON.parse(r);
console.log("rows:"+rows);
var data1 = google.visualization.arrayToDataTable(rows.data1, false);
var data2 = google.visualization.arrayToDataTable(rows.data2, false);
var options1 = {
title: 'SPC Chart weekly',
legend: ['USL', 'UCL', 'Data', 'LCL', 'LSL'],
colors: ['Red', 'Orange', 'blue', 'Orange', 'Red'],
pointSize: 4,
};
var chart1 = new google.visualization.LineChart(document.getElementById("linechartweekly"));
chart1.draw(data1, options1);
var table2 = new google.visualization.Table(document.getElementById("table2"));
table2.draw(data2, {showRowNumber: false, width: '50%', height: '100%'});
}
function failure_callback(error) {
display_msg("ERROR: " + error.message);
console.log('failure_callback() entered. WTF'+error.message);
}
</script>
</body>
</html>
May I know how to change my date to the right format removing the time and also ensure the correct starting date
Any help is much appreciated.
The actual problem has me stumped, but I do have a workaround; see modified code example below, with some additional error handling.
I've extensively tested the server-side function, and from its perspective there is absolutely no difference in the row object that is created whether the range starts at column 'I' or 'J'.
The problem manifests itself in the client-side success handler which, when column 'I' is included is essentially passed a null argument, note the whole object, not just the row.data1 part, is null.
The row object that is being passed from the server to the client is quite complicated (an object with 3 key value pairs, where the values are fairly long arrays). I can't see anything in the GAS documentation that disallows this: Legal parameters and return values are JavaScript primitives like a Number, Boolean, String, or null, as well as JavaScript objects and arrays that are composed of primitives, objects and arrays. So this could be a bug?
The workaround, illustrated in the code examples below is to stringify the object in the server-side function, and then parsing it back to an object in the client.
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/charts/loader.js"></script>
</head>
<body>
<div id="table1"></div>
<div id="linechartweekly"></div>
<div id="table2"></div>
<div class = "block" id="message" style="color:red;">
<script>
google.charts.load('current', {'packages':['table']});
google.charts.load('current', {packages: ['corechart', 'line']});
google.charts.setOnLoadCallback(getSpreadsheetData);
function display_msg(msg) {
console.log("display_msg():"+msg);
document.getElementById("message").style.display = "block"; // Style of display
var div = document.getElementById('message');
div.innerHTML = msg;
}
function getSpreadsheetData() {
google.script.run.withFailureHandler(failure_callback).withSuccessHandler(drawChart).getSpreadsheetData();
}
function drawChart(r) {
// Parse back to an object
var rows = JSON.parse(r);
console.log("rows:"+rows);
var data1 = google.visualization.arrayToDataTable(rows.data1, false);
var data2 = google.visualization.arrayToDataTable(rows.data2, false);
var data3 = google.visualization.arrayToDataTable(rows.data3, false);
var options1 = {
title: 'SPC Chart weekly',
legend: ['USL', 'UCL', 'Data', 'LCL', 'LSL'],
colors: ['Red', 'Orange', 'blue', 'Orange', 'Red'],
pointSize: 4,
};
var table1 = new google.visualization.Table(document.getElementById("table1"));
table1.draw(data1, {showRowNumber: false, width: '50%', height: '100%'});
var chart1 = new google.visualization.LineChart(document.getElementById("linechartweekly"));
chart1.draw(data2, options1);
var table2 = new google.visualization.Table(document.getElementById("table2"));
table2.draw(data3, {showRowNumber: false, width: '50%', height: '100%'});
}
function failure_callback(error) {
display_msg("ERROR: " + error.message);
console.log('failure_callback() entered. WTF'+error.message);
}
</script>
</body>
</html>
Code
function doGet(e) {
return HtmlService
.createTemplateFromFile("Line Chart multiple Table")
.evaluate()
.setTitle("Google Spreadsheet Chart")
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
}
function getSpreadsheetData() {
var ssID = "1jxWPxxmLHP-eUcVyKAdf5pSMW6_KtBtxZO7s15eAUag";
var sheet = SpreadsheetApp.openById(ssID).getSheets()[0];
//var firstrow = 6; //11th row
//var range = sheet.getRange(firstrow, 1, sheet.getLastRow() - firstrow + 1, 6);
//var data1 = range.getValues();
var d1 = sheet.getRange('A1:B5').getValues();
var d2 = sheet.getRange('I2:O4').getValues();
var d3 = sheet.getRange('I2:O4').getValues();
var rows = {data1: d1, data2: d2, data3: d3};
// Stringify the object
var r = JSON.stringify(rows);
return r;
}

Trouble with Google charts, queries and javascript

I'm trying to create a very simple website consisting of a Google bubble chart overlaid on a background image. The bubble chart is supposed to be generated based on data from a set of Google spreadsheets, and the user can select, through a simple text form, the names of the data points that are to be published. In the code below I have obfuscated the spreadsheet URL, because I don't want to make the data public yet (note however that the viewing privileges of the document are set to public, as required).
The code doesn't work, as no chart is drawn. Obviously there is something fishy going on, because there is never an alert from the collectData method, which handles the query response.
Also, initially I didn't have the draw method and its code was instead in the sendAndDraw method, sans the setTimeout. In that version, I got a Data table not found error on the webpage, but again, the collectData method didn't seem to be called, as it didn't raise an error.
Any suggestions as to what might be the issue? I should add that I am completely new to both javascript and Google developers tools.
EDIT: Following kfa's comment, the form was changed to include a post method. Now I get the Data Table not defined problem once again.
<!DOCTYPE html>
<html>
<head>
<style>
#container{
position: relative;
width: 200px;
height: 200px;
}
#background{
width: 100%;
height: 100%;
}
#bubbleChart{
width: 100%;
height: 100%;
}
</style>
<title>gRNA</title>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
google.charts.load('current', {'packages' : ['corechart']});
google.charts.setOnLoadCallback(function() {initialize()});
var dataSourceUrls = [
'https://docs.google.com/spreadsheets/d/foo/edit#gid=0&headers=1'
];
var nSheets = dataSourceUrls.length;
var query;
var bubbleDataArray = [];
var bubbleData;
function initialize() {
//Currently not doing anything here
}
//Takes a list of names as a comma separated list.
function sendQueries(nameString) {
var queryString = generateQuery(nameString);
for(i = 0; i < nSheets; i++) {
query = new google.visualization.Query(dataSourceUrls[i]);
query.setQuery(queryString);
query.send(collectData);
query.abort();
}
}
//Generates the query string for the selected names.
function generateQuery(nameString) {
nameString = nameString.split(",");
var nNames = nameString.length;
var queryString = [];
queryString.push("select F, D, E, B ");
queryString.push("where F matches ");
queryString.push(nameString[0]);
for(i = 1; i < nNames; i++) {
queryString.push("or ");
queryString.push(nameString[i]);
}
return queryString.join("");
}
//Collect and manage the query responses.
function collectData(response) {
alert('Hi!');
if(response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' +
response.getDetailedMessage());
return;
}
var data = response.getDataTable();
if(data.getNumberOfRows() > 0) {
bubbleDataArray.push(data);
}
}
function sendAndDraw(nameString) {
bubbleDataArray = [];
sendQueries(nameString);
setTimeout(draw,2000);
}
function draw() {
bubbleData = bubbleDataArray[0];
var nTables = bubbleDataArray.length;
for(i = 1; i < nTables; i++) {
bubbleData = google.visualization.data.join(bubbleData,
bubbleDataArray[i], 'full', [[0,0]],
[1,2,3], [1,2,3]);
}
var container = document.getElementById('bubbleChart');
var bubbleChart = new google.visualization.BubbleChart(container);
var options = {
'backgroundColor':'transparent'
}
bubbleChart.draw(bubbleData,options);
}
function plot() {
sendAndDraw(document.getElementById('nameSel').value);
}
</script>
</head>
<body>
<form onsubmit="plot(); return false;" method="post">
<input type="text" id="nameSel"/>
<input type="submit" value="Plot"/>
</form>
<br />
<div id="container">
<div id="background"></div>
<div id="bubbleChart"></div>
</div>
</body>
</html>

Tableau JavaScript API - get Selected value

I am currently researching the possibility to grabbing data from the Tableau report(s) via the JavaScript API but the closet I can get to grabbing values from a graph after filtering is selecting the value via the selectSingleValue() method.
For example: JavaScript API Tutorial
In the API tutorial tab called 'Select'. One of the examples selects the row "Marcao Sao, China". Is it possible to extract that numerical value of $52.0k ?
I have tried looking into the Objects returned (via FireBug) but I cannot seem to locate the right object. My recent location was in getActiveSheets().
Any help would be appreciated.
In the JavaScript API tutorial tab 'Events' it shows you how to add an event listener to return the selected marks. You can then loop through the marks to get the values you want.
Copy the below code block into a file, save as html and open in your favourite web browser (tested on ie11).
<html>
<head>
<meta charset="utf-8">
<title>Tableau 8 Javascrip API</title>
<script type="text/javascript" src="http://public.tableausoftware.com/javascripts/api/tableau_v8.js"></script>
<script type="text/javascript">
/////////////////////
// Global variables
var viz, workbook, activeSheet
// function called by viz on marks being selected in the workbook
function onMarksSelection(marksEvent) {
return marksEvent.getMarksAsync().then(reportSelectedMarks);
}
function reportSelectedMarks(marks) {
for (var markIndex = 0; markIndex < marks.length; markIndex++) {
var pairs = marks[markIndex].getPairs();
for (var pairIndex = 0; pairIndex < pairs.length; pairIndex++) {
var pair = pairs[pairIndex];
if (pair.fieldName == "AVG(F: GDP per capita (curr $))") {
alert("You selected a country with an avg GPD per capita of " + pair.formattedValue);
}
}
}
}
// Initialise the viz to hold the workbook
function initializeViz(){
var placeholderDiv = document.getElementById("tableauViz");
var url = "http://public.tableausoftware.com/views/WorldIndicators/GDPpercapita?Region=";
var options = {
width: "800px", //width: placeholderDiv.offsetWidth,
height: "400px", //height: placeholderDiv.offsetHeight,
hideTabs: true,
hideToolbar: true,
onFirstInteractive: function () {
workbook = viz.getWorkbook();
activeSheet = workbook.getActiveSheet();
}
};
viz = new tableauSoftware.Viz(placeholderDiv, url, options);
// Add event listener
viz.addEventListener(tableauSoftware.TableauEventName.MARKS_SELECTION, onMarksSelection);
}
</script>
</head>
<body>
<!-- Tableau view goes here -->
<div id="tableauViz" style="height:1200px; width:1200px"\></div>
<script type='text/javascript'>
//Initialize the viz after the div is created
initializeViz();
</script>
</body>
</html>

Changing the number of results using Google Feed API

I'm trying increase the number of items that appear on this webpage using the Google Feed API to 25 instead of 4 items:
http://ewh.ieee.org/reg/1/sac/news.php
Here is the JS:
<script src="//www.google.com/jsapi?key=AIzaSyA5m1Nc8ws2BbmPRwKu5gFradvD_hgq6G0" type="text/javascript"></script>
<script type="text/javascript">
google.load("feeds", "1");
function feedLoaded(result) {
if (!result.error) {
var container = document.getElementById("content");
container.innerHTML = '';
for (var i = 0; i < result.feed.entries.length; i++) {
var entry = result.feed.entries[i];
var div = document.createElement("div");
div.appendChild(document.createTextNode(i + ': ' + entry.title));
container.appendChild(div);
}
}
}
function OnLoad() {
var feedControl = new google.feeds.FeedControl();
feedControl.addFeed("http://feeds.feedburner.com/IeeeSpectrum", "Latest IEEE Spectrum News:");
feedControl.draw(document.getElementById("content"));
feed.includeHistoricalEntries(); // tell the API we want to have old entries too
feed.setNumEntries(25); // we want a maximum of 25 entries, if they exist
feed.load(feedLoaded);
}
google.setOnLoadCallback(OnLoad);
</script>
And the HTML:
<body style="font-family: Arial;border: 0 none;">
<div id="content">Loading IEEE Spectrum News...</div>
</body>
I tried using feed.setNumEntries(), but it doesn't seem to work. I appreciate any help. Thank you.
I found the solution to my problem:
<script type="text/javascript">
google.load("feeds", "1");
function initialize() {
var control = new google.feeds.FeedControl();
control.setNumEntries(25);
control.addFeed("http://feeds.feedburner.com/IeeeSpectrum", "Latest IEEE Spectrum News:");
control.draw(document.getElementById("content"));
}
google.setOnLoadCallback(initialize);
</script>

Categories