Refreshing animated tile layer without reloading maps control - bing-maps

I'm using the animated radar layer from the Bing Maps interactive SDK:
https://www.bing.com/api/maps/sdkrelease/mapcontrol/isdk/weatherradarmap?toWww=1&redig=40890DB2E2C449AC91DAD6CD69209931
I understand how the tile layers are getting assigned. How could I modify that so that the tile layers were refreshed on an interval (say every 15 minutes) without reloading the entire page (meaning without reloading the maps control)?
So far I've pulled out the tileSource loop into its own function:
function addRadarLayer() {
// alert to mark time when radar data was loaded or refreshed
// var d = new Date();
// alert("Updating radar " + d.toLocaleTimeString());
// alert (map.layers.length);
for (var i = 0; i < timestamps.length; i++) {
var tileSource = new Microsoft.Maps.TileSource({
uriConstructor: urlTemplate.replace('{timestamp}', timestamps[i])
});
tileSources.push(tileSource);
};
if (map.layers.length>0) {
// alert('clear layer');
// map.layers.clear();
// alert(map.layers.length);
//alert('add layer')
map.layers[0].setOptions({ mercator: tileSources, frameRate: 500 });
} else {
var animatedLayer = new Microsoft.Maps.AnimatedTileLayer({ mercator: tileSources, frameRate: 500 });
map.layers.insert(animatedLayer);
//alert('New tiles');
};
};
then I call that function directly and then using setInterval
addRadarLayer();
window.setInterval(function () {
addRadarLayer();
}, 1000 * 60 * 15); // repeat forever, polling every 15 minutes
};
But after some time I can see the page no longer refreshing. If I refresh the browser all is OK, but (again) that reloads the map control which is not what I really want.

When you refresh the tile source, add a random query string parameter to the URL like
?r=1234 and change the value (1234) on each refresh. This should by pass the local cache which is root cause of your issue.

Related

Refresh page every 5 secs in Protractor until element is present

Scenario: I am working with Protractor testing framework and I need to refresh the page every 5 seconds until the element is present on the web page but I am not sure how to do it.
I have seen protractor documentation and I have come across this page https://www.protractortest.org/#/api?view=ProtractorExpectedConditions even this doesn't refresh the page
var EC = protractor.ExpectedConditions;
var ele = element(by.control({controlType: "sap.m.ObjectIdentifier", id: /clone/,
properties: {text: "MANAGER"}}));
var isVisible = EC.visibilityOfElementLocated(ele);
browser.wait(isVisible, 5000); //wait for an element to become visible
browser.sleep(3000)
Any suggestions
You can use browser.refresh() in a loop:
while(true) {
if(/*element not visible */) {
browser.refresh();
} else {
break;
}
browser.sleep(5000);
}

RxJs Observable with infinite scroll OR how to combine Observables

