Cesuim handle timebar click - javascript

How to handle Cesuim timebar click?
It would be very comfortable to get clicked time in callback.
Im nearly sure that it is implemented, so im looking for something like this.
function handleClick(event) {
console.log(event.CLICKED_DATETIME);
}
viewer.timebar.ON_TIMEBAR_CLICK = handleClick;
Thanks.

Yes, that's pretty simple. Here's what Viewer does currently:
viewer.timeline.addEventListener('settime', onTimelineScrubfunction, false);
But, some caveats:
This event will fire quite rapidly if the user "scrubs" (drags the current time bar across the timeline). Be prepared for a lot of events at once, like mousemove or touch/pointer move. Don't try to animate the globe during the event, just set some variable to take effect on the next animation frame like Viewer does.
This API is technically "private", which just means it is subject to change in any release without Cesium's normal deprecation policy. Even so, it's the only way to get this event, so go ahead and use it, just be careful to retest it particularly after reading about any timeline changes in the release notes (this is uncommon, as the timeline has been static for a long time now, but someday a full replacement could happen).

not too elegant but also an option
viewer.timeline.container.onmouseup = (e) => {
console.log(viewer.clock.currentTime);
}

Related

Infinite loop when overriding graphHandlerMouseUp on MxGraph using Angular

I have a difficult question to explain and I'm way out of my comfort zone as far as expertise in Javascript, TrueType, Angular and MxGraph are concerned... Hope to be able to explain.
I have an Angular component displaying and MxGraph. I was able to integrate MxGraph with Angular following this link (How to integrate mxGraph with Angular 4?). Even if I use Angular 7, the solution still works...
The graph is displayed correctly on the page and everything works fine, including my override of the function graphHandlerMouseUp, which I do with this code:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
mx.graphHandlerMouseUp.apply(this, arguments);
}
When I run this page for the first time, no problem happens.
Then through a button I call a page with another component (through routing). If from this page I go back to the first component (again through a routerlink) the page and the component with the MxGraph loads correctly, BUT when I use this function (i.e., release the mouse button).
It seems to me a recoursive problem, as when I put a console output like this:
// Save the position of the mouse when releasing the button: used for
// detecting the target in a drag and drop
mx.graphHandlerMouseUp = mx.mxGraphHandler.prototype.mouseUp;
mx.mxGraphHandler.prototype.mouseUp = function( graph, evt ) {
currentdropX = evt.graphX;
currentdropY = evt.graphY;
// INIFINTE LOOP HERE
console.log("Test");
mx.graphHandlerMouseUp.apply(this, arguments);
}
The "Test" is written a number of times which is continuously growing. Yet, if I understood, this was the correct way of overriding the function. Of course on the first load of the page, "Test" is displayed once. Passing to another component and then back on this it is displayed an "infinite" number of times (until I reach the: "ERROR RangeError: Maximum call stack size exceeded")...
I tried also to remove that override, and besides the obvious lack of the functionality, the very same problem happened to the function "mxDragSource", which is overridden with the same approach.
As I said: I'm not expert enough in javascript, truetype, MxGraph or Angular, so any hint, even if obvious, is very welcome!
Thanks!
The first time you run your code, you store mx library's mouseUp event in a variable and you assign a new function to mouseUp event in which you call the old mouseUpEvent.
The second time you run your code, you store your current mouseUp event (that you have modified) and you assign a function in which you call the old mouseUpEvent, which is the same you stored previously. There goes your recursion !
What you need to do is override the third-party function properly, so you don't execute your code twice.
How to do it ?
You can create a mixin and use this mixin in your componentB/ If you have no idea what is it and how to do it, please reproduce your problem in a stackblitz I'll be glad to help you to implement it.

Linking into a page with a hash fragment (so user lands at specific content on page) issues

