Safari 6.0.2 not calling onaudioprocess - html

I've earlier successfully used the JavaScriptAudioNode in the Web Audio API to synthesize and mix audio both in Chrome and Safari 6.0. However, the latest version of Safari no longer appears to work, because it does not call onaudioprocess to fill the source buffers.
This is a simplified example which plays only silence and appends text to the document body on each call to onaudioprocess:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("a").click(function() {
var context = new webkitAudioContext();
var mixerNode=context.createJavaScriptNode(2048, 0, 2);
mixerNode.onaudioprocess=function(ape) {
var buffer=ape.outputBuffer;
for(var s=0;s<buffer.length;s++)
{
buffer.getChannelData(0)[s]=0;
buffer.getChannelData(1)[s]=0;
}
$("body").append("buffering<br/>");
};
$("body").html("");
mixerNode.connect(context.destination);
return false;
});
});
</script>
</head>
<body>
start
</body>
</html>
The above example works in Chrome as expected, but not in desktop Safari. The iOS version of Safari does not work either, but it never did work for me in the first place.
Calling context.createJavaScriptNode does return a proper object of type JavaScriptAudioNode and connecting it to the destination node does not throw any exceptions. context.activeSourceCount remains at zero, but this is also the case in Chrome as it apparently only counts active nodes of type AudioBufferSourceNode. context.currentTime also increments as expected.
Am I doing something wrong here or is this an actual bug or a missing feature in Safari? The Apple documentation has no mention of JavaScriptAudioNode (nor the new name, ScriptProcessorNode) but it did work before on the first release of Safari 6. The iOS Safari requirement for user input doesn't seem to help, as the example above should take care of that.
The simple example can be found here and a more complex one is my Protracker module player which exhibits the same behaviour.

There are a couple bugs in Safari's implementation of the Web Audio API that you'll need to look out for. The first is in the createJavaScriptNode constructor... it seems to have problems with the "input channels" param being set to 0. Try changing it to this:
createJavaScriptNode(2048, 1, 2)
The second issue has to do with garbage collection (I think); once your mixerNode variable is out of scope, Safari seems to stop firing the onaudioprocess callback. One solution is to introduce mixerNode at the top-level scope (i.e. declaring var mixerNode; at the top of your script) and then store your JavaScriptNode in that top-level variable. If you plan on dynamically creating multiple mixerNodes, you can achieve the same effect by storing references to them in a top-level array variable.
If you make these two changes (input channel param set to 1, maintaining a reference to the mixerNode) then your script should work in Safari as expected.

Related

HTML5 drag-drop between different browsers and apps

Browser quirks aside, HTML5 drag and drop works between different windows of the same browser. When I run up the web app in different browsers, I still get the dragover events but the data is missing.
Here are the the dragStart and dragOver handlers. Not very interesting, they just implement what MDN tells you to do. The code is Typescript.
public dragStart(e: DragEvent): boolean {
e.dataTransfer.setData("text/json", JSON.stringify(this.Model));
return true;
}
public dragOver(e: DragEvent): boolean {
if (e.dataTransfer.items.length) {
let raw = e.dataTransfer.items[0];
if (raw && raw.type === "text/json") {
//highlight drop target
}
return false;
}
return true;
}
This code running in two Chrome windows, it works - I can drag between the windows. This code running in two Firefox windows, it works.
Chrome to Firefox, or Firefox to Chrome, I get events but no data.
Why and what can I do about this? And if it cannot be made to work, what's the point of firing drag events for drag from another app if you (Google, Mozilla etc) aren't going to surface the data?
This probably isn't the end of the world for copying from one browser app to another; a given user will more than likely use the same browser for both. But I had visions of interop with desktop applications, and this does not bode well.
When I change the "mime type" used with setData and getData from text/json (which is what the data is) to text/plain, suddenly it works between Firefox and Chrome, and I can also drag a node to a text editor, where unsurprisingly the JSON is inserted as text.
And it works with Edge now. Crikey, according to mdn you can make up your own values. They left out "but not if you want interop with another app"
I had thought to use the type to decide whether to allow drop, and text/plain does little to help me wit that. I can try a parse and if that succeeds I can check some attributes, but this is rather heavyweight, and according to Drag'n'drop dataTransfer.getData empty it won't work anyhow.

speechSynthesis.speak not working in chrome

