Avoiding lag caused by hoisting - javascript

I want to log 'tapped' and execute the HUD asap, but oauth_upload_photo is causing it to lag (apparently because of hoisting). How can I snap the HUD instantly??
var submit_post = function submit_post(){
console.log('tapped');
// Show HUD
plugins.navigationBar.hideRightButton();
var hud = document.getElementById("hud");
hud.style.display = 'block';
// Get the image
var image = document.getElementById('myImage');
var imageURI = image.src;
// Get the caption from the textarea
var cap = document.getElementById('tar');
var caption = cap.value;
// Call upload photo
oauth_upload_photo(imageURI,caption);
};

Your issue (which needs a lot more explanation before we could understand what you're actually asking about) has nothing to do with javascript variable hoisting. All hoisting does is cause variables to be defined at the top of the function,regardless of where their initial declaration is located in the function. It doesn't change the execution order of any statements.
Also, in some browsers console.log() is not guaranteed to be completely synchronous and the display of the data in the log window is not necessarily immediate either. There is sometimes a delay before it actually logs. I don't know if this is caused by marshalling data across process boundaries, general repaint logic or some other internal implementation issue.
You may also want to change this:
var submit_post = function submit_post(){
to this:
var submit_post = function (){
or even this:
function submit_post() {
so you aren't double defining the same symbol.

Related

How can audio started in other functions be stopped?

I'm trying to create a DTMF keypad emulator using only HTML, CSS, and JavaScript,
I'm having a small problem with sound. When an image is clicked, that runs the offHook() function which starts playing dial tone. However, I want that dial tone to stop as soon as the first number is clicked (or pressed once I had a keyboard listener). For each number function i.e. dial1(), dial2(), etc. which is tied up to the individual buttons that are clicked, I run the numberDial() function, which is supposed to stop the dial tone audio. However, it refuses to stop.
I have tried:
-dialTone.pause();
-dialTone.stop();
-dialTone.src = "";
Regardless, the dial tone continues. How can I get it to stop when numberDial() is run? This function is run every time a number is pressed (I'll add other stuff later) so it will only be stopping the audio the first number, but it shouldn't do any harm the other times either.
My understanding is that the function numberDial() should recognize the variable dialTone - so why is dialTone refusing to pause?
function offHook() {
document.getElementById("WE2500").style.display = "none";
document.getElementById("dialPad").style.display = "block";
var dialTone = new Audio('dialTone.m4a');
dialTone.play();
}
var number = "";
function numberDial() {
dialTone.src = "";
}
function dial1() {
numberDial();
number = number + "1";
var tone1 = new Audio('DTMF-1.wav');
tone1.play();
}
The problem here is "my understanding is that the function numberDial() should recognize the variable dialTone". Your understanding is very wrong indeed: variables declared with an allocator keyword (var,let, or `const) inside functions do not persist outside those functions. This is pretty much how every conventional programming language works, so the fact that you thought it would persist is... curious? It might be worth reading up on how Javascript works.
The only time this is not true is if you don't use an allocator at all to declare your variables, but this is an extreme bad practice: always use an allocator keyword and make sure you're properly scoping them.
So, let's do that: the var dialTone that you define in your offHook() function is only accessible inside the offHook function right. If you need access to it at the global level, it'll have to be declared it at the global level, even if you don't then initialize it until you call your offHook function:
var dialTone;
function offHook() {
...
dialTone = new Audio('dialTone.m4a'); // note: NO 'var'. It already exists.
dialTone.play();
}
function numberDial() {
// we now need to make sure we only stop dialTone if
// the dialTone variable actually points to something.
if (dialTone) {
// see http://stackoverflow.com/questions/14834520 on why this "stops" audio.
dialTone.pause();
dialTone.currentTime = 0;
}
}

Can I put a CollaborativeString inside a custom type?

I'm reading the Google Drive Realtime API documentation on Building a Collaborative Data Model.
I really like the way gapi.drive.realtime.databinding.bindString behaves. It doesn't mess up your cursor placement when multiple people are typing in the same text box. But it requires that you pass it a CollaborativeString.
But if you register a custom type, you have to use gapi.drive.realtime.custom.collaborativeField no matter what type of field you are defining, and you can't pass one of these to bindString. In fact, the collaborativeField type does not appear to be documented anywhere, and inspecting it in the console shows that it has no methods. That means there's no registerReference method, which CollaborativeString uses to keep track of cursor positions.
How frustrating. So I guess I have to work around it. I see a few options:
Ignore the fact that the cursor gets messed up during collaboration
Use a CollaborativeMap instead of a custom type, and wrap it with my custom type at runtime
Probably going to do option 2.
I think you misunderstand how this site works, the onus is not on other people to show you how to do something - you're asking other people to take time from their day and help you.
That being said, taking a quick look at the page that you linked shows that what you want to do is not only possible but quite straightforward and compatible with bindString. Stealing from the example code from that page:
// Call this function before calling gapi.drive.realtime.load
function registerCustomTypes()
{
var Book = function () { };
function initializeBook()
{
var model = gapi.drive.realtime.custom.getModel(this);
this.reviews = model.createList();
this.content = model.createString();
}
gapi.drive.realtime.custom.registerType(Book, 'Book');
Book.prototype.title = gapi.drive.realtime.custom.collaborativeField('title');
Book.prototype.author = gapi.drive.realtime.custom.collaborativeField('author');
Book.prototype.isbn = gapi.drive.realtime.custom.collaborativeField('isbn');
Book.prototype.isCheckedOut = gapi.drive.realtime.custom.collaborativeField('isCheckedOut');
Book.prototype.reviews = gapi.drive.realtime.custom.collaborativeField('reviews');
Book.prototype.content = gapi.drive.realtime.custom.collaborativeField('content');
gapi.drive.realtime.custom.setInitializer(Book, initializeBook);
}
and
// Pass this as the 2nd param to your gapi.drive.realtime.load call
function onDocLoaded(doc)
{
var docModel = doc.getModel();
var docRoot = docModel.getRoot();
setTimeout(function ()
{
var book = docModel.create('Book');
book.title = 'Moby Dick';
book.author = 'Melville, Herman';
book.isbn = '978-1470178192';
book.isCheckedOut = false;
book.content.setText("Call me Ishmael. Some years ago - never mind how long precisely - having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world.");
docRoot.set('tbook', book);
debugger;
}, 0);
}
Good luck and have fun with the Realtime API - it's a lot of fun to play with.
I know this question and answer are getting old, but for reference's sake, just the last part of Grant Watters' very good answer, the onDocLoaded routine, is rather misleading. That function as written, is more suited for the 3rd parameter to the gapi.drive.realtime.load call, the onInitializeModel callback.
The 2nd parameter is called every time the Doc is loaded. You wouldn't normally add the same object over and over as the above routine would... Instead, you would normally set up your event handling, your dataBinds etc. This version might clarify somewhat:
// Pass this as the 2nd param to your gapi.drive.realtime.load call
function onDocLoaded(doc)
{
var docModel = doc.getModel();
var docRoot = docModel.getRoot();
var text = doc.getModel().getRoot().get("text");
// Add an event listener...
text.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, onStringChanged);
// ...and/or bind to collaborative objects:
var textArea = document.getElementById('textArea1')
textBinding = gapi.drive.realtime.databinding.bindString(text, textArea);
etc...
}
Not incidentally, bindString returns the binding object, which is needed to "unbind" later, preventing an AlreadyBound error or other unexpected behavior when the next Doc is loaded. Do something like this:
function onDocLoaded(doc)
{
// Clear any previous bindings etc:
if (textBinding) { textBinding.unbind() };
textBinding = null;
etc...

Odd bug with drawImage on HTML5 canvas

JsFiddle (note: It doesn't show anything, it's merely a way for me to show my code in a neater format) http://jsfiddle.net/h6tVR/
I am new to HTML5 canvas and have decided to play about and see what I can do with it. So far I've been able to draw a locally hosted image onto the canvas and even do a bit of basic tiling:
window.onload = function(){
var GameClosure = function() {
var canv = document.getElementById("canv");
var canvContext = canv.getContext("2d");
var sprite = new Image();
sprite.src = "sprite.png"
var tile = new Image();
tile.src = "tile.png"
function loadSprite(){
sprite.onload = function(){
canvContext.drawImage(sprite, 50, 50);
};
}
function loadTiles(){
tile.onload = function(){
for(var i = 0; i < 800; i += 16){
for(var r = 0; r < 608; r += 16){
canvContext.drawImage(tile, i, r);
}
}
};
}
return{
loadTiles: loadTiles,
loadSprite: loadSprite
};
}();
GameClosure.loadTiles();
GameClosure.loadSprite();
}
I am getting an odd problem with this. When I load it up, the majority of the time, only the tiles will load up. I've tried a couple of things so far, I've switched the GameClosure.loadTiles() and GameClosure.loadSprite(); calls to see if the load order made any difference. It doesn't. I even tried creating a second context and assigning the tiles to one and the sprite to another, but this made no difference. Commenting out the tile call produces the sprite correctly.
It gets even odder. I was refreshing the page rapidly and I noticed that occasionally (with no pattern to it, sometime it could happen 3 times in a row, other time once in 20) the tiles would load AND the sprite would load on top as I would expect it to.
Can this be fixed? My only guess is that my code is running somewhat asyncronously and the for loops creating the tiles are completing after the sprite has been loaded, but looking at my code I don't see where this could be happening.
Separate the concerns. Wait for all resources to be loaded (and the document), then launch your game. Always be sure to hook event handler before assigning src to avoid 'random' (cache-related, in fact) behaviors.
When you set an onload handler, your javascript will continue while the resource loads in the background. The handler will be executed when the resource has loaded. You have no way to tell when that will happen and in what order.
When you have multiple resources and want to call your draw-function the moment the last one has loaded, you could have a global preloader-object. Each onload-handler should call a function on the preloader to inform it that the resource has loaded. That function should check if all resources have reported in, and when that's the case execute the draw-function.
Also, when you set an onload-handler and the resource is already loaded. When you set .src and the resource is in the browsers cache, it will get loaded instantly. So you always need to first set .onload and then set .src.

innerHTML can't be trusted: Does not always execute synchronously

To see the problem in action, see this jsbin. Clicking on the button triggers the buttonHandler(), which looks like this:
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking";
longPrimeCalc();
}
You would expect that this code changes the text of the div to "thinking", and then runs longPrimeCalc(), an arithmetic function that takes a few seconds to complete. However, this is not what happens. Instead, "longPrimeCalc" completes first, and then the text is updated to "thinking" after it's done running, as if the order of the two lines of code were reversed.
It appears that the browser does not run "innerHTML" code synchronously, but instead creates a new thread for it that executes at its own leisure.
My questions:
What is happening under the hood that is leading to this behavior?
How can I get the browser to behave the way I would expect, that is, force it to update the "innerHTML" before it executes "longPrimeCalc()"?
I tested this in the latest version of chrome.
Your surmise is incorrect. The .innerHTML update does complete synchronously (and the browser most definitely does not create a new thread). The browser simply does not bother to update the window until your code is finished. If you were to interrogate the DOM in some way that required the view to be updated, then the browser would have no choice.
For example, right after you set the innerHTML, add this line:
var sz = elm.clientHeight; // whoops that's not it; hold on ...
edit — I might figure out a way to trick the browser, or it might be impossible; it's certainly true that launching your long computation in a separate event loop will make it work:
setTimeout(longPrimeCalc, 10); // not 0, at least not with Firefox!
A good lesson here is that browsers try hard not to do pointless re-flows of the page layout. If your code had gone off on a prime number vacation and then come back and updated the innerHTML again, the browser would have saved some pointless work. Even if it's not painting an updated layout, browsers still have to figure out what's happened to the DOM in order to provide consistent answers when things like element sizes and positions are interrogated.
I think the way it works is that the currently running code completes first, then all the page updates are done. In this case, calling longPrimeCalc causes more code to be executed, and only when it is done does the page update change.
To fix this you have to have the currently running code terminate, then start the calculation in another context. You can do that with setTimeout. I'm not sure if there's any other way besides that.
Here is a jsfiddle showing the behavior. You don't have to pass a callback to longPrimeCalc, you just have to create another function which does what you want with the return value. Essentially you want to defer the calculation to another "thread" of execution. Writing the code this way makes it obvious what you're doing (Updated again to make it potentially nicer):
function defer(f, callback) {
var proc = function() {
result = f();
if (callback) {
callback(result);
}
}
setTimeout(proc, 50);
}
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking...";
defer(longPrimeCalc, function (isPrime) {
if (isPrime) {
elm.innerHTML = "It was a prime!";
}
else {
elm.innerHTML = "It was not a prime =(";
}
});
}

Firefox/Gecko unable to animate transform="rotate(…)" in SVG?

In the page I'm working on, when the user clicks on an object, one SVG group rotates out of the way while another rotates in.
The code as it is works just fine in WebKit, but it isn't working at all in Gecko. Here is the block of code that is not being executed by Gecko:
var totStep = dur*2/msrate, step=0;
window.timer = window.setInterval(function(){
if(step<totStep/2){
var inangle = -50*easeIn(step,totStep/2);
iris.setAttribute("transform","rotate("+inangle+" 23 -82)");}else{
var prog = easeOut2((step-(totStep/2)),totStep/2);
var outangle = 50*prog;
var down = 400*prog;
vinyl.setAttribute("transform","rotate("+(-50+outangle)+" 986 882)");
needle1.setAttribute("transform","translate(0 "+(-400+down)+")");
buttons.setAttribute("transform","translate(0 "+(-400+down)+")");}
step++;
if (step > totStep) {window.clearInterval(window.timer); return}
});
Most of this code is adapted from a function which opens the eye when the page is loaded, and that function works fine in Gecko, which is why this is enigmatic to me.
You can see the page with all of its source code at this page. The problematic function is written in the linked eye.js. The problem occurs when the user clicks on "DJ Docroot" under the "Music" section of the menu, which is accessed by clicking anywhere.
You're missing a second argument to setInterval to specify the interval at which the function should be called. So, for example, the following code works:
window.timer = window.setInterval(function(){
if(step<totStep/2){
var inangle = -50*easeIn(step,totStep/2);
iris.setAttribute("transform","rotate("+inangle+" 23 -82)");}else{
var prog = easeOut2((step-(totStep/2)),totStep/2);
var outangle = 50*prog;
var down = 400*prog;
vinyl.setAttribute("transform","rotate("+(-50+outangle)+" 986 882)");
needle1.setAttribute("transform","translate(0 "+(-400+down)+")");
buttons.setAttribute("transform","translate(0 "+(-400+down)+")");}
step++;
if (step > totStep) {window.clearInterval(window.timer); return}
},10);
Webkit probably just assumes a default value.
Also, just a suggestion, in the future, it might be easier to spot errors like these if you adopt code conventions that will make your code more legible: http://javascript.crockford.com/code.html
A tool like jslint will help with this.

Categories

Resources