Pebble time refresh UI card contents without calling new card - pebble-watch

so I have this simple code:
var UI = require('ui');
var numero = 1;
setInterval(function(){
var main = new UI.Card({ body: numero });
numero++;
main.show();
}, 1000);
I want to do the same without calling a new card, how can I do this? In a simpler way

This is what you want:
var UI = require('ui');
var numero = 1;
var main = new UI.Card({ body: numero });
main.show();
setInterval(function(){
main.body(numero);
numero++;
}, 1000);
In your original code, every time setInterval would run you would define a new card and display it on top of whatever was already displayed. What you want to do instead is define the card first, then on each successive iteration you can update that one card's body.
Notice that you do not need to have main.show(); to update the display. By changing the body text, the display is updated.

Related

My Throttable EventStream implementation - is it redundant?

Subject: I have a stream (actually combined stream from Bacon.interval and buttons clicks EventStreams) wich fires ajax request and solve task of manual and automatic data refresh.
Problem: After manual events (buttons clicks) I need reset timer because two immediate updates looks ugly.
My solution: I've created my own Bacon.interval implementation where event polls can be reseted http://jsfiddle.net/cvvkxveq/1/:
Bacon.dynInterval = function(time,resetter){
if(!time) return Bacon.once(new Bacon.Error("Invalid args"));
var ivId, lastTime = Date.now();
return time < 1 ? Bacon.once(new Bacon.Error("Invalid time")) : Bacon.fromBinder(function(sink) {
function setUpInterval(){
if(ivId) clearInterval(ivId);
ivId = setInterval(function(){
var n = Date.now();
var tdx = n - lastTime;
lastTime = n;
sink(new Bacon.Next(tdx));
},time);
}
setUpInterval();
if(resetter) resetter.onValue(setUpInterval);
return function() {
clearInterval(ivId);
sink(new Bacon.End())
}
})
}
Question: Is such behaivour can be done without custom event stream?
Update (thanks #raimohanska's answer) basing on #raimohanska's answer I've also converded my ouUiE event sream (manualTriggerE) to property with initial value to accomplish immediate updates starts.
var quotesService = Bacon.constant({url:"quotes.php"});
var onUiE = $("#next_stock, #prev_stock, #refresh_quotes").clickE().map(".currentTarget.id");
var onUiP = onUiE.toProperty("");
var periodicUpdateE = onUiP.flatMapLatest(function(){ return Bacon.interval(3000)});
var manualPlusPeriodicP = onUiP.toEventStream().merge(periodicUpdateE);
var quotesStream = quotesService.sampledBy(manualPlusPeriodicP).ajax();
If you have a stream manualTriggerE, you can add periodic updates that are reseted on each manual trigger like this:
let periodicUpdateE = manualTriggerE.flatMapLatest(() => Bacon.interval(1000))
let manualPlusPeriodicE = manualTrigger.merge(periodicUpdateE)
The trick is flatMapLatest: it restarts the periodic updates whenever a manual trigger occurs.

Leaflet Icon on multiple layer calling with ajax

i've a little issue for calling new icon with leaflet.
this is my code and i'm using the ajax lib leaflet-ajax.
var map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
var Icon1 = L.icon({
iconUrl: '/img/pin.svg',
iconSize: [38, 40]
});
var Icon2 = L.icon({
iconUrl: '/img/pin2.svg',
iconSize: [38, 40]
});
var Icon3 = L.icon({
iconUrl: '/img/pin3.svg',
iconSize: [38, 40]
});
function popUp(feature, layer) {layer.bindPopup('<p><b>' + feature.properties.name + '</b></p>' + '<p>' + feature.properties.description + '</p>');}
function popUp2(feature, layer) {layer.bindPopup('<p><b>' + feature.properties.name + '</b></p>' + '<p>' + feature.properties.special + '</p>');}
and where i think i have my issue with the call method for the icon
// call json
var geojsonLayer1 = new L.GeoJSON.AJAX("/json/jsonlayer1.json", {onEachFeature:popUp}, {icon:Icon1});
var geojsonLayer2 = new L.GeoJSON.AJAX("/json/jsonlayer2.json", {onEachFeature:popUp}, {icon:Icon2});
var geojsonLayer3 = new L.GeoJSON.AJAX("/json/jsonlayer3.json", {onEachFeature:popUp2}, {icon:Icon3});
// create group layer
var group1 = L.layerGroup([geojsonLayer1]);
var group2 = L.layerGroup([geojsonLayer2]);
var group3 = L.layerGroup([geojsonLayer3]);
// call group layer on dialog box
var checkboxesJson = {
"layer1": group1,
"layer2": group2,
"layer3": group3
};
L.control.layers(null,checkboxesJson).addTo(map);
Thank you for your help !
If my understanding is correct, you are trying to create a customized GeoJSON layer group through using leaflet-ajax plugin. But you do not know how to specify the icon that should be applied on markers for points of your GeoJSON data?
It looks like this plugin will simply use all standard L.GeoJSON options that are specified in the second argument of the constructor / factory, as you did for your onEachFeature option. Note that you can have many options in one single object.
However, you should use option pointToLayer instead of just icon, which is an option for a Marker. The function that you pass to pointToLayer should return an L.Marker, where indeed you would use the icon option.
For example you could do:
var geojsonLayer1 = L.geoJson.ajax(
"/json/jsonlayer1.json",
{
onEachFeature: popUp,
pointToLayer: function (feature, latlng) {
return L.marker(latlng, { icon: Icon1 })
}
});
Note that it is usually recommended to instantiate one icon per marker, even though it should work in your simple case.
By the way, note that L.geoJson (and with ajax plugin, L.geoJson.ajax) return an extended type of L.LayerGroup, so you do not have to import them into a new Layer Group to be able to use them in the Layers Control.
Therefore you could directly do:
// call group layer on dialog box
var checkboxesJson = {
"layer1": geojsonLayer1,
"layer2": geojsonLayer2,
"layer3": geojsonLayer3
};
L.control.layers(null,checkboxesJson).addTo(map);

Initialize youtube api to target ajax loaded iframe

it's my first time to work with Youtube API and after I got most things done, I'm having one last issue with it which i can't seem to solve, so I would appreciate if you could take a few minutes to help.
Here it goes in simple words:
I have a video slider (carousel). Under the main video, are thumbnails of other related videos (this is the site: kushtube.com)
when I click on a related video thumbnail, the main player content is loaded via AJAX
What I wanted to ultimately achieve is that when the current video ends,the next vicdo starts playing automatically.
Now after I did some work, I managed to make it work like that, BUT
it only work on page load...
If i click on one of the related video thumbnails, the content of the player loads via AJAX and I cant seem to be able to re-initialize Youtube API to target the new ajax-loaded player.
This is my current code:
<script>
// global variable for the player
var player;
function ytInit(){
// create the global player from the specific iframe (#video)
player = new YT.Player('test', {
events: {
// call this function when player is ready to use
'onReady': onPlayerReady,
'onStateChange': onPlayerStatusChange
}
});
}
// this function gets called when API is ready to use
function onYouTubeIframeAPIReady() {
ytInit();
}
jQuery(document).ready( function($){
jQuery('ul.carousel-list li').click( function(){
ytInit();
jQuery('ul.carousel-list li.active').removeClass('active');
});
});
function onPlayerStatusChange(event) {
/* video status
----------------
-1 (unstarted)
0 (ended)
1 (playing)
2 (paused)
3 (buffering)
5 (video cued)
*/
console.log( event );
if( event.data == 0 ){
var thisLI = jQuery('ul.carousel-list li.active'); //get active slider item
var thisLINext = jQuery('ul.carousel-list li.active').next(); //get next slider item
var nextVideoCode = thisLINext.attr('video_code'); //get next video code
var nextVideoTitle = thisLINext.attr('video_title'); //get next video code
var nextVideoUrl = thisLINext.attr('video_url'); //get next video code
thisLINext.addClass('active'); //activate next slider item
thisLI.removeClass('active'); //deactivate current cslider item
//load next video
player.loadVideoById(nextVideoCode);
//update video title in header
jQuery('.entry-header h1.entry-title a').attr('href', decodeURIComponent(nextVideoUrl)).text(decodeURIComponent(nextVideoTitle));
}
}
function onPlayerReady(event) {
// bind events
var playButton = document.getElementById("play-button");
playButton.addEventListener("click", function() {
player.playVideo();
});
var pauseButton = document.getElementById("pause-button");
pauseButton.addEventListener("click", function() {
player.pauseVideo();
});
}
// Inject YouTube API script
var tag = document.createElement('script');
tag.src = "//www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
</script>
Do you have any helpful suggestions? I would really appreciate.
Calling ytInit() when you click the <li> creates a new player object. I think that's not quite what you want.
Have you tried:
jQuery('ul.carousel-list li').click( function(){
// ytInit();
// just load the video from the "video_code" attr
player.loadVideoById(this.getAttribute('video_code'));
jQuery('ul.carousel-list li.active').removeClass('active');
});
If that doesn't autoplay, then you may have to wait for the YT.PlayerState.CUED (i.e. 5) event in your player status change event listener to start the player.

Cannot update label on Google Apps Script GUI Builder Interface at runtime

I have an interface that calls a script for spreadsheet creation using data taken from other spreadsheet. I want the interface to update its labels at runtime in order to give visual feedback to the user and let him know the script is running and it's not stuck. When I try to update the label I put in the interface, it doesn't update the first time, but updates correctly after myFunction() reaches its end. Which means I can see the message "Creation Completed", but the message "Creating file..." is never shown. Also, the button buttonCompile is never disabled so it seems that the instructions before myFunction() are not executed at all. How can I get the labels updated and the button disabled before myFunction() starts executing? (I already double-checked variable references)
function doGet() {
var app = UiApp.createApplication();
app.add(app.loadComponent("File creation"));
var buttonCreate = app.getElementById('createBtn');
var handlerCrea = app.createServerHandler('createClickHandler');
buttonCreate.addClickHandler(handlerCreate);
return app;
}
function createClickHandler(e) {
var app = UiApp.getActiveApplication();
var label = app.getElementById('createLbl');
label.setText("Creating file...");
var buttonCompile = app.getElementById('compileBtn');
buttonCompile.setEnabled(false);
myFunction();
label.setText("Creation completed.");
buttonCompile.setEnabled(true);
app.close();
return app;
}
The cause of this behavior is that the GUI is updated only after leaving a handler. A workaround is to use two handlers. The 1st one sets the label text to Creating file... and disables the button, the 2nd one executes the myFunction function, changes the text to Creation completed, and eanbles the button. Here is an example. It disables/enables the button and the worker handler simply waits 5 seconds.
function doGet(e) {
var app = UiApp.createApplication();
var container = app.createHorizontalPanel().setId('container');
var btnPerformance = app.createButton("Performance Demo").setId('btnPerformance');
var handlerPerformance = app.createServerHandler('onBtnPerformanceClick');
var handlerWait = app.createServerHandler('onWait');
btnPerformance.addClickHandler(handlerPerformance);
btnPerformance.addClickHandler(handlerWait);
container.add(btnPerformance);
app.add(container);
return app;
}
function enableControls(enable) {
var lstControls = [ 'btnPerformance' ];
var app = UiApp.getActiveApplication();
for (var i = 0; i < lstControls.length; i++) {
var ctl = app.getElementById(lstControls[i]);
ctl.setEnabled(enable);
}
}
function onWait(e) {
enableControls(false);
return UiApp.getActiveApplication();
}
function onBtnPerformanceClick(e) {
Utilities.sleep(5000);
enableControls(true);
return UiApp.getActiveApplication();
}

Store & retrieve the identifiers of a multipliable widget's instances

The aim is to remove only the last row at any time and only by the last remove button.
There is a user interface which building up as a multiplication of the same row. The number of rows are controlled by 'Add' & 'Remove' buttons which are also elements of the row. The problem is that the hidden widgets - that are applied for each row to distinguish the instances by storing their row numbers - are storing the very same number which is the last one. Except the first (0) hidden widget which stores the proper number (0). Where am I missing the point? How should this be resolved?
As per the remove buttons have two different purposes (not detailed here), we use a cacheService to distinguish the last row from all the others. Only the last row should be removed at any time.
var cache = CacheService.getPrivateCache();
we clear the cache and create the first instance
function doGet() {
var app = UiApp.createApplication();
app.add(app.createVerticalPanel().setId('mainContainer'));
cache.removeAll([]);
ui(0);
cache.put('numberOfInstances',0);
return app; }
each instance is held by a horizontal panel which contains the mentioned hidden widget, a label which informs about the instance number, and the Add & Remove buttons.
function ui(instance) {
var app = UiApp.getActiveApplication();
var eventContainer = app.createHorizontalPanel()
.setId('eventContainer' + instance);
var instanceContainer = app.createHidden('instanceContainer',instance);
var showInstance = app.createLabel(instance)
.setId('showInstance' + instance);
var addButton = app.createButton('Add')
.setId('add' + instance)
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(instanceContainer));
var removeButton = app.createButton('X')
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(instanceContainer));
app.getElementById('mainContainer')
.add(eventContainer
.add(instanceContainer)
.add(showInstance)
.add(addButton)
.add(removeButton));
return app; }
and the event handling...
function add(inst) {
var app = UiApp.getActiveApplication();
var instance = Number(inst.parameter.instanceContainer);
ui(instance+1);
cache.put('numberOfInstances',instance+1);
return app; }
function remove(inst) {
var app = UiApp.getActiveApplication();
var instance = Number(inst.parameter.instanceContainer);
var numberOfInstances = cache.get('numberOfInstances')
if( (instance != 0) && (instance = numberOfInstances) ) {
app.getElementById('mainContainer').remove(app.getElementById('eventContainer' + instance));
cache.put('numberOfInstances',instance-1);
app.getElementById('add' + (instance-1)).setEnabled(true); } //avoiding multiple click during server response
return app; }
The aim is to remove only the last row at any time and only by the last remove button.
Many Thanks.
Why don't you simply use a clientHandler just as you did on the 'add' button? You could target the preceding 'remove' button and disable it each time you create a new one and change /update each time you remove one row.
EDIT : I can suggest you something, feel free to have a look, I changed a bit the approach but it is working and I hope you'll find it at least interesting ;-)
Link to the online test
function doGet() {
var app = UiApp.createApplication();
var counter = app.createHidden().setName('counter').setId('counter').setValue('1');
var mainContainer = app.createVerticalPanel().setId('mainContainer')
app.add(mainContainer.add(counter));
var event1Container = app.createHorizontalPanel()
var showInstance = app.createLabel('1')
var addButton = app.createButton('Add')
.setId('add1')
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(mainContainer));
var removeButton = app.createButton('X')
.setId('remove1')
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(mainContainer));
mainContainer.add(event1Container
.add(showInstance)
.add(addButton)
.add(removeButton));
return app; }
function add(inst) {
var app = UiApp.getActiveApplication();
var hiddenVal =inst.parameter.counter;
var counterVal = Number(hiddenVal);
var mainContainer = app.getElementById('mainContainer')
var counter = app.getElementById('counter')
++ counterVal
counter.setValue(counterVal.toString())
var eventContainer = app.createHorizontalPanel().setId('eventContainer'+counterVal)
var showInstance = app.createLabel(counterVal.toString())
var addButton = app.createButton('Add')
.setId('add'+counterVal)
.addClickHandler(app.createClientHandler()
.forEventSource().setEnabled(false)) //avoiding multiple click during server response
.addClickHandler(app.createServerHandler('add')
.addCallbackElement(mainContainer));
var removeButton = app.createButton('X')
.setId('remove'+counterVal)
.addClickHandler(app.createServerHandler('remove')
.addCallbackElement(mainContainer));
app.add(eventContainer
.add(showInstance)
.add(addButton)
.add(removeButton));
if(counterVal>1){app.getElementById('remove'+(counterVal-1)).setEnabled(false)}
return app; }
function remove(inst) {
var app = UiApp.getActiveApplication();
var counterVal = Number(inst.parameter.counter);
var counter = app.getElementById('counter')
if(counterVal ==1) {return app}
var maincontainer = app.getElementById('mainContainer')
app.getElementById('eventContainer' + counterVal).setVisible(false)
--counterVal
counter.setValue(counterVal.toString())
app.getElementById('add'+counterVal).setEnabled(true)
app.getElementById('remove'+counterVal).setEnabled(true)
return app;
}
NOTE : I didn't make use of .remove(widget) since this is a fairly new method and I don't know exactly how it works... I'll test it later. Until then I used setVisible(false) instead, sorry about that :-)
Note 2 : I didn't use the cache since the hidden widget is sufficient to keep track of what is going on... if you needed it for something else then you could always add it back .

Resources