How to check if a selected string contains a substring of an highlight in epubjs - highlight

As the title above.
Assume, I have a paragraph:
It will be seen that this mere painstaking burrower and grub-worm of a poor devil of a Sub-Sub appears to have gone through the long Vaticans and street-stalls of the earth..
The bold string is a highlight. When I drag my mouse to select string
grub-worm of a poor devil of a Sub-Sub
Then I want to check if my selected text contains the highlight(or the part of the highlight) or not. How could I do that?
The code below is the example to add a highlight when I select a text.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>EPUB.js Highlights Example</title>
<script src="../dist/epub.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js"></script>
<link rel="stylesheet" type="text/css" href="examples.css">
<style type="text/css">
::selection {
background: yellow;
}
#extras {
width: 600px;
margin: 40px auto;
}
#highlights {
list-style: none;
margin-left: 0;
padding: 0;
}
#highlights li {
list-style: none;
margin-bottom: 20px;
border-top: 1px solid #E2E2E2;
padding: 10px;
}
#highlights a {
display: block;
}
#viewer.spreads {
top: 0;
margin-top: 50px;
}
[ref="epubjs-mk"] {
background: url("data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPScxLjEnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZycgeG1sbnM6eGxpbms9J2h0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsnIHg9JzBweCcgeT0nMHB4JyB2aWV3Qm94PScwIDAgNzUgNzUnPjxnIGZpbGw9JyNCREJEQkQnIGlkPSdidWJibGUnPjxwYXRoIGNsYXNzPSdzdDAnIGQ9J00zNy41LDkuNEMxOS42LDkuNCw1LDIwLjUsNSwzNC4zYzAsNS45LDIuNywxMS4zLDcuMSwxNS42TDkuNiw2NS42bDE5LTcuM2MyLjgsMC42LDUuOCwwLjksOC45LDAuOSBDNTUuNSw1OS4yLDcwLDQ4LjEsNzAsMzQuM0M3MCwyMC41LDU1LjQsOS40LDM3LjUsOS40eicvPjwvZz48L3N2Zz4=") no-repeat;
width: 20px;
height: 20px;
cursor: pointer;
margin-left: 0;
}
</style>
</head>
<body>
<div id="frame">
<div id="viewer" class="spreads"></div>
<a id="prev" href="#prev" class="arrow">‹</a>
<a id="next" href="#next" class="arrow">›</a>
</div>
<div id="extras">
<ul id="highlights"></ul>
</div>
<script>
// Load the opf
var book = ePub("https://s3.amazonaws.com/moby-dick/OPS/package.opf");
var rendition = book.renderTo("viewer", {
width: "100%",
height: 600,
ignoreClass: 'annotator-hl',
manager: "continuous"
});
var displayed = rendition.display(6);
// Navigation loaded
book.loaded.navigation.then(function(toc){
// console.log(toc);
});
var next = document.getElementById("next");
next.addEventListener("click", function(){
rendition.next();
}, false);
var prev = document.getElementById("prev");
prev.addEventListener("click", function(){
rendition.prev();
}, false);
var keyListener = function(e){
// Left Key
if ((e.keyCode || e.which) == 37) {
rendition.prev();
}
// Right Key
if ((e.keyCode || e.which) == 39) {
rendition.next();
}
};
rendition.on("keyup", keyListener);
document.addEventListener("keyup", keyListener, false);
rendition.on("relocated", function(location){
// console.log(location);
});
// Apply a class to selected text
rendition.on("selected", function(cfiRange, contents) {
rendition.annotations.highlight(cfiRange, {}, (e) => {
console.log("highlight clicked", e.target);
});
contents.window.getSelection().removeAllRanges();
});
this.rendition.themes.default({
'::selection': {
'background': 'rgba(255,255,0, 0.3)'
},
'.epubjs-hl' : {
'fill': 'yellow', 'fill-opacity': '0.3', 'mix-blend-mode': 'multiply'
}
});
// Illustration of how to get text from a saved cfiRange
var highlights = document.getElementById('highlights');
rendition.on("selected", function(cfiRange) {
book.getRange(cfiRange).then(function (range) {
var text;
var li = document.createElement('li');
var a = document.createElement('a');
var remove = document.createElement('a');
var textNode;
if (range) {
text = range.toString();
textNode = document.createTextNode(text);
a.textContent = cfiRange;
a.href = "#" + cfiRange;
a.onclick = function () {
rendition.display(cfiRange);
};
remove.textContent = "remove";
remove.href = "#" + cfiRange;
remove.onclick = function () {
rendition.annotations.remove(cfiRange);
return false;
};
li.appendChild(a);
li.appendChild(textNode);
li.appendChild(remove);
highlights.appendChild(li);
}
})
});
</script>
</body>
</html>