The Feature I Want:
I want to give a user a link like mysite.com/foo#bar so when they hit this link they land on the page foo and are scrolled half way down the page to the content with the id bar. It should be noted that this link will always be clicked from off site or typed into the address bar manually, the user will not already be on the page with all assets loaded.
Also fyi I am using angular and the page in question each bit of content is in an element directive, and in each directive template there are images
The Issue:
Easy enough right? Well I'm running into some problems, most of the time it works, but maybe 40% of the time it doesn't and the user lands above the content, I believe this is because the browser scrolls to the correct point on the page, but then slightly afterward images are loaded in above it pushing the rest of the page down, leaving the user in a random unintuitive spot. (For some reason the failure rate seems to be worse on iPhones...)
What I've Tried So Far:
In a run function I look for a hash fragment on any route and scroll to it if it exists.
if($location.hash()) {
$anchorScroll();
}
I've tried:
Wrapping it in a timeout
This works sometimes but is obviously not consistent, sure I can set it to 500ms and on a great wifi connection it's fine, but not on a mobile with poor signal
listening for $viewContentLoaded
Too fast, ui-router seems to fire this event way before the page is rendered
Emitting an event in each post-link function for all directives on the page.
link: function ($scope) {
$scope.$emit('loadingFinished');
Then picking that up again in the run function
$scope.$on('loadingFinished', function () {
$timeout(doAnchorScroll, 500);
}
This raised the success rate but still wasn't foolproof. And I witnessed it leaving the user stranded in weird spots mainly on iPhones.
Can anyone suggest a way of detecting a moment where it is safe to scroll, or perhaps some other way of ensuring landing in the correct spot?
Two things - first is a user-scroll should prevent it - you don't want to be scrolling the view if they've already scrolled. Second is listening for the images to finish loading.
Listening for the images is quite easy:
$("img").one("load", $anchorScroll);
Then in $anchorScroll I'd suggest checking if all images have loaded, and returning immediately if not (bonus points if you only check images above the anchor - but only doing a quick reply):
var scrolled = false;
function $anchorScroll() {
var allLoaded = true;
$("img").each(function() {
if (!this.loaded) {
return allLoaded = false;
}
});
if (scrolled || !allLoaded) {
return;
}
...
The scrolling is possibly slightly harder - you can check for scroll events, but they are slightly different between platforms - and might even get fired for the manual scroll - if you find that happens then simply have a global that says "I'm scrolling here" and ignore it, otherwise:
$(document).one("scroll", function() { // Use ".on" if this gets fired for code...
scrolled = true;
});
Note that you probably don't need to care about failed image loads, or ones that have already loaded, since your initial call to $anchorScroll() will catch them.

Audio duration NaN on certain page request action

I have been trying to create my custom media player using HTML5 and Jquery.
I have followed different approaches and ran into some trouble based on my way of refreshing the page.
First Case
$(document).ready(function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
In this case, the duration returns NaN when I redirect the page to the same URL by pressing the ENTER key in the address bar. However, it works completely fine when I refresh using the reload button or by pressing the F5 button.
Second Case
I read in some answers that loading duration after the loadedmetadataevent might help. So I tried the following:
$(document).ready(function(){
$('audio').on('loadedmetadata', function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
});
Surprisingly, in this case, the inverse of the first case happened. The duration gets displayed completely fine in the case of a redirect, i.e., pressing ENTER while in the address bar. However, in the case of refreshing using the F5 button or the reload button, the duration doesn't get displayed at all, not even NaN which led me to believe that the code doesn't get executed at all.
Further reading suggested this might be a bug within the webkit browsers but I couldn't find anything conclusive or helpful.
What could be the cause behind this peculiar behavior?
It'd be great if you could explain it along with the solution to this problem.
Edit:
I am mainly looking for an explanation behind this difference in behavior. I would like to understand the mechanism behind rendering a page in the case of redirect and refresh.
It sounds like the problem is that the event handler is set too late, i.e. the audio file has loaded its metadata before the document is even ready.
Try setting the event handler as soon as possible by removing the $(document).ready call:
$('audio').on('loadedmetadata', function(){
duration = Math.ceil($('audio')[0].duration);
$('#duration').html(duration);
});
Note that this requires that the <script> tag be after the <audio> tag in the document.
Alternatively, you can tweak your logic slightly, so that the code that updates the duration always runs (but fails gracefully if it gets a NaN):
function updateDuration() {
var duration = Math.ceil($('audio')[0].duration);
if (duration)
$('#duration').html(duration);
}
$(document).ready(function(){
$('audio').on('loadedmetadata', updateDuration);
updateDuration();
});
Lovely code examples and stuff from people - but the explanation is actually very simple.
If the file is already in the cache then the loadedmetadata event will not fire (nor will a number of other events - basically because they've already fired by the time you attach your listeners) and the duration will be set. If it's not in the cache then the duration will be NaN, and the event will fire.
The solution is sort of simple.
function runWhenLoaded() { /* read duration etc, this = audio element */ }
if (!audio.readyState) { // or $audio[0].readyState
audio.addEventListener("loadedmetadata", runWhenLoaded);
// or $audio.on("loadedmetadata", runWhenLoaded);
} else {
runWhenLoaded.call(audio);
// or runWhenLoaded.call($audio[0]);
}
I've included the jQuery alternatives in the code comments.
According to w3 spec this is standard behavior when duration returns NaN.
So I suggest use durationchange event:
$('audio').on('durationchange', function(){
var duration = $('audio')[0].duration;
if(!isNaN(duration)) {
$('#duration').html(Math.ceil(duration));
}
});
NOTE: This code (and your too) will not work correct in case if you have more than one audio element on page. Reason is that you listen events from all audio elements on page and each element will fire own event:
$('audio').on('durationchange', function(){...});
OR
You can try:
<script>
function durationchange() {
var duration = $('audio')[0].duration;
if(!isNaN(duration)) {
$('#duration').html(Math.ceil(duration));
}
}
</script>
<audio ondurationchange="durationchange()">
<source src="test.mp3" type="audio/mpeg">
</audio>
Note that behaviors will differ from one browser to another. On Chrome, you have different type of loading. When resources are not in cache, it will fetch either the complete file (for js or css for example), either a part of the file (mp3 for example). This partial file contains metadata that allows browser to determine duration and other data such as the time it'll take to download whole file at this rate, trigerring for example canplay or canplaythrough events. If you look at network usage in you dev console, you'll see that the HTTP status code will be either 200 (succesful load) or 206(partial load - for mp3 for example).
When you hit refresh, elements are checked to see if they changed. HTTP status will then be 304, meaning file hasn't been modified. If it hasn't changed and is still in browser cache, then it won't be downloaded. The call to determine if it has or not changed comes from the server providing the file.
When ou simply click enter in adress bar, it's automatically taken from cache, not validating online. So it's much faster.
So depending on how you call or refresh your page (either simmple enter, refresh or complete refresh without cache), you'll have big differences on the moment you get the metadata from your mp3. Between taking the metadata from cache directly vs making a request to a server, the difference can be a few hundreds milliseconds, which is enough to change what data is available at different moment.
That being said, listening to loadedmetada should give consistent result. This event is triggered when the data with duration information is loaded, so whatever way the page is loaded, it shouldn't matter if that called is properly made. At this point you have to consider maybe some interference from other elements. What you should do is follow your audio through various events to get exactly where its at at different moments. So in you document ready you could add listeners for different event and see where the problem occurs. Like this:
$('audio')[0].addEventListener('loadstart', check_event)
$('audio')[0].addEventListener('loadeddata', check_event)
$('audio')[0].addEventListener('loadedmetadata', check_event)//at this point you should be able to call duration
$('audio')[0].addEventListener('canplay', check_event) //and so on
function check_event(e) {
console.log(e.target, e.type)
}
You'll see that depending on the way you refresh, these events can come at different moments, maybe explaining inconsistencies in your outputs.

Detect if user will leave site like Bounce Exchange

Bounce Exchange has figured out an almost perfect way of detecting if a user will leave the website. They do this based on tracking mouse gestures, mouse velocity, and breaking of the browser plane. If they detect someone is leaving they fire off a popup on a lightbox.
I can poorly emulate this, by the following:
$("body").mouseleave(function() {
jQuery('#avoid-bounce').show();
});
The only problem is this is rather annoying. Even if it captures someone, the moment they leave the body it fires again.
How probable would it be to factor in mouse speed and allow the event to fire only once? I'm still fairly new to JavaScript and jQuery, but I'm learning.
This is exactly what .one() is for:
$("body").one('mouseleave', function() {
jQuery('#avoid-bounce').show();
});
You can add a flag to your code:
$("body").mouseleave(function() {
if ( jQuery('#avoid-bounce').data('shown') != true ) {
jQuery('#avoid-bounce').data('shown', true).show();
}
});
Creating a flag will make sure the show() code will not be called the second time.
Or you can try OuiBounce,the bounce exchange alternative: https://github.com/carlsednaoui/bounce-exchange-alternative

Flash Microphone Event Resize

I have been recently studying and learning Flash AC3 and my intention was to make a small voice recorder for my website. I have been using google and the search engines and get different answers here and there but still it is not exactly working properly.
The problem I am having is, the flash plugin is 215x50 pixels. I know that unless it is 215x138 pixels, the flash player security panel will automatically NOT open.
I devised a work around which is that if and when the security is being called to open, I would resize the DIV the flash object is in using a javascript function called ResizeFlash to a size of 215x138 and then back again to 215x50 after the user makes a choice whether or not they allow the microphone.
Now I have been scratching my head for a few days because I DO get the following code to work and it does resize the DIV, but it does not resize the DIV back. I think I might have the call to ResizeFlash in the wrong place (???). I am not familiar enough to know where it might be wrong.
I keep rearranging the code to see if that would work and I would get times where it does resize to 215x138, open the Security Panel, then resize back to 215x50 but then the recording would not begin, as if I were stuck somewhere in a loop.
I hope that someone can please take some time and just take a glance at this code and show me the right way to handle this. Thank you very much!
Here is the code:
public function Main():void
{
recButton.stop();
submitButton.enabled = false; // These reset everything, maybe in wrong place??
activity.stop();
addListeners();
mic = Microphone.getMicrophone();
if (mic == null)
{
// no camera is installed
}
else if (mic.muted)
{
// user has disabled the access in security settings
mic.addEventListener(StatusEvent.STATUS, onMicStatus, false, 0, true); // listen out for their new decision
Security.showSettings('2'); // show security settings window to allow them to change security settings
}
else
{
// you have access
mic.setUseEchoSuppression(true); //... also this might be in wrong place?
// .. I would like this to always be on
}
}
private function addListeners():void
{
recButton.addEventListener(MouseEvent.MOUSE_UP, startRecording);
submitButton.addEventListener(MouseEvent.MOUSE_UP, onSend);
recorder.addEventListener(RecordingEvent.RECORDING, recording);
recorder.addEventListener(Event.COMPLETE, recordComplete);
activity.addEventListener(Event.ENTER_FRAME, updateMeter);
}
function onMicStatus(event:StatusEvent):void
{
if (event.code == "Microphone.Unmuted")
{
mic.removeEventListener(StatusEvent.STATUS, onMicStatus);
ExternalInterface.call('ResizeFlash', '215', '50'); // When the user presses allow, resize the div back to 215x50
}
}
private function startRecording(e:MouseEvent):void
{
recorder.record();
e.target.gotoAndStop(2);
recButton.removeEventListener(MouseEvent.MOUSE_UP, startRecording);
recButton.addEventListener(MouseEvent.MOUSE_UP, stopRecording);
}
private function stopRecording(e:MouseEvent):void
{
recorder.stop();
e.target.gotoAndStop(1);
recButton.removeEventListener(MouseEvent.MOUSE_UP, stopRecording);
recButton.addEventListener(MouseEvent.MOUSE_UP, startRecording);
}
I know that I have something in there in the wrong order..! I appreciate any comments.
Resizing the app back to 215x50 in the Microphone's status event handler may be too soon, as you have suggested.
Just a hunch, but that status event is dispatched immediately when the user clicks the "Allow" radio button in the Flash security panel. The panel is still open. In fact, if you leave it open and click between allow/deny it will get dispatched each time...
When the security panel is up, there are some things you cannot do. I wonder if using ExternalInterface (to resize the app) is falling into this bucket.
I would suggest the following:
Test your resize functionality without the security panel in the mix. Make sure this code successfully resizes the app in both directions.
Then have a look at this question about how to detect when the user actually closes the security panel. There are two approaches there, one is very hacky (the BitmapData.draw() hack) but I know it works. I suggest trying the second one, and commenting/upvoting there if it does work (I will too). It's a more elegant way to detect when the user closes the dialog, but I haven't had a chance to try it.
When you detect the dialog is closed, resize the app.

Categories

Resources