I'm using the following code to do an "intelligent" autocomplete via Geosearch using Google Places API:
var input = 'field_18';
google.maps.event.addDomListener(document.getElementById(input), 'keydown', function(event) {
if (event.keyCode === 13) {
event.preventDefault();
}
});
var ppx_autocomplete = new google.maps.places.Autocomplete((document.getElementById(input)), { types: ['geocode'] });
ppx_autocomplete.setFields(['geometry', 'formatted_address']);
google.maps.event.addListener(ppx_autocomplete, 'place_changed', function () {
var place = ppx_autocomplete.getPlace();
document.getElementById(input).value = place.formatted_address;
var lat = place.geometry.location.lat();
var lng = place.geometry.location.lng();
document.getElementById('pp_18_geocode').value = latlng;
});
Pretty common and straight forward.
The downside is: The autocompletion starts right away with the first 2 or 3 letters typed, resulting in a LOT of requests to Google, hence to my API key consumption.
Is there any way to restrict the number of requests, e.g. by sending requests only after 5 typed letters AND maybe after some delay time, e.g. not sending requests when the user still types...
What you are looking for can be done, but you'll need to use the Autocomplete Service [1] instead of the Autocomplete widget [2]. Here is an example that waits until the fifth character is entered to make a request, and then makes one after each 2 additional characters. The number of characters can be edited at the line "edit params here". You need to insert your own API key. A similar example is at https://codepen.io/ecglover8/pen/ExPqdNd that does every 3 characters.
Since you won't be using the Autocomplete widget, you'll need to handle session tokens [3] yourself (not shown). From a price standpoint, whether you'll need tokens or not depends on exactly how you plan to use the predictions [4]. This example actually makes a geocoding API request using the Place ID from the prediction and displays the lat/long.
[1] https://developers.google.com/maps/documentation/javascript/places-autocomplete#place_autocomplete_service
[2] https://developers.google.com/maps/documentation/javascript/places-autocomplete#add-autocomplete
[3] https://developers.google.com/maps/documentation/javascript/places-autocomplete#session_tokens
[4] https://developers.google.com/maps/documentation/places/web-service/usage-and-billing#ac-per-request
//declare constants
const ac = document.getElementById("ac");
const g = document.getElementById("geocoded");
const results = document.getElementById("results");
//listen for typing into input box
ac.addEventListener("input", ACRequest);
//show resulting predictions
const displayPredictions = function(predictions, status) {
results.innerHTML = "";
g.innerHTML = "";
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status);
return;
}
predictions.forEach(prediction => {
let li = document.createElement("li");
li.appendChild(document.createTextNode(prediction.description));
li.dataset.placeid = prediction.place_id;
li.addEventListener("click", Geo)
results.appendChild(li);
});
let img = document.createElement("img");
img.setAttribute("src", "https://developers.google.com/maps/documentation/images/powered_by_google_on_white.png");
results.appendChild(img);
};
//make autocomplete request if input value length divisible by 3
function ACRequest() {
//edit params here
if ((ac.value.length > 4) && (ac.value.length % 2 == 1)) {
const service = new google.maps.places.AutocompleteService();
service.getPlacePredictions(
{
//here is where you can add bounds, componentrestrictions, types, etc
input: ac.value
}, displayPredictions
)
};
};
function Geo() {
console.log(this.getAttribute("data-placeid"));
const geocoder = new google.maps.Geocoder();
geocoder.geocode(
{
"placeId": this.getAttribute("data-placeid")
}, function(address, status) {
if (status == "OK") {
g.innerHTML = address[0].geometry.location
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
li {
border: 2px solid blue;
list-style: none;
margin: 2px;
padding: 2px;
}
li:hover {
border: 2px solid red;
}
.pac-card {
border-radius: 2px 0 0 2px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
box-sizing: border-box;
font-family: Roboto;
margin: 10px 10px 0 0;
outline: none;
}
.title {
background-color: #4D90FE;
color: #FFFFFF;
font-size: 25px;
font-weight: 500;
margin: 10px 0px;
padding: 6px 12px;
}
<div class="pac-card">
<div class="title">Autocomplete Makes Requests Every Three Characters</div>
<input id="ac" size="10" type="text" />
<ul id="results"></ul>
<p id="geocoded"></p>
</div>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&libraries=places&v=weekly" async defer></script>
Related
I have created a user directory web app concept that pulls user images, names, and email addresses within G-Suite. I'm generating a Google Sheet with the data and displaying that data through an HTML table. All the data pulls over into the table cells as intended, including the thumbnails on my end. However, when another admin uses the application, the thumbnails show up as the default "head and shoulders" image. I have all my permissions set so anyone in the domain can use it, so I'm pretty sure it's not an issue with permissions on the application or the sheet. If anyone has any incite as to why the user.thumbnailPhotoUrl is not giving the user's images it would be very much appreciated. It might also be worth noting that I added all the profile pictures myself through the admin panel. My supervisor updated his profile picture and was only able to see his own.
Here is the code I am using:
code.gs
function doGet(){
return HtmlService.createHtmlOutputFromFile("table3");
}
function getDomainUsersList() {
var users = [];
var options = {
domain: "my_domain", // Google Apps domain name
customer: "my_customer",
maxResults: 100,
projection: "basic", // Fetch basic details of users
viewType: "domain_public",
orderBy: "email" // Sort results by users
}
do {
var response = AdminDirectory.Users.list(options);
response.users.forEach(function(user) {
users.push([user.name.fullName, user.primaryEmail, user.thumbnailPhotoUrl]);
});
// For domains with many users, the results are paged
if (response.nextPageToken) {
options.pageToken = response.nextPageToken;
}
} while (response.nextPageToken);
// Insert data in a spreadsheet
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1nzRcC8ChbY2C0wjTY_hC0txkYMphMofvxyHws86syfM/edit#gid=0");
var sheet = ss.getSheetByName("Users") || ss.insertSheet("Users", 1);
sheet.getRange(1,1,users.length, users[0].length).setValues(users);
var data = sheet.getRange(1,1,sheet.getLastRow()-1,3).getValues();
return data;
}
/**function getData() {
var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1nzRcC8ChbY2C0wjTY_hC0txkYMphMofvxyHws86syfM/edit#gid=0");
var sheet = ss.getSheetByName("Users") || ss.insertSheet("Users", 1);
var data = sheet.getRange(1,1,sheet.getLastRow()-1,3).getValues();
return data;
table3.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<style>
* {
box-sizing: border-box;
}
#myInput {
background-image: url('/css/searchicon.png');
background-position: 10px 10px;
background-repeat: no-repeat;
width: 100%;
font-size: 16px;
padding: 12px 20px 12px 40px;
border: 1px solid #ddd;
margin-bottom: 12px;
}
#myTable {
border-color: blue;
border-collapse: collapse;
width: 100%;
border: 1px solid #ddd;
font-size: 18px;
}
#myTable th, #myTable td {
text-align: left;
padding: 12px;
}
#myTable tr {
border-bottom: 1px solid #ddd;
}
#myTable tr.header, #myTable tr:hover {
background-color: #f1f1f1;
}
</style>
</head>
<body>
<h2>Employee Directory</h2>
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search for names.." title="Type in a name">
<div style="overflow-x:auto;">
<table id="myTable">
<thead>
<tr>
<th id="header">Picture</th>
<th id="header" onclick="sortTable(1)">Name</th>
<th id="header" onclick="sortTable(2)">Email</th>
</tr>
</thead>
<tbody id="table-body">
</tbody>
</table>
</div>
<script>
function myFunction() {
var input, filter, table, tr, td, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[1];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
</script>
<script>
document.addEventListener("DOMContentLoaded",function(){
google.script.run.withSuccessHandler(generateTable).getDomainUsersList();
});
function generateTable(dataArray) {
dataArray.forEach(function(r){
var tbody = document.getElementById("table-body");
var row = document.createElement("tr");
var col1 = document.createElement("td");
col1.textContent = r[0];
var col2 = document.createElement("td");
col2.textContent = r[1];
var col3 = document.createElement("td");
var image = document.createElement("img");
image.src = r[2];
col3.appendChild(image);
row.appendChild(col3);
row.appendChild(col1);
row.appendChild(col2);
tbody.appendChild(row);
});
}
</script>
<!--Sortable Headers -->
<script>
function sortTable(n) {
var table, rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
table = document.getElementById("myTable");
switching = true;
//Set the sorting direction to ascending:
dir = "asc";
/*Make a loop that will continue until
no switching has been done:*/
while (switching) {
//start by saying: no switching is done:
switching = false;
rows = table.rows;
/*Loop through all table rows (except the
first, which contains table headers):*/
for (i = 1; i < (rows.length - 1); i++) {
//start by saying there should be no switching:
shouldSwitch = false;
/*Get the two elements you want to compare,
one from current row and one from the next:*/
x = rows[i].getElementsByTagName("TD")[n];
y = rows[i + 1].getElementsByTagName("TD")[n];
/*check if the two rows should switch place,
based on the direction, asc or desc:*/
if (dir == "asc") {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
//if so, mark as a switch and break the loop:
shouldSwitch= true;
break;
}
} else if (dir == "desc") {
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
//if so, mark as a switch and break the loop:
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
/*If a switch has been marked, make the switch
and mark that a switch has been done:*/
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
//Each time a switch is done, increase this count by 1:
switchcount ++;
} else {
/*If no switching has been done AND the direction is "asc",
set the direction to "desc" and run the while loop again.*/
if (switchcount == 0 && dir == "asc") {
dir = "desc";
switching = true;
}
}
}
}
</script>
</body>
</html>
Thanks in advance,
Isaac
#IMTheNachoMan reported the issue in Google's issue tracker. You can click on the star next to the issue number to receive updates and to give more priority to his report.
Please can someone guide me on how to implement a static (sticky) header to this dynamically created table?
I have tried multiple things from Stackoverflow threads for a while now but lack HTML/CSS knowledge and I'm obviously missing something simple.
I have managed to get it working using a table created directly in the main body of the code, but when I use my dynamically created tables from JSON I can't get anything to 'stick'.
Below the code:
<!DOCTYPE html>
<html>
<meta name="viewport" content="width=device-width, initial-scale=0.50, maximum-scale=1, user-scalable=0"/>
<head>
<title>iNews HTML Running Order</title>
<style>
table
{
border: solid 1px #CCCCCC;
border-collapse: collapse;
text-align: left;
font:30px Arial;
}
tr, th, td
{
white-space: nowrap;
padding-right: 50px;
}
tr
{
background-color: #ffffff;
border: solid 1px #CCCCCC;
}
th
{
background-color: #CCCCCC;
}
#container
{
text-align: center;
max-width: 100%;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
<body onload="initialisePage('LW')">
<p id="showData">Loading Running Order...</p>
</body>
<script>
var loop;
var filename;
var table;
function updateJSONData(filename)
{
getDataFromJSON(filename)
loop = setInterval(function(){getDataFromJSON(filename);}, 500);
}
function initialisePage(newFilename)
{
filename = newFilename;
updateJSONData(filename)
}
function setFileName(newFilename)
{
clearInterval(loop)
filename = newFilename;
updateJSONData(filename)
}
function getDataFromJSON(filename)
{
$.get( "http://10.142.32.72/dashboard/"+filename+".json", function( data ) {
var myBooks = JSON.parse(data);
CreateTableFromJSON(myBooks)
});
}
function CreateTableFromJSON(myBooks)
{
var title = ["Page", "Slug", "Pres 1", "Pres 2", "CAM", "Format", "Clip Dur", "Total", "Backtime"];
var col = ["page-number", "title", "pres1", "pres2", "camera", "format", "runs-time", "total-time", "back-time"];
// CREATE DYNAMIC TABLE.
table = document.createElement("table");
// CREATE HTML TABLE HEADER ROW USING THE EXTRACTED HEADERS ABOVE.
var tr = table.insertRow(-1); // TABLE ROW.
for (var i = 0; i < col.length; i++) {
var th = document.createElement("th"); // TABLE HEADER.
th.innerHTML = title[i];
tr.appendChild(th);
}
// ADD JSON DATA TO THE TABLE AS ROWS.
for (var i = 0; i < myBooks.length; i++) {
tr = table.insertRow(-1);
if (myBooks[i]["floated"] == "true"){
tr.style.color = "#ffffff";
tr.style.background = "blue";
}
if ((myBooks[i]["break"] == "true") && (myBooks[i]["floated"] == "false")){
tr.style.background = "#00ff00";
}
for (var j = 0; j < col.length; j++) {
var tabCell = tr.insertCell(-1);
tabCell.innerHTML = myBooks[i][col[j]];
}
}
// FINALLY ADD THE NEWLY CREATED TABLE WITH JSON DATA TO A CONTAINER.
var divContainer = document.getElementById("showData");
divContainer.innerHTML = "";
divContainer.appendChild(table);
console.log("Refreshed: " + filename);
}
</script>
</html>
Many thanks in advance,
Joe
Remove <body onload="initialisePage('LW')"> and use DOMContentLoaded instead as it happens much sooner than the document load event.
load is only fired after ALL resources/content has been loaded, including "non-essential" (non-DOM) content like images and external content like ad-banners, which means the load event may be fired tens-of-seconds after DOMContentLoaded which makes the load event kinda useless today).
Change your CSS to this:
table > thead > tr > th {
position: sticky;
top: 0;
z-index: 10;
}
table > tbody > tr.floated {
color: '#ffffff';
background-color: 'blue';
}
table > tbody > tr.broken {
background-color: '#00ff00';
}
JavaScript uses camelCase for functions, values (variables and parameters) and properties, not PascalCase.
Avoid var and use const and let in scripts where appropriate instead. Note that const means "unchanging reference" (kinda like C++); it does not mean "immutable" or "compile-time constant value". I think this definition of const was a mistake by the JavaScript language designers, but that's just, like, my opinion, man.
Use CSS classes via classList instead of setting individual style properties using .style.
The current JavaScript ecosystem also generally uses 1TBS instead of the Allman style.
Prefer === (exactly-equals) instead of == (equals) because JavaScript's type coercion can be surprising).
Avoid using innerHTML wherever possible. Use .textContent for setting normal text content (and avoid using .innerText too). Misuse of innerHTML leads to XSS vulnerabilities.
It's 2020. STOP USING JQUERY!!!!!!!!!!
Cite
Cite
Cite
Cite
DONT USE ALL-CAPS IN YOUR JAVASCRIPT COMMENTS BECAUSE IT LOOKS LIKE THE AUTHOR IS SHOUTING AT YOU NEEDLESSLY AND IT GETS QUITE ANNOYING FOR OTHER READERS ARRRRGGGHHHHH
You need to handle HTTP request responses correctly (e.g. to check for succesful responses with the correct Content-Type).
Avoid using j as an iterable variable name because it's too visually similar to i.
Change your JavaScript to this:
<script>
// You should put all of your own application-specific top-level page script variables in their own object so you can easily access them separately from the global `window` object.
const myPageState = {
loop : null,
fileName: null,
table : null
};
window.myPageState = myPageState; // In the top-level function, `const` and `let`, unlike `var`, do not create a global property - so you need to explicitly set a property like so: `window.{propertyName} = ...`.
window.addEventListener( 'DOMContentLoaded', onDOMLoaded );
function onDOMLoaded( ev ) {
window.myPageState.fileName = "LW";
window.myPageState.loop = setInterval( refreshTable, 500 );
}
async function refreshTable() {
if( typeof window.myPageState.fileName !== 'string' || window.myPageState.fileName.length === 0 ) return;
const url = "http://10.142.32.72/dashboard/" + window.myPageState.fileName + ".json";
const resp = await fetch( url );
if( resp.status === 200 && resp.headers['ContentType'] === 'application/json' ) {
const deserialized = await resp.json();
ceateAndPopulateTableFromJSONResponse( deserialized );
}
else {
// Error: unexpected response.
// TODO: error handling
// e.g. `console.error` or `throw new Error( "Unexpected response." )`, etc.
}
}
function ceateAndPopulateTableFromJSONResponse( myBooks ) {
// TODO: Verify the `myBooks` object layout (i.e. schema-verify `myBooks`).
const columnTitles = ["Page", "Slug", "Pres 1", "Pres 2", "CAM", "Format", "Clip Dur", "Total", "Backtime"];
const columnNames = ["page-number", "title", "pres1", "pres2", "camera", "format", "runs-time", "total-time", "back-time"];
const table = window.myPageState.table || document.createElement( 'table' );
if( window.myPageState.table !== table ) {
window.myPageState = table;
document.getElementById("showData").appendChild( table );
}
// Create the <thead>, if nnecessary:
if( table.tHead === null )
{
table.tHead = document.createElement( 'thead' );
const tHeadTR = table.tHead.insertRow(-1);
for( let i = 0; i < columnNames.length; i++ ) {
const th = document.createElement('th');
th.textContent = columnTitles[i];
tHeadTR.appendChild( th );
}
}
// Clear any existing tbody:
while( table.tBodies.length > 0 ) {
table.removeChild( table.tBodies[0] );
}
// Populate a new <tbody>:
{
const tbody = document.createElement('tbody');
for( let i = 0; i < myBooks.length; i++ ) {
const tr = table.insertRow(-1);
tr.classList.toggle( 'floated', myBooks[i]["floated"] === "true" );
tr.classList.toggle( 'broken' , myBooks[i]["break" ] === "true" && myBooks[i]["floated"] === "false" );
for( let c = 0; c < columnNames.length; c++ ) {
const td = tr.insertCell(-1);
const colName = columnNames[c];
td.textContent = myBooks[i][ colName ];
}
}
table.appendChild( tbody );
}
console.log( "Refreshed: " + window.myPageState.fileName );
}
</script>
What I have at the moment is a place autocomplete which displays results for one country.
What I want to do is make it display results for more countries (limited to 2 or 3).
As I understand it, this is not possible with the current version of the autocomplete ( https://code.google.com/p/gmaps-api-issues/issues/detail?id=4233)
So what I'm going to do is get two lists of predictions and display those instead of the autocomplete result.
Is there any way to trigger the dropdown part of the autocomplete and populate it with the predictionslist?
triggered code in the onChange of the input:
var inputData = this.value;
var options = {
componentRestrictions: { country: "be" }
};
service = new google.maps.places.AutocompleteService();
var request = {
input: inputData,
componentRestrictions: {country: 'be'},
};
var secondaryRequest = {
input: inputData,
componentRestrictions: {country: 'lu'},
};
service.getPlacePredictions(request, callback);
service.getPlacePredictions(secondaryRequest, callback);
callback function:
function callback(predictions, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
alert(status);
return;
}
//here I need to display that dropdown if it isn't already
// and then add the results of the current predictions.
}
UPDATE
Multiple countries filter in place autocomplete was introduced in version 3.27 of Maps JavaScript API in January 2017:
You can now restrict Autocomplete predictions to only surface from multiple countries. You can do this by specifying up to 5 countries in the componentRestrictions field of the AutocompleteOptions.
source: https://developers.google.com/maps/documentation/javascript/releases#327
Here's my demo solution. As mentioned in the comment. it uses several calls to get predictions and build up the result list with them. When a result is selected the address is geocoded.
This means 3 calls instead of 1 with the autocomplete, but so far I haven't found a way around it.
<!DOCTYPE html>
<html>
<head>
<title>Retrieving Autocomplete Predictions</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?libraries=places&v=3.exp"></script>
<script>
function initialize() {
$("#place").live("keyup", function (evt) {
// Clear any previously set timer before setting a new one
window.clearTimeout($(this).data("timeout"));
$(this).data("timeout", setTimeout(function () {
//whe the timeout has expired get the predictions
var inputData = $("#place").val();
service = new google.maps.places.AutocompleteService();
var request = {
input: inputData,
componentRestrictions: {country: 'be'},
};
var secondaryRequest = {
input: inputData,
componentRestrictions: {country: 'lu'},
};
$('#resultWindow').empty();
service.getPlacePredictions(request, callback);
service.getPlacePredictions(secondaryRequest, callback);
}, 1000));
});
function callback(predictions, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
console.log(status);
return;
}
var resultHTML = '';
for (var i = 0, prediction; prediction = predictions[i]; i++) {
resultHTML += '<div>' + prediction.description + '</div>';
}
if($('#resultWindow').html() != undefined && $('#resultWindow').html() != ''){
resultHTML = $('#resultWindow').html()+ resultHTML;
}
if(resultHTML != undefined && resultHTML != ''){
$('#resultWindow').html(resultHTML).show();
}
//add the "powered by google" image at the bottom -> required!!
if($('#resultWindow').html() != undefined){
$('#resultWindow #googleImage').remove();
var imageHtml = $('#resultWindow').html() + '<img id="googleImage" src="powered-by-google-on-white2.png"/>';
$('#resultWindow').html(imageHtml);
}
}
function geocodeAddress(address) {
var geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': address}, function (results, status)
{
if (status == google.maps.GeocoderStatus.OK)
{
$('#latitude').val(results[0].geometry.location.lat());
$('#longitude').val(results[0].geometry.location.lng());
}
else {
console.log("Error: " + google.maps.GeocoderStatus);
}
});
}
$('#resultWindow div').live('click',function(){
//get the coördinates for the selected (clicked) address
$('#resultWindow').hide();
var address = $(this).text();
var addressParts = address.split(',');
$('#country').val(addressParts[2]);
$('#city').val(addressParts[1]);
$('#place').val(addressParts[0]);
if(address != ''){
geocodeAddress(address);
}
});
/*end custom autocomplete stuff*/
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<style type="text/css">
#resultWindow{
position: fixed;
/* top: 0;
left: 0;
width: 100%;
height: 100%;*/
background-color: #fff;
filter:alpha(opacity=50);
-moz-opacity:0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
z-index: 10000;
border: 1px solid black;
color:black;
display:none;
}
</style>
</head>
<body>
<div id="placeholder">
<input type="text" id="place" style="width:200px;"/>
<label for="latitude">Latitude</label>
<input type="text" id="latitude"/>
<label for="longitude">Longitude</label>
<input type="text" id="longitude"/>
<label for="city">city</label>
<input type="text" id="city"/>
<label for="country">selected country</label>
<input type="text" id="country"/>
<div id="resultWindow"></div>
</div>
</body>
</html>
I'm adapting this progress bar:http://www.richardshepherd.com/tv/audio/ to work with my playlist code, but I can't work out why it's not working. I expect it's something ridiculous (I tried adding the (document).ready function, but that broke the rest of my code).
This is what I have:
function loadPlayer() {
var audioPlayer = new Audio();
audioPlayer.controls="controls";
audioPlayer.preload="auto";
audioPlayer.addEventListener('ended',nextSong,false);
audioPlayer.addEventListener('error',errorFallback,true);
document.getElementById("player").appendChild(audioPlayer);
nextSong();
}
function nextSong() {
if(urls[next]!=undefined) {
var audioPlayer = document.getElementsByTagName('audio')[0];
if(audioPlayer!=undefined) {
audioPlayer.src=urls[next];
audioPlayer.load();
audioPlayer.play();
next++;
} else {
loadPlayer();
}
} else {
alert('the end!');
}
}
function errorFallback() {
nextSong();
}
function playPause() {
var audioPlayer = document.getElementsByTagName('audio')[0];
if(audioPlayer!=undefined) {
if (audioPlayer.paused) {
audioPlayer.play();
} else {
audioPlayer.pause();
}
} else {
loadPlayer();
}
}
function stop() {
var audioPlayer = document.getElementsByTagName('audio')[0];
audioPlayer.pause();
audioPlayer.currentTime = 0;
}
function pickSong(num) {
next = num;
nextSong();
}
var urls = new Array();
urls[0] = '01_horses_mouth/mp3/01. Let The Dog See The Rabbit preface.mp3';
urls[1] = '01_horses_mouth/mp3/02. The Other Horse\'s Tale.mp3';
urls[2] = '01_horses_mouth/mp3/03. Caged Tango.mp3';
urls[3] = '01_horses_mouth/mp3/04. Crumbs.mp3';
urls[4] = '01_horses_mouth/mp3/05. Mood Elevator Reprise.mp3';
urls[5] = '01_horses_mouth/mp3/06. Mood Elevator.mp3';
var next = 0;
// Display our progress bar
audioPlayer.addEventListener('timeupdate', function(){
var length = audioPlayer.duration;
var secs = audioPlayer.currentTime;
var progress = (secs / length) * 100;
$('#progress').css({'width' : progress * 2});
var tcMins = parseInt(secs/60);
var tcSecs = parseInt(secs - (tcMins * 60));
if (tcSecs < 10) { tcSecs = '0' + tcSecs; }
$('#timecode').html(tcMins + ':' + tcSecs);
}, false);
I end up getting the default player which works fine, as do my own play/pause and stop buttons, but the progress bar does nothing.
Oh, and this is what I've stuck in my css:
#progressContainer {position: relative; display: block; height: 20px;
background-color: #fff; width: 200px;
-moz-box-shadow: 2px 2px 5px rgba(0,0,0,0.4);
-webkit-box-shadow: 2px 2px 5px rgba(0,0,0,0.4);
box-shadow: 2px 2px 5px rgba(0,0,0,0.4);
margin-top: 5px;}
#progress {
display: block;
height: 20px;
background-color: #99f;
width: 0;
position: absolute;
top: 0;
left: 0;}
and this is the html:
<div id="player" >
<span id="timecode"></span>
<span id="progressContainer">
<span id="timecode"></span>
<span id="progress"></span>
</div>
The page is here: http://lisadearaujo.com/clientaccess/wot-sound/indexiPhone.html
Please note that this is only working with the media query for iPhone portrait orientation, so if you look at it on a desktop, you'll need to squeeze your window up. :-)
I've now gone with a different solution (http://www.adobe.com/devnet/html5/articles/html5-multimedia-pt3.html) which explained how to acheive this a little better for me. I'm a copy/paster so have very little clue about the correct order in which things must go. What I've got now is this:
function loadPlayer() {
var audioPlayer = new Audio();
audioPlayer.controls="controls";
audioPlayer.preload="auto";
audioPlayer.addEventListener('ended',nextSong,false);
audioPlayer.addEventListener('error',errorFallback,true);
audioPlayer.addEventListener('timeupdate',updateProgress, false);
document.getElementById("player").appendChild(audioPlayer);
nextSong();
}
var urls = new Array();
urls[0] = '01_horses_mouth/mp3/01. Let The Dog See The Rabbit preface.mp3';
urls[1] = '01_horses_mouth/mp3/02. The Other Horse\'s Tale.mp3';
urls[2] = '01_horses_mouth/mp3/03. Caged Tango.mp3';
urls[3] = '01_horses_mouth/mp3/04. Crumbs.mp3';
urls[4] = '01_horses_mouth/mp3/05. Mood Elevator Reprise.mp3';
urls[5] = '01_horses_mouth/mp3/06. Mood Elevator.mp3';
var next = 0;
function updateProgress()
{
var audioPlayer = document.getElementsByTagName('audio')[0];
var value = 0;
if (audioPlayer.currentTime > 0) {
value = Math.floor((100 / audioPlayer.duration) * audioPlayer.currentTime);
}
progress.style.width = value + "%";
}
Hurray. It works. I am not entirely sure why, but that's OK for now...
In my application, I have a store search form.
When user search for near by stores,
It generates a list of locations with a map link.
When user click on the map link, it should build a code for that specifics location.
Can any one suggest me how can i get this done.
I am trying to do soothing like this
public ActionResult GoogleMap(string address)
{
StringBuilder map = new StringBuilder();
map.Append("<h1>");
map.Append(address);
map.Append("</h1>");
ViewBag.Address = map;
return PartialView("_GoogleMap");
}
More Details:
I don't have any working code yet for retrieving map from Google API. What i want to do is I can pass String Address of the location in to my action method and I want to Build Markup that can display the Map Block.
Working Code Link
Here is some working code I hope this will give more details of what I am trying to achieve
If i can built my string builder with all the markup that will do all.
thanks
I got this done by my self
my Script(GoogleMap.js) is like this
var ad1 = $("#ad1").val();
var ad2 = $("#ad2").val();
var address1 = ad1;
var address2 = ad2;
//var lat = $("#lat").val();
//var lng = $("#lng").val();
var map;
var map2;
var gdir;
var gdir2;
var geocoder = null;
var geocoder2 = null;
var addressMarker;
var addressMarker2;
function initialize() {
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("map_canvas"));
gdir = new GDirections(map, document.getElementById("directions"));
GEvent.addListener(gdir, "load", onGDirectionsLoad);
GEvent.addListener(gdir, "error", handleErrors);
setDirections(address2, address1, "en_US");
// setDirections("San Francisco", "Mountain View", "en_US");
}
}
function setDirections(fromAddress, toAddress, locale) {
gdir.load("from: " + fromAddress + " to: " + toAddress,
{ "locale": locale });
}
My View is like this
include this with your API key
<script src="http://maps.google.com/maps?file=api&v=2&sensor=true_or_false&key=ABQIAAAAOdhD1B3BVBtgDtcx-d52URS1RRrijDm1gy4LClE7B_C-myLe1RRtDZ0LHqXIq8q0z4mVVysLOLUxtA"></script>
#Html.Raw("<h1>"+PharmacyLocation+"</h1>")
#Html.Hidden("Adrs1", PharmacyLocation, new { id = "ad1" })
#Html.Hidden("Adrs2", DistanceFrom, new { id = "ad2" })
#Html.Hidden("lat", lt, new { id = "lat" })
#Html.Hidden("lng", lg, new { id = "lng" })
<style type="text/css">
body {
font-family: Verdana, Arial, sans serif;
font-size: 11px;
margin: 2px;
}
table.directions th {
background-color:#EEEEEE;
}
img {
color: #000000;
}
.directions
{
}
.directions td
{
width:300px;
}
#directions
{
width: 300px; height: auto;
float: left;
}
#map_canvas{
width: 300px; height: 400px;
float:right;
margin-top: 16px;
}
</style>
<script src="../../Scripts/GoogleMap.js" type="text/javascript"></script>
<script>
$(document).ready(function () {
initialize()
});
</script>
<table class="directions">
<tr><th>Directions</th><th>Map</th></tr>
<tr>
<td valign="top"><div id="directions"></div></td>
<td valign="top"><div id="map_canvas"></div></td>
</tr>
</table>
<script>
$("#map_canvas").attr("style", "position: absolute; background-color: rgb(229, 227, 223); padding-top:25px;");
</script>
Hope this will be useful for anyone who is in need.