I'm using chrome Version 55.0.2883.87 m (64-bit) on Windows 10.
The following simple html file reproduces the problem and is extracted from my more complex app. It is supposed to speak the 3 words on page load. It works on MS Edge and Firefox but does not work on chrome. This code was working for me on Chrome no problem a couple weeks back.
<html>
<head>
<script lang="javascript">
window.speechSynthesis.speak(new SpeechSynthesisUtterance("cat"));
window.speechSynthesis.speak(new SpeechSynthesisUtterance("dog"));
window.speechSynthesis.speak(new SpeechSynthesisUtterance("bark"));
</script>
</head>
<body></body>
</html>
I may never know for sure, because this problem was intermittent, but it seemed to go away after I started to cancel right before speak.
utter = new window.SpeechSynthesisUtterance("cat");
window.speechSynthesis.cancel();
window.speechSynthesis.speak(utter);
I don't think the cancel necessarily has to come between the utterance object creation and use. Just that it come before every speak. I may have had a different problem as I was only creating one utterance object, not a bunch. I did only see it on Chrome 78. Using Windows 7, 64-bit. Never saw the problem on Firefox or Edge.
EDIT 2 weeks later. No recurrences after several dozen tries. It seems .cancel() solved my problem. My symptoms were: calling speechSynthesis.speak() in Chrome would sometimes not start the speech. There were no immediate indications of a problem in the code, speechSynthesis.speaking would be true and .pending would be false. There would be no events from the utterance object. Normally, when speech would work, I'd get a 'start' event about 0.1 seconds after calling .speak().
speechSynthesis.speak() is no longer allowed without user activation in Google's Chrome web browser since 2018. It violates autoplay policy of Google Chrome. Thus Google Chrome has managed to revoke it's autoplay functionality but you can make use of it by adding a button to make a custom call.
You can visit here to check the status provided by chrome itself also below is the image attached which clearly shows that speechSynthesis.speak() call is prohibited without user's permission.
Link to image
Link to article by Google Chrome
To add to this, the issue for me was the playback rate on the instance of SpeechSynthesisUtterance was above 2. I discovered it must be set to 2 or less in chrome (although it works with higher rates in other browsers like safari).
In chrome, if the utterance rate is above 2, it causes the window.speechSynthesis to be stuck, and needs window.speechSynthesis.cancel() before it will play audio again (at a valid rate below 2) via .speak().
Did your text to voice tryout work only once? Here is why.
In chrome you have to cancel the speechSynthesis, otherwise its not compliant to googles autoplay policy. So you should start your script with:
window.speechSynthesis.cancel()
To cancel any speech synthesis that happened before.
resultsDisplay = document.getElementById("rd");
startButton = document.getElementById("startbtn");
stopButton = document.getElementById("stopbtn");
recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)();
recognition.lang = "en-US";
recognition.interimResults = false;
recognition.maxAlternatives = 5;
recognition.onresult = function(event) {
resultsDisplay.innerHTML = "You Said:" + event.results[0][0].transcript;
};
function start() {
recognition.start();
startButton.style.display = "none";
stopButton.style.display = "block";
}
function stop() {
recognition.stop();
startButton.style.display = "block";
stopButton.style.display = "none";
}
.resultsDisplay {width: 100%; height: 90%;}
#stopbtn {display: none;}
<div class="resultsDisplay" id="rd"></div>
<br/>
<center>
<button onclick="start()" id="startbtn">Start</button>
<button onclick="stop()" id="stopbtn">Stop</button>
</center>
Try
utterance = new SpeechSynthesisUtterance("cat, dog, bark");
speechSynthesis.speak(utterance);
I made a Weave at LiveWeave.
Instead of specifying the text while calling new, you could try specifying an object with rate, volume, and text separately, and then converting it to voice.

All-in-one location/hashchange history management library