I have a table which uses infinite scroll to load more results and append them, when the user reaches the bottom of the page.
At the moment I have the following code:
var currentPage = 0;
var tableContent = Rx.Observable.empty();
function getHTTPDataPageObservable(pageNumber) {
return Rx.Observable.fromPromise($http(...));
}
function init() {
reset();
}
function reset() {
currentPage = 0;
tableContent = Rx.Observable.empty();
appendNextPage();
}
function appendNextPage() {
if(currentPage == 0) {
tableContent = getHTTPDataPageObservable(++currentPage)
.map(function(page) { return page.content; });
} else {
tableContent = tableContent.combineLatest(
getHTTPDataPageObservable(++currentPage)
.map(function(page) { return page.content; }),
function(o1, o2) {
return o1.concat(o2);
}
)
}
}
There's one major problem:
Everytime appendNextPage is called, I get a completely new Observable which then triggers all prior HTTP calls again and again.
A minor problem is, that this code is ugly and it looks like it's too much for such a simple use case.
Questions:
How to solve this problem in a nice way?
Is is possible to combine those Observables in a different way, without triggering the whole stack again and again?
You didn't include it but I'll assume that you have some way of detecting when the user reaches the bottom of the page. An event that you can use to trigger new loads. For the sake of this answer I'll say that you have defined it somewhere as:
const nextPage = fromEvent(page, 'nextpage');
What you really want to be doing is trying to map this to a stream of one directional flow rather than sort of using the stream as a mutable object. Thus:
const pageStream = nextPage.pipe(
//Always trigger the first page to load
startWith(0),
//Load these pages asynchronously, but keep them in order
concatMap(
(_, pageNum) => from($http(...)).pipe(pluck('content'))
),
//One option of how to join the pages together
scan((pages, p) => ([...pages, p]), [])
)
;
If you need reset functionality I would suggest that you also consider wrapping that whole stream to trigger the reset.
resetPages.pipe(
// Used for the "first" reset when the page first loads
startWith(0),
//Anytime there is a reset, restart the internal stream.
switchMapTo(
nextPage.pipe(
startWith(0),
concatMap(
(_, pageNum) => from($http(...)).pipe(pluck('content'))
),
scan((pages, p) => ([...pages, p]), [])
)
).subscribe(x => /*Render page content*/);
As you can see, by refactoring to nest the logic into streams we can remove the global state that was floating around before
You can use Subject and separate the problem you are solving into 2 observables. One is for scrolling events , and the other is for retrieving data. For example:
let scrollingSubject = new Rx.Subject();
let dataSubject = new Rx.Subject();
//store the data that has been received back from server to check if a page has been
// received previously
let dataList = [];
scrollingSubject.subscribe(function(page) {
dataSubject.onNext({
pageNumber: page,
pageData: [page + 10] // the data from the server
});
});
dataSubject.subscribe(function(data) {
console.log('Received data for page ' + data.pageNumber);
dataList.push(data);
});
//scroll to page 1
scrollingSubject.onNext(1);
//scroll to page 2
scrollingSubject.onNext(2);
//scroll to page 3
scrollingSubject.onNext(3);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>

How to wait the page to test is loaded in non angular site?

I've tried this:
browser.wait(function () {
return browser.executeScript('return document.readyState==="complete" &&' +
' jQuery !== undefined && jQuery.active==0;').then(function (text) {
return text === true;
});
}, 30000);
If jQuery.active==0 then page is completely loaded. This should work for sites with JQuery and non angular pages.
However, I have many problems of instability to test for non angular sites.
How to fix this?
By default protractor waits until the page is loaded completely. If you are facing any error then it is because protractor is waiting for the default time to be completed, that you have specified in your conf.js file to wait until page loads. Change the value to wait a for longer time if you think your app is slow -
// How long to wait for a page to load.
getPageTimeout: 10000, //Increase this time to whatever you think is better
You can also increase the defaultTimeoutInterval to make protractor wait a little longer before the test fails -
jasmineNodeOpts: {
// Default time to wait in ms before a test fails.
defaultTimeoutInterval: 30000
},
If you want to wait for any particular element, then you can do so by using wait() function. Probably waiting for last element to load is the best way to test it. Here's how -
var EC = protractor.ExpectedConditions;
var lastElement = element(LOCATOR_OF_LAST_ELEMENT);
browser.wait(EC.visibilityOf(lastElement), 10000).then(function(){ //Alternatively change the visibilityOf to presenceOf to check for the element's presence only
//Perform operation on the last element
});
Hope it helps.
I use ExpectedConditions to wait for, and verify page loads. I walk through it a bit on my site, and example code on GitHub. Here's the gist...
Base Page: (gets extended by all page objects)
// wait for & verify correct page is loaded
this.at = function() {
var that = this;
return browser.wait(function() {
// call the page's pageLoaded method
return that.pageLoaded();
}, 5000);
};
// navigate to a page
this.to = function() {
browser.get(this.url, 5000);
// wait and verify we're on the expected page
return this.at();
};
...
Page Object:
var QsHomePage = function() {
this.url = 'http://qualityshepherd.com';
// pageLoaded uses Expected Conditions `and()`, that allows us to use
// any number of functions to wait for, and test we're on a given page
this.pageLoaded = this.and(
this.hasText($('h1.site-title'), 'Quality Shepherd')
...
};
QsHomePage.prototype = basePage; // extend basePage
module.exports = new QsHomePage();
The page object may contain a url (if direct access is possible), and a pageLoaded property that returns the ExepectedCondition function that we use to prove the page is loaded (and the right page).
Usage:
describe('Quality Shepherd blog', function() {
beforeEach(function() {
// go to page
qsHomePage.to();
});
it('home link should navigate home', function() {
qsHomePage.homeLink.click();
// wait and verify we're on expected page
expect(qsHomePage.at()).toBe(true);
});
});
Calling at() calls the ExpectedCondidion (which can be be an and() or an or(), etc...).
Hope this helps...

Auto scroll or End less Scroll in Iphone

I am developing iphone application.
need to display 5 records at a time
and when i reached page end then the next 5 records will need to load using automatic scrolling.
initial 5 records display done.
Scroll not working when i reach page end.
Can any one.
I found the solution. my code as follows,
myTable.addEventListener('scroll', function(e){
if (Ti.Platform.osname === 'iphone')
{
var offset = e.contentOffset.y;
var height = e.size.height;
var total = offset + height;
var theEnd = e.contentSize.height;
// this condition will check whether the scroll reach end or not?
if (theEnd == total)
{
//call the function once again.
loadContents();
}
}
});
I hope... some one will use it.
#Suresh.g
You Can use "ScrollView" with property "contentHeight :Ti.UI.Size (or "Auto")".
When you use "ScrollView" it set default height on your requirement.
You can also enable for
showHorizontalScrollIndicator : true,
for more information read this blog

Drag an event in fullCalendar component with a specific duration

I've seen the solution to drag and drop external events in fullcalendar. But, in this demo, all the external events have a duration of 2 hours (because defaultEventMinutes parameter is set to 120). I'm trying to change this demo in order to manage events with different durations. Say, "My event 1" is 45min long, "My event 2" is 165min, etc.
At the beginning I though there may be an attribute to store the duration in the eventObject, but according to the documentation, it's not the case.
Then, I thought it would be possible to change the value of 'defaultEventMinutes' when starting dragging the event. But apparently, I can't do it without rebuilding the whole calendar.
According to you, what is the best means to meet this requirement?
Thanks in advance for your advice...
Worked on this as well and have solved the duration shown on fullCalendar this way:
Having a custom "setOptions" function for fullCalendar.
Having a property for fullCalendar called "dragMinutes" that can be set during elements $(this).draggable({start:...}).
Here is the code for the custom setOptions:
...
function Calendar(element, options, eventSources) {
var t = this;
// hack for setting options that updates
function setOptions(new_options, refresh) {
$.extend(options, new_options);
if (refresh) {
var viewName = currentView.name;
changeView(viewName, true);
}
}
// exports ...
t.setOptions = setOptions;
...
Heres the code for handling "dragMinutes" option in fullCalendar:
/* External Dragging
--------------------------------------------------------------------------------*/
function dragStart(_dragElement, ev, ui) {
hoverListener.start(function (cell) {
clearOverlays();
if (cell) {
if (cellIsAllDay(cell)) {
renderCellOverlay(cell.row, cell.col, cell.row, cell.col);
} else {
var d1 = cellDate(cell);
if (opt('dragMinutes'))
var d2 = addMinutes(cloneDate(d1), opt('dragMinutes'));
else
var d2 = addMinutes(cloneDate(d1), opt('defaultEventMinutes'));
renderSlotOverlay(d1, d2);
}
}
}, ev);
}
And heres how i make event draggable and update the "dragMinutes":
// make the event draggable using jQuery UI
$(this).draggable({
containment: 'document',
// return a custom styled elemnt being dragged
helper: function (event) {
return $('<div class="uv-planning-dragging"></div>').html($(this).html());
},
opacity: 0.70,
zIndex: 10000,
appendTo: 'body',
cursor: 'move',
revertDuration: 0,
revert: true,
start: function (e, ui) {
// set the "dragMinutes" option in fullCalendar so shown interval about to be added is correct.
var data = $(this).data('eventObject');
if (data) {
var min = data.jsonProps.durationMsec / 1000 / 60;
if (macroCalendar.calendar) {
macroCalendar.calendar.fullCalendar('setOptions', { dragMinutes: Math.round(min) }, false);
}
}
},
stop: function (e, ui) {
// further process
}
});
Hope it helps.
If anyone still visits the thread and don't find the solution, the solution would be to set the duration parameter in event div... and then call draggable on that div.
$(this).data('event', {
title: 'new event title', // use the element's text as the event title
id: $(this).attr('id'),
stick: true, // maintain when user navigates (see docs on the renderEvent method)
duration: '03:00:00' // will set the duration during drag of event
});
Currently, the best solution I have found is adding a duration attribute on my event Object, then the code to create my fullCalendar looks like this:
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
editable: true,
droppable: true, // this allows things to be dropped onto the calendar !!!
drop: function(date, allDay) { // this function is called when something is dropped
// retrieve the dropped element's stored Event Object
var originalEventObject = $(this).data('eventObject');
// we need to copy it, so that multiple events don't have a reference to the same object
var copiedEventObject = $.extend({}, originalEventObject);
// assign it the date that was reported
copiedEventObject.start = date;
// HERE I force the end date based on the start date + duration
copiedEventObject.end = new Date(date.getTime() + copiedEventObject.duration * 60 * 1000);
copiedEventObject.allDay = allDay;
// render the event on the calendar
// the last `true` argument determines if the event "sticks" (http://arshaw.com/fullcalendar/docs/event_rendering/renderEvent/)
$('#calendar').fullCalendar('renderEvent', copiedEventObject, true);
// is the "remove after drop" checkbox checked?
if ($('#drop-remove').is(':checked')) {
// if so, remove the element from the "Draggable Events" list
$(this).remove();
}
}
});
The only drawback is when you're dragging the event, the event duration looks like defaultEventMinutes and not the actual duration, but I don't know how to fix it
These special properties can either be specified in the provided event object, or they can be standalone data attributes:
<!-- DURATION OF 3 hours EVENT WILL PROPAGATE TO CALENDAR WHEN DROPPED -->
<div class='draggable' data-event='1' data-duration='03:00' />
https://fullcalendar.io/docs/dropping/eventReceive/
With the latest fullcalendar v2.0.2, if you want the overlay to be of the particular duration, you can update in this function of fullcalendar-arshaw.js
function dragStart(_dragElement, ev, ui) {
hoverListener.start(function(cell) {
clearOverlays();
if (cell) {
var seconds = duration_in_minutes * 1000 * 60 ;
// we need to pass seconds into milli-seconds
if (d1.hasTime()) {
d2.add(seconds);
renderSlotOverlay(d1, d2, cell.col);
}
else {
d2.add(calendar.defaultAllDayEventDuration);
renderDayOverlay(d1, d2, true, cell.col);
}
}
}, ev);
}
Here, pass your duration in the external events object and that object you can fetch in _dragElement and then convert it into milli-seconds and pass it in d2.add(seconds). This will create the shadow of that mili-seconds on that calendar.
For non-external events you can use the fullcalendar settings:
defaultTimedEventDuration: (hours+':00:00'),
forceEventDuration: true,
// defaultEventMinutes: hours*60, // not needed
and in the event data you do not set the end property (or you null it):
eventData = {
title: title,
start: start,
// end: end, // MUST HAVE no end for fixedduration
color: '#00AA00',
editable: true, // for dragging
};
Ref: http://fullcalendar.io/docs/event_data/defaultTimedEventDuration/
Tip: In case you want to prevent the resizing of the events which is possible due to editable: true, you can use CSS to hide the handle: .fc-resizer.fc-end-resizer { display:none; }
Since v4 some of the above options are not working at all. The problem i was facing was as follows:
All day items for me have a duration, but not a start time. When i select a start time by dragging, the start time is set but as soon as i set the end date ( which is done similar as above answers ), the end date is reset again.. there is something buggy going on in the setDate function... the end date is set, this part works, then it does a comparisson on itself to find out the time difference between the dates, but the date is already set by the system itself causing the difference to be 0 which is causing the enddate to be set to null again......
A giant pain in my neck i got to say... it works perfect when staying within the timeline, but that's about it.
I managed to 'fix', more like destroy it by using this line in the eventDrop event, but it will also work in any other events you may use:
update your event with ajax here, since you have the start and end date *
calendar.refetchEvents(); in the success function
This is going to refetch all the events, it sounds pretty killer for performance but it doesn't seem to take up much time, try it for yourself.
This way my titles, times etc are always up to date and the calendar is showing the right end date.