I assume you only know the functionality of epubjs you listed above. From rendition.on(selected,...), we can get output: cfiRange. From book.getRange(cfiRange).then(function (range)..., we can get output: range.
That means whenever we select a word or sentence, we get cfiRange and range.
cfiRange is epubcfi(/6/10[id139]!/4/2[filepos12266]/6,/3:1,/3:4, which based on position of the selected word. I don't know how it calculates/works but if you do then you can check if the cfiRange contains a existing highlight word's cfiRange.
range.toString() can give you the text. if your application is only storing a word. then you can check if the new selected word == or contain your existing highlight word.

Related

Twilio Video Call issue on iOS Safari, After call start video freeze nodejs issue

I have created a video call application using Nodejs & Twilio CLI. And using this in my both mobile app Android & iOS. On Android is working perfectly. But on iOS, there is an issue, when users reach the video call page, it's showing preview but as the user clicks on the Join Room button, then his/her video stops and just showing a black screen. While he can talk with other users and can see the video of them. And the Second user also can see his/her video perfectly. Only the issue he/she can't see his/her video on that call.
My html code
<!DOCTYPE html>
<html>
<head>
<style>
.joinbtn {
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #2b96cc;
color: #fff;
}
.stvbtn {
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #2b96cc;
color: #fff;
}
.endbtn {
float:right;
border: none;
padding: 10px 10px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 14px;
margin: 4px 2px;
cursor: pointer;
background-color: #dc3545;
color: #fff;
}
#media screen and (max-width: 820px) {
video {
object-fit: cover;
width: 100%;
height: 47vh;
}
}
#media screen and (min-width: 821px){
video {
object-fit: contain;
}
}
.connect_btn{
display: flex;
justify-content: center;
align-content: space-around;
margin-top: -50px;
opacity: 0.8;
padding-bottom:8px;
}
button.endbtn:disabled, button.joinbtn:disabled {
background-color: #607d8b;
color: #ffffff;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/webrtc-adapter/6.4.0/adapter.js" type="text/javascript"></script>
<script src="webrtc.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Clifix Video Chat</title>
</head>
<body>
<div id="room-controls">
<video id="video" autoplay muted playsinline loop width="100%"></video>
<div class="connect_btn">
<label for="passcode"></label>
<input id="passcode" type="hidden" value="8514"/>
<!--button class="stvbtn" id="start-video" onclick="viplay()">On/Off</button-->
<button class="joinbtn" id="button-join">Join Room</button>
<button class="endbtn" id="button-leave" disabled="disabled">End Call</button>
</div>
</div>
<!-- EDIT_CODE -->
<script src="//media.twiliocdn.com/sdk/js/video/releases/2.3.0/twilio-video.min.js"></script>
<script src="index.js"></script>
</body>
</html>
My nodejs code:
'use strict';
(() => {
//const ROOM_NAME = 'demo';
var urltemp = location.search;
var array = urltemp.split('?');
var array1 = array[1];
var array2 = array1.split('=');
var id = array2[1];
const ROOM_NAME = id;
const Video = Twilio.Video;
let videoRoom, localStream;
const video = document.getElementById('video');
// preview screen
navigator.mediaDevices
.getUserMedia({ video: true, audio: true })
.then((vid) => {
video.srcObject = vid;
localStream = vid;
});
// buttons
const joinRoomButton = document.getElementById('button-join');
const leaveRoomButton = document.getElementById('button-leave');
joinRoomButton.onclick = () => {
//video.play();
// get access token
fetch(`video-token?passcode=${getPasscode()}&room=${ROOM_NAME}`)
.then((resp) => {
if (resp.ok) {
var url=window.location.href,
separator = (url.indexOf("?")===-1)?"?":"&",
newParam=separator + "join=true";
var newUrl=url.replace(newParam,"");
newUrl+=newParam;
window.history.replaceState(null,null,newUrl);
return resp.json();
} else {
console.error(resp);
if (resp.status === 401) {
throw new Error('Go Back & Join Again');
} else {
throw new Error('Unexpected error. Open dev tools for logs');
}
}
})
.then((body) => {
const token = body.token;
//console.log(token);
//connect to room
return Video.connect(token, { name: ROOM_NAME });
})
.then((room) => {
//console.log(`Connected to Room ${room.name}`);
videoRoom = room;
room.participants.forEach(participantConnected);
room.on('participantConnected', participantConnected);
room.on('participantDisconnected', participantDisconnected);
room.once('disconnected', (error) =>
room.participants.forEach(participantDisconnected)
);
joinRoomButton.disabled = true;
leaveRoomButton.disabled = false;
})
.catch((err) => {
alert(err.message);
});
};
// leave room
leaveRoomButton.onclick = () => {
var url=window.location.href,
separator = (url.indexOf("?")===-1)?"?":"&",
newParam=separator + "end=true";
var newUrl=url.replace(newParam,"");
newUrl+=newParam;
window.history.replaceState(null,null,newUrl);
videoRoom.disconnect();
//console.log(`Disconnected from Room ${videoRoom.name}`);
joinRoomButton.disabled = false;
leaveRoomButton.disabled = true;
};
})();
const getPasscode = () => {
const passcodeInput = document.getElementById('passcode') || {};
const passcode = passcodeInput.value;
passcodeInput.value = '';
return passcode;
};
// connect participant
const participantConnected = (participant) => {
//console.log(`Participant ${participant.identity} connected'`);
const div = document.createElement('div'); //create div for new participant
div.id = participant.sid;
participant.on('trackSubscribed', (track) => trackSubscribed(div, track));
participant.on('trackUnsubscribed', trackUnsubscribed);
participant.tracks.forEach((publication) => {
if (publication.isSubscribed) {
trackSubscribed(div, publication.track);
}
});
document.body.appendChild(div);
};
const participantDisconnected = (participant) => {
//console.log(`Participant ${participant.identity} disconnected.`);
document.getElementById(participant.sid).remove();
};
const trackSubscribed = (div, track) => {
div.appendChild(track.attach());
};
const trackUnsubscribed = (track) => {
track.detach().forEach((element) => element.remove());
};
As per my understanding, before this my video was not working on iOS safari then I have done modifications in my HTML video code.
From this:
<video id="video" autoplay muted width="100%"></video>
To:
<video id="video" autoplay muted playsinline loop width="100%"></video>
Then it starts working as having video freezing at the iOS User side when he/she start calling.
Twilio developer evangelist here.
When you call Video.connect the Video SDK will ask for permission to use your microphone and camera. Safari does not like giving access to the microphone and camera more than once at a time and since you also ask for media access to show the preview, it drops the preview tracks and creates new tracks for the video call. This is why the preview goes dark, but other participants can see and hear the video/audio.
Instead, you should reuse the tracks that you got for the preview by storing a reference to them and then passing them to Video.connect as the tracks property in the ConnectOptions. You already store a reference to the localStream so you can use that when you get to connect, like this:
return Video.connect(token, {
name: ROOM_NAME,
tracks: localStream.getTracks()
});
That way the tracks for the preview will be re-used for the video call and nothing should go dark.

jQuery Droppable: Only allow element to drop, if there is no other element in that dropzone

I'm writing a puzzle, where you have to drag an item into the correct dropzone.
Problem: I want that you can only drag an item into a dropzone, if that dropzone does not contain any other items. How can I check, whether there are no other items in that dropzone?
Here is a gif of my current puzzle:
Here is a gif which shows the problem:
As you can see, I can drag multiple items into the same dropzone.
If a dropzone already contains an item, the user should not be able to drop another item into that dropzone. How do I achieve that?
My script so far:
$( ".draggable" ).draggable({ revert: 'invalid', snap: ".dropfield", snapTolerance: 30, snapMode: "inner"});
$( ".dropfield" ).droppable({
accept: ".dropling",
drop: function( event, ui ) {
if(some-condition){ // if correct word got dragged into the correct dropzone
var id = ui.draggable.attr('id');
$("#" + id).draggable( 'disable' );
$(this).droppable( 'disable' );
$("#" + id).css( "background-color", "#7FFF00");
}
});
Html-excerpt:
<div id="liebe" class="dropling draggable text-center">
Liebe
</div>
<span class="dropfield" value="scheitern">
</span>
PS: There are already several topics on Stack-Overflow with the same question. However, I'm not intelligent enough to apply the suggested answers to my case. Please help me.
Edit1
Here is a gif which shows my preferred behavior:
I dragged a wrong word into a dropzone. But as long that dropzone is occupied by a word, no other words should be able to be dropped into that dropzone.
My current code:
if(some-condition){ //correct word
$("#" + id).draggable( 'disable' );
$(this).droppable( 'disable' );
$("#" + id).css( "background-color", "#7FFF00");
}
} else { //wrong word
console.log("wrong word dropped");
$(this).droppable( 'disable' );
}
As soon as I drag the wrong word out of the dropzone, the dropzone should become enabled again. But how can I achieve that?
I would advise breaking this into their own functions. This way you can enable and disable drop repeatedly. Not sure what you want to trigger the item to become draggable and droppable again based on the example you have supplied. Based on what you have supplied, I can offer this the following example.
$(function() {
function enableDrop($target) {
console.log("Enabled Drop");
$target.droppable({
accept: ".dropling",
classes: {
"ui-droppable-hover": "drop-target"
},
drop: function(event, ui) {
var $that = $(this),
dragWord = ui.draggable.text().trim(),
$item = ui.draggable;
if (checkWord(dragWord)) {
console.log("Accepted: " + $item.attr("id"));
$item.
removeClass("draggable")
.draggable('disable')
.attr("style", "")
.appendTo($that);
disableDrop($that);
$that.css("background-color", "#7FFF00");
} else {
return false;
}
}
});
}
function disableDrop($target) {
console.log("Disabling Drop on " + $target.attr("class"));
$target.droppable("destroy");
}
function checkWord(w) {
var result = false;
console.log("Checked Word: " + w);
if (w == "Liebe") {
result = true;
}
return result;
}
$(".draggable").draggable({
revert: 'valid',
snap: ".dropfield",
snapTolerance: 30,
snapMode: "inner"
});
enableDrop($(".dropfield"));
});
p .dropfield {
border: 1px solid #ccc;
border-radius: 3px;
display: inline-block;
width: 4em;
height: 1.5em;
margin-bottom: -.25em
}
p .drop-target {
border: 1px dashed #ccc;
background-color: #ccc;
}
.text-center {
text-align: center;
}
.draggable {
border: 1px solid #ccc;
border-radius: 3px;
display: inline-block;
width: 4em;
height: 1em;
padding: .25em 0;
margin-bottom: -.25em
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<p>Diese Schlussfolgerung ist <span class="dropfield" value="scheitern"></span>: Ee kann doch nicht sein, dass es gut ist, </p>
<div id="liebe" class="dropling draggable text-center">Liebe</div>
<div id="absurd" class="dropling draggable text-center">absurd</div>
The easiest way here is probably to solve the whole thing in a more generic way. For this I would add an attribute to the respective Dom element (data-count) and then check how many characters are contained and how many are still allowed:
See /** ADDED **/ for the things i did:
$(function() {
function textWrapper(str, sp, btn) {
if (sp == undefined) {
sp = [0, 0];
}
var txt = "";
if (btn) {
txt = "<span class='w b'>" + str + "</span>";
} else {
txt = "<span class='w'>" + str + "</span>";
}
if (sp[0]) {
txt = " " + txt;
}
if (sp[1]) {
txt = txt + " ";
}
return txt;
}
function chunkWords(p) {
var words = p.split(" ");
words[0] = textWrapper(words[0], [0, 1]);
var i;
for (i = 1; i < words.length; i++) {
var re = /\[.+\]/;
if (re.test(words[i])) {
var b = makeTextBox(words[i].slice(1, -1));
words[i] = " " + b.prop("outerHTML") + " ";
} else {
if (words[0].indexOf(".")) {
words[i] = textWrapper(words[i], [1, 0]);
} else {
words[i] = textWrapper(words[i], [1, 1]);
}
}
}
return words.join("");
}
function unChunkWords(tObj) {
var words = "";
$(tObj).contents().each(function(i, el) {
if ($(el).hasClass("b")) {
words += "[" + $(el).text() + "]";
} else {
words += $(el).text();
}
});
return words.replace(/\s+/g, " ").trim();
}
function makeBtn(tObj) {
var btn = $("<span>", {
class: "ui-icon ui-icon-close"
}).appendTo(tObj);
}
function makeTextBox(txt) {
var sp = $("<span>", {
class: "w b"
}).html(txt);
makeBtn(sp);
return sp;
}
function makeDropText(obj) {
return obj.droppable({
drop: function(e, ui) {
var txt = ui.draggable.text();
var newSpan = textWrapper(txt, [1, 0], 1);
$(this).after(newSpan);
makeBtn($(this).next("span.w"));
makeDropText($(this).next("span.w"));
$("span.w.ui-state-highlight").removeClass("ui-state-highlight");
update()
},
over: function(e, ui) {
$(this).add($(this).next("span.w")).addClass("ui-state-highlight");
},
out: function() {
$(this).add($(this).next("span.w")).removeClass("ui-state-highlight");
}
});
}
$("p.given").html(chunkWords($("p.given").text()));
$("p.given").on("click", ".b > .ui-icon", function() {
$(this).parent().remove();
});
$("p.given").blur(function() {
var w = unChunkWords($(this));
$(this).html(chunkWords(w));
makeDropText($("p.given span.w"));
});
$("span.given").draggable({
helper: "clone",
revert: "invalid"
});
makeDropText($("p.given span.w"));
/** ADDED **/
// update at beginning
update();
// register update events
$("p.given").on('click keydown keyup drag drop', update);
function update(e) {
var templateText = unChunkWords($("p.given"));
var templateTextWithoutParameters = templateText.replace(/\[(.+?)\]/g, "");
var templateTextWithoutParametersLenght = templateTextWithoutParameters.length;
// calc total length
var totalLength = templateTextWithoutParametersLenght;
// since 'helper: clone' we have to ignore it!
$("[data-count]:not(.ui-draggable-dragging)").each(function(index, item) {
var count = $(item).attr("data-count")
var text = "[" + $(item).text() + "]";
var length = templateText.split(text).length - 1;
totalLength += count * length;
});
// 46,8 keycodes for delete & backspace
var maxLength = 200;
if (totalLength >= maxLength && e && e.keyCode !== 46 && e.keyCode !== 8) {
e.preventDefault();
}
// disable data counts
var remaining = maxLength - totalLength;
$("[data-count]:not(.ui-draggable-dragging)").each(function(index, item) {
var count = $(item).attr("data-count");
if (parseInt(count) > remaining) {
$(item).attr("disabled", true);
$(item).draggable().draggable('disable');
} else {
$(item).attr("disabled", false);
$(item).draggable().draggable('enable');
}
})
$(".output").text(totalLength);
}
});
p.given {
display: flex;
flex-wrap: wrap;
}
p.given span.w span.ui-icon {
cursor: pointer;
}
div.blanks {
display: inline-block;
min-width: 50px;
border-bottom: 2px solid #000000;
color: #000000;
}
div.blanks.ui-droppable-active {
min-height: 20px;
}
span.answers>b {
border-bottom: 2px solid #000000;
}
span.given {
margin: 5px;
}
/** ADDED **/
[disabled] {
color: grey
}
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="row">
<p class="given" contenteditable="true">Lorem Ipsum is [Test] Ipsum has been the industry's [America] standard dummy text ever since the 1500s, </p>
</div>
<div class="divider"></div>
<div class="section">
<section>
<div class="card blue-grey ">
<div class="card-content white-text">
<div class="row">
<div class="col s12">
<span class="given btn-flat white-text red lighten-1" rel="1" data-count="50">Test</span>
<span class="given btn-flat white-text red lighten-1" rel="2" data-count="30">America</span>
<span class="given btn-flat white-text red lighten-1" rel="3" data-count="20">Qatar</span>
<span class="given btn-flat white-text red lighten-1" rel="4" data-count="10">Philippines</span>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="divider"></div>
Count: <span class="output"></span>

OSM building and jQuery mobile

I have a problem with input "range" and OSM Buildings for leaflet maps. I used basic example from official example which works fine but if I add jquery mobile to <head> section it breaks the range input. It's strange... I was using jquery mobile input to change the leaflet map opacity and it's working. Here is my basic example:
var map = new L.Map('map');
map.setView([52.52111, 13.40988], 16, false);
new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'osm.org',
maxZoom: 18,
maxNativeZoom: 20
}).addTo(map);
var osmb = new OSMBuildings(map).load();
//************************************************************
var now,
date, time,
timeRange, dateRange,
timeRangeLabel, dateRangeLabel;
function changeDate() {
var Y = now.getFullYear(),
M = now.getMonth(),
D = now.getDate(),
h = now.getHours(),
m = 0;
timeRangeLabel.innerText = pad(h) + ':' + pad(m);
dateRangeLabel.innerText = Y + '-' + pad(M+1) + '-' + pad(D);
osmb.date(new Date(Y, M, D, h, m));
}
function onTimeChange() {
now.setHours(this.value);
now.setMinutes(0);
changeDate();
}
function onDateChange() {
now.setMonth(0);
now.setDate(this.value);
changeDate();
}
function pad(v) {
return (v < 10 ? '0' : '') + v;
}
timeRange = document.getElementById('time');
dateRange = document.getElementById('date');
timeRangeLabel = document.querySelector('*[for=time]');
dateRangeLabel = document.querySelector('*[for=date]');
now = new Date;
changeDate();
// init with day of year
var Jan1 = new Date(now.getFullYear(), 0, 1);
dateRange.value = Math.ceil((now-Jan1)/86400000);
timeRange.value = now.getHours();
timeRange.addEventListener('change', onTimeChange);
dateRange.addEventListener('change', onDateChange );
timeRange.addEventListener('input', onTimeChange);
dateRange.addEventListener('input', onDateChange);
body {
font-family: sans-serif;
padding: 5px;
margin: 0;
}
#map {
width: 300px;
height: 300px;
float: left;
margin: 0 15px 0 0;
}
label {
height: 20px;
}
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>Shadows</title>
<script src='http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js'></script>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css">
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<script src='http://cdn.osmbuildings.org/OSMBuildings-Leaflet.js'></script>
<link rel='stylesheet prefetch' href='http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css'>
</head>
<body>
<div id="map"></div>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<input id="date" type="range" min="1" max="365" step="1"><label for="date"></label><br>
<input id="time" type="range" min="0" max="23" step="1"><label for="time"></label>
</body>
</html>
For jQuery Mobile rangesliders you may use:
$(document).on("pagecreate", "#page-1", function() {
$("#date").on("change", onDateChange);
$("#time").on("change", onTimeChange);
});
Codepen: http://codepen.io/anon/pen/gLJRXb