First of all, I know there's libraries that provide polyfills for location.pushState/popState (History.js, Hash.js, jQuery hashchange), so please don't just link to those.
I need a more powerful library to achieve the following in a RIA:
User clicks a link
library is notified and loads context via Ajax (no complete reload!)
All <a> elements are leveraged with a click handler that
prevents page reloads in 2. (preventDefault) and
calls location.pushState instead / sets location.hash for older browsers
loaded content is inserted in page and replaces current content
Continue with 1.
Also, previously loaded content should be restored as the user navigates back.
As an example, klick through Google+ in Internet Explorer <10 and any other browser.
Is there anything that comes even close? I need support for IE8, FF10, Safari 5 and Chrome 18. Also, it should have a permissive license like MIT or Apache.
I believe Sammy.js ( http://sammyjs.org) (MIT-licenced) has the best focus on what you want to do, with its 2 main pillars being:
Routes
Events
I could quote from the docs but it's pretty straightforward:
setup clientside routes that relate to stuff to be done, e.g: update the view through ajax
link events to call routes, e.g: call the route above when I click an link. (You would have to make sure e.preventDefault is called in the defined event I believe, since this is an app decision really, so that can't be abstracted away by any library that you're going to use imho)
Some relevant docs
http://sammyjs.org/docs
http://sammyjs.org/docs/routes
http://sammyjs.org/docs/events
Example for a route: (from http://sammyjs.org/docs/tutorials/json_store_1)
this.get('#/', function(context) {
$.ajax({
url: 'data/items.json',
dataType: 'json',
success: function(items) {
$.each(items, function(i, item) {
context.log(item.title, '-', item.artist);
});
}
});
});
Or something like
this.get('#/', function(context) {
context.app.swap(''); ///the 'swap' here indicates a cleaning of the view
//before partials are loaded, effectively rerendering the entire screen. NOt doing the swap enables you to do infinite-scrolling / appending style, etc.
// ...
});
Of course other clientside MVC-frameworks could be an option too, which take away even more plumbing, but might be overkill in this situation.
a pretty good (and still fairly recent) comparison:
http://codebrief.com/2012/01/the-top-10-javascript-mvc-frameworks-reviewed/
( I use Spine.js myself ) .
Lastly, I thought it might be useful to include an answer I've written a while ago that goes into detail to the whole best-practice (as I see it) in client-side refreshes, etc. Perhaps you find it useful:
Accessibility and all these JavaScript frameworks
I currently use PathJS in one of my applications.
It has been the best decision that i have made.
For your particular usecase take a look at HTML5 Example.
The piece of code that that makes the example work (from the source):
<script type="text/javascript">
// This example makes use of the jQuery library.
// You can use any methods as actions in PathJS. You can define them as I do below,
// assign them to variables, or use anonymous functions. The choice is yours.
function notFound(){
$("#output .content").html("404 Not Found");
$("#output .content").addClass("error");
}
function setPageBackground(){
$("#output .content").removeClass("error");
}
// Here we define our routes. You'll notice that I only define three routes, even
// though there are four links. Each route has an action assigned to it (via the
// `to` method, as well as an `enter` method. The `enter` method is called before
// the route is performed, which allows you to do any setup you need (changes classes,
// performing AJAX calls, adding animations, etc.
Path.map("/users").to(function(){
$("#output .content").html("Users");
}).enter(setPageBackground);
Path.map("/about").to(function(){
$("#output .content").html("About");
}).enter(setPageBackground);
Path.map("/contact").to(function(){
$("#output .content").html("Contact");
}).enter(setPageBackground);
// The `Path.rescue()` method takes a function as an argument, and will be called when
// a route is activated that you have not yet defined an action for. On this example
// page, you'll notice there is no defined route for the "Unicorns!?" link. Since no
// route is defined, it calls this method instead.
Path.rescue(notFound);
$(document).ready(function(){
// This line is used to start the HTML5 PathJS listener. This will modify the
// `window.onpopstate` method accordingly, check that HTML5 is supported, and
// fall back to hashtags if you tell it to. Calling it with no arguments will
// cause it to do nothing if HTML5 is not supported
Path.history.listen();
// If you would like it to gracefully fallback to Hashtags in the event that HTML5
// isn't supported, just pass `true` into the method.
// Path.history.listen(true);
$("a").click(function(event){
event.preventDefault();
// To make use of the HTML5 History API, you need to tell your click events to
// add to the history stack by calling the `Path.history.pushState` method. This
// method is analogous to the regular `window.history.pushState` method, but
// wraps calls to it around the PathJS dispatched. Conveniently, you'll still have
// access to any state data you assign to it as if you had manually set it via
// the standard methods.
Path.history.pushState({}, "", $(this).attr("href"));
});
});
</script>
PathJS has some of the most wanted features of a routing library:
Lightweight
Supports the HTML5 History API, the 'onhashchange' method, and graceful degredation
Supports root routes, rescue methods, paramaterized routes, optional route components (dynamic routes), and Aspect Oriented Programming
Well Tested (tests available in the ./tests directory)
Compatible with all major browsers (Tested on Firefox 3.6, Firefox 4.0, Firefox 5.0, Chrome 9, Opera 11, IE7, IE8, IE9)
Independant of all third party libraries, but plays nice with all of them
I found the last too points most attractive.
You can find them here
I hope you find this useful.
i'd like to suggest a combination of
crossroads.js as a router
http://millermedeiros.github.com/crossroads.js/
and hasher for handling browser history and hash urls (w/ plenty of fallback solutions):
https://github.com/millermedeiros/hasher/
(based on http://millermedeiros.github.com/js-signals/)
This will still require a few lines of code (to load ajax content etc.), but give you loads and loads of other possibilities when handling a route.
Here's an example using jQuery (none of the above libraries require jQuery, i'm just lazy...)
http://fiddle.jshell.net/Fe5Kz/2/show/light
HTML
<ul id="menu">
<li>
foo
</li>
<li>
bar/baz
</li>
</ul>
<div id="content"></div>
JS
//register routes
crossroads.addRoute('foo', function() {
$('#content').html('this could be ajax loaded content or whatever');
});
crossroads.addRoute('bar/{baz}', function(baz) {
//maybe do something with the parameter ...
//$('#content').load('ajax_url?baz='+baz, function(){
// $('#content').html('bar route called with parameter ' + baz);
//});
$('#content').html('bar route called with parameter ' + baz);
});
//setup hash handling
function parseHash(newHash, oldHash) {
crossroads.parse(newHash);
}
hasher.initialized.add(parseHash);
hasher.changed.add(parseHash);
hasher.init();
//add click listener to menu items
$('#menu li a').on('click', function(e) {
e.preventDefault();
$('#menu a').removeClass('active');
$(this).addClass('active');
hasher.setHash($(this).attr('href'));
});​
Have you looked at the BigShelf sample SPA (Single Page Application) from Microsoft? It sounds like it covers how to achieve most of what you're asking.
It makes use of History.js, a custom wrapper object to easily control navigation called NavHistory and Knockout.js for click handling.
Here's an extremely abbreviated workflow of how this works: first you'll need to initialize a NavHistory object which wraps history.js and registers a callback which executes when there is a push state or hash change:
var nav = new NavHistory({
params: { page: 1, filter: "all", ... etc ... },
onNavigate: function (navEntry) {
// Respond to the incoming sort/page/filter parameters
// by updating booksDataSource and re-querying the server
}
});
Next, you'll define one or more Knockout.js view models with commands that can be bound to links buttons, etc:
var ViewModel = function (nav) {
this.search = function () {
nav.navigate({ page: 2, filter: '', ... }); // JSON object matching the NavHistory params
};
}
Finally, in your markup, you'll use Knockout.js to bind your commands to various elements:
<a data-bind="click: search">...</a>
The linked resources are much more detailed in explaining how all of this works. Unfortunately, it's not a single framework like you're seeking, but you'd be surprised how easy it is to get this working.
One more thing, following the BigShelf example, the site I'm building is fully cross-browser compatible, IE6+, Firefox, Safari (mobile and desktop) and Chrome (mobile and desktop).
The AjaxTCR Library seems to cover all bases and contains robust methods that I haven't seen before. It's released under a BSD License (Open Source Initiative).
For example, here are five AjaxTCR.history(); methods:
init(onStateChangeCallback, initState);
addToHistory(id, data, title, url, options);
getAll();
getPosition();
enableBackGuard(message, immediate);
The above addToHistory(); has enough parameters to allow for deep hash-linking in websites.
More eye-candy of .com.cookie(), .storage(), and .template() provides more than enough methods to handle any session data requirements.
The well documented AjaxTCR API webpage has a plethora of information with downloadable doc's to boot!
Status Update:
That website also has an Examples Webpage Section including downloadable .zip files with ready to use Front End(Client) and Back End(Server) project files.
Notably are the following ready-to-use examples:
One-way Cookie
HttpOnly Cookies
History Stealing
History Explorer
There are quite a bit other examples that rounds out the process to use many of their API methods, making any small learning curve faster to complete.
Several suggestions
ExtJs, see their History Example, and here are the docs.
YUI Browser History Manager.
jQuery BBQ seem to provide a more advanced feature-set over jQuery.hashcode.
ReallySimpleHistory may also be of help, though it's quite old and possibly outdated.
Note: ExtJs History has been extended to optimize duplicate (redundant) calls to add().
PJAX is the process you're describing.
The more advanced pjax techniques will even start to preload the content, when the user hovers over the link.
This is a good pjax library.
https://github.com/MoOx/pjax
You mark the containers which need will be updated on the subsequent requests:
new Pjax({ selectors: ["title", ".my-Header", ".my-Content", ".my-Sidebar"] })
So in the above, only the title, the .my-header, .my-content, and .my-sidebar will be replaced with the content from the ajax call.
Somethings to look out for
Pay attention to how your JS loads and detects when the page is ready. The javascript will not reload on new pages. Also pay attention to when any analytics calls get called, for the same reason.

event.DataTransfer doesn't transfer ID of dragged object when running in Jetty

I have a Maven Jetty project with JSPs using JavaScript. I want to be able to highlight parts of a canvas corresponding to the dragged image's size.
When I look at my JSP by simply opening it in the browser everything works as expected but when I start the Jetty Server with the goal jetty:run the ID of the dragged object is not being set or cannot be retrieved from the transferData of the event.
The relevant code: All draggable images have a unique ID. On drag start I set the ID of the dragged image on the event's transferData like this:
function dragShipFromTable(event) {
event.dataTransfer.setData("id",event.target.id);
}
When the image is dragged over the canvas I call the following function
function allowDrop(event) {
event.preventDefault();
var id = event.dataTransfer.getData("id");
var img = document.getElementById(id);
// Do the highlighting stuff
....
}
The problem is that when the server is started and I do the above action then in allowDrop(event) the ID of the dragged image is not being retrieved from the transferData. It is undefined and therefore the highlighting fails. This is not the case when simply opening the JSP as a File.
Well I kind of found the answer to my own question. First of all the setData method on the dataTransfer object only allows certain formats as first parameter. I interpreted it as an associative array but this is not the case. So it should be
event.dataTransfer.setData("text/plain", id);
event.dataTransfer.getData("text/plain");
to set and retrieve the id correctly.
But in the end everything failed due to chrome as there is a bug in Chrome which prevents me from using dataTransfer as intended (see How do you get the selected items from a dragstart event in Chrome? Is dataTransfer.getData broken? ).
I now use global variables to store the information I need. And still I am wondering why everything worked fine when the page was displayed as a file instead of as a response from the webserver.

Preloading images (in Chrome) [duplicate]

I am pre-loading some images and then using them in a lightbox. The problem I have is that although the images are loading, they aren't being displayed by the browser.
This issue is specific to Chrome. It has persisted through Chrome 8 - 10, and I've been trying on and off to fix it all this time and have got nowhere.
I have read these similar questions,
Chrome not displaying images though assets are being delivered to browser
2 Minor Crossbrowser CSS Issues. Background images not displaying in Google Chrome?
JavaScript preloaded images are getting reloaded
Which all detail similar behaviour but in Chrome for Mac. Whereas this is happening in Windows.
All other browsers seem to be fine.
If you have Firefox and Chrome open, load the page in Firefox, and then in Chrome, the images appear.
Once you have manually loaded the images, using the Webkit webdev toolbar thingy, they always show up
All the links the images and such are fine and working
Clearing everything from Chrome doesn't seem to make any difference (cache, history, etc)
If anyone has any ideas it would be fantastically helpfull, as I'm literally all out of options here.
PS, Apologies if there are late replies, I'm off on holiday for a week tomorrow! :D
Update
Here is the javascript function which is preloading the images.
var preloaded = new Array();
function preload_images() {
for (var i = 0; i < arguments.length; i++){
document.write('<');
document.write('img src=\"'+arguments[i]+'\" style=\"display:none;\">');
};
};
Update
I'm still having issues with this, and I've removed the whole preloading images function. Perhaps delivering a style sheet via document.write() isn't the best way?
Chrome might not be preloading them as it's writing to the DOM with no display, so it might be intelligent enough to realise it doesn't need to be rendered. Try this instead:
var preloaded = new Array();
function preload_images(){
for (var x = 0; x < preload_images.arguments.length; x++)
{
preloaded[x] = new Image();
preloaded[x].src = preload_images.arguments[x];
}
}
The Javascript Image object has a lot of useful functions as well you might find useful:
http://www.javascriptkit.com/jsref/image.shtml
onabort()
Code is executed when user aborts the
downloading of the image.
onerror()
Code is executed when an error occurs
with the loading of the image (ie: not
found). Example(s)
onload()
Code is executed when the image
successfully and completely downloads.
And then you also have the complete property which true/false tells you if the image has fully (pre)loaded.
It turns out that Chrome takes into account the HTTP Caching and discards any preloaded images immediately after preload if the Caching is incorrectly set to expire.
In my case I am generating the images dynamically and by default the response was sent to the browser with immediate expiration.
To fix it I had to set the following below:
Response.Cache.SetExpires(DateTime.Now.AddYears(1));
Response.Cache.SetCacheability(HttpCacheability.Public);
return File(jpegStream, "image/jpeg");