calculateDistances() function doesn't work taking start/end value from a select

I'd like to calculate the distance between 2 points, taking coordinates for each point from a select. The following is the code so far
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Calcola il tuo itinerario</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0;
padding: 0;
}
#panel {
position: absolute;
top: 5px;
left: 20%;
margin-left: -180px;
z-index: 5;
background-color: #fff;
padding: 5px;
border: 1px solid #999;
width: 300px;
}
/*
Provide the following styles for both ID and class,
where ID represents an actual existing "panel" with
JS bound to its name, and the class is just non-map
content that may already have a different ID with
JS bound to its name.
*/
#panel, .panel {
font-family: 'Roboto','sans-serif';
line-height: 30px;
padding-left: 10px;
}
#panel select, #panel input, .panel select, .panel input {
font-size: 15px;
}
#panel select, .panel select {
width: 100%;
}
#panel i, .panel i {
font-size: 12px;
}<br>
#outputDiv {
font-size: 11px;
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true"></script>
<script>
var directionsDisplay;
var directionsService = new google.maps.DirectionsService();
var map;
function initialize() {
directionsDisplay = new google.maps.DirectionsRenderer();
var chiesamadre = new google.maps.LatLng(37.485558, 13.987883);
var mapOptions = {
zoom:17,
center: chiesamadre
};
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
directionsDisplay.setMap(map);
}
function calcRoute() {
var start = document.getElementById('start').value;
var end = document.getElementById('end').value;
var request = {
origin:start,
destination:end,
travelMode: google.maps.TravelMode.WALKING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
directionsDisplay.setDirections(response);
}
});
}
function calculateDistances() {
var start = new google.maps.Map(document.getElementById('start').value);
var end = new google.maps.Map(document.getElementById('end').value);
var service = new google.maps.DistanceMatrixService();
service.getDistanceMatrix(
{
origin:start,
destination:end,
travelMode: google.maps.TravelMode.WALKING,
unitSystem: google.maps.UnitSystem.METRIC,
avoidHighways: false,
avoidTolls: false
}, callback);
}
function callback(response, status) {
if (status != google.maps.DistanceMatrixStatus.OK) {
alert('Error was: ' + status);
} else {
var origins = response.originAddresses;
var destinations = response.destinationAddresses;
var outputDiv = document.getElementById('outputDiv');
outputDiv.innerHTML = '';
deleteOverlays();
for (var i = 0; i < origins.length; i++) {
var results = response.rows[i].elements;
addMarker(origins[i], false);
for (var j = 0; j < results.length; j++) {
addMarker(destinations[j], true);
outputDiv.innerHTML += origins[i] + ' to ' + destinations[j]
+ ': ' + results[j].distance.text + ' in '
+ results[j].duration.text + '<br>';
}
}
}
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="panel">
<b>Partenza: </b>
<select id="start" onChange="calcRoute();">
<option value="37.485558, 13.987883">Chiesa Madre</option>
<option value="37.484813, 13.983602">Calvario</option>
<option value="37.484474, 13.986312">Torre civica con orologi</option>
<option value="37.485703, 13.986480">Chiesa di San Giuseppe</option>
<option value="37.491049, 13.988843">Torre Con Orologi Cristo re</option>
<option value="37.484507, 13.989363">Chiesa Santa Maria del Rosario</option>
<option value="37.472353, 13.945771">Vassallaggi</option>
</select>
<b>Arrivo: </b>
<select id="end" onChange="calcRoute();">
<option value="37.485558, 13.987883">Chiesa Madre</option>
<option value="37.484813, 13.983602">Calvario</option>
<option value="37.484474, 13.986312">Torre civica con orologi</option>
<option value="37.485703, 13.986480">Chiesa di San Giuseppe</option>
<option value="37.491049, 13.988843">Torre Con Orologi Cristo re</option>
<option value="37.484507, 13.989363">Chiesa Santa Maria del Rosario</option>
<option value="37.472353, 13.945771">Vassallaggi</option>
</select>
<b>Distanza: </b>
<p><button type="button" onClick="calculateDistances();">Calculate
distances</button></p>
<p> <div id="outputDiv"></div></p>
</div>
<div id="map-canvas"></div>
</body>
</html>
calcRoute() function works fine but calculateDistances() function doesn't work, in fact if I click on the "Calculate distances" button after selecting start and end, no text is showed in the white space on the button, where is the div with the expected result of calculated distance. Maybe I did something wrong with the start and end variables of calculateDistances() function. I added this JSfiddle: https://jsfiddle.net/Lnq91j0c/
and this is the link of the page online: http://turismosancataldo.it/calcolo-itinerario-piedi-dist.html
How to fix the function? Thank You for any help.

dynamic legend for fusion table map

I have a modified sample of fusion table map and its code is given below.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<title>Fusion Tables Layer Example: Dynamic styles and templates</title>
<style>
body { font-family: Arial, sans-serif; font-size: 12px; }
#map-canvas { height: 660px; width: 100%; }
#map-canvas img { max-width: none; }
#visualization { height: 100%; width: 100%; }
#legend1 { width: 200px; background: #FFF;padding: 10px; margin: 5px;font-size: 12px;font-family: Arial, sans-serif;border: 1px solid black;}
.color {border: 1px solid;height: 12px;width: 15px; margin-right: 3px;float: left;}
.red {background: #C00;}
.blue {background: #06C;}
</style>
<script type="text/javascript" src="https://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
var map = new google.maps.Map(document.getElementById('map-canvas'), {
center: new google.maps.LatLng(37.4, -122.1),
zoom: 10,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var layer = new google.maps.FusionTablesLayer({
query: {
select: 'Address',
from: '15UY2pgiz8sRkq37p2TaJd64U7M_2HDVqHT3Quw'
},
map: map,
styleId: 1,
templateId: 1
});
var legend1 = document.createElement('div');
legend1.id = 'legend1';
var content1 = [];
content1.push('<p><div class="color red"></div>Red markers</p>');
legend1.innerHTML = content1.join('');
legend1.index = 1;
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(legend1);
var legend2 = document.createElement('div');
legend2.id = 'legend1';
var content2 = [];
content2.push('<p><div class="color blue"></div>Blue markers</p>');
legend2.innerHTML = content2.join('');
legend2.index = 1;
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(legend2);
google.maps.event.addDomListener(document.getElementById('style'),
'change', function() {
var selectedStyle = this.value;
layer.set('styleId', selectedStyle);
var selectedTemplate = this.value;
layer.set('templateId', selectedTemplate);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
<div>
<label>Select style:</label>
<select id="style">
<option value="1">Red</option>
<option value="2">Blue</option>
</select>
</div>
</body>
</html>
How can I add dynamic legend to this map so that when selecting blue markers, the legend should show only a blue marker with its name and when selecting red markers it will show the red marker icon in legend.
You must clear the controls(remove all legends) and then add the desired legend again.
right before
google.maps.event.addDomListener(document.getElementById('style')[...]
add this code:
//we need a copy of all legends(nodes),
//otherwise they wouldn't be accessible when they have been removed
var clonedArray = map.controls[google.maps.ControlPosition.RIGHT_BOTTOM]
.getArray().slice();
//observe changes of the styleId
google.maps.event.addListener(layer,'styleid_changed',function(){
//clear the controls
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].clear();
//add the desired control
map.controls[google.maps.ControlPosition.RIGHT_BOTTOM]
.push(clonedArray[this.get('styleId')-1])
});
//trigger the event to initially have only the legend
//based on the selected style
google.maps.event.trigger(layer,'styleid_changed');
Demo: http://jsfiddle.net/doktormolle/t3nY6/

Resources