SpeechSynthesisUtterance not working in mobile broswer - mobile

I am using SpeechSynthesisUtterance in my mobile website. This code is okay when I use it in my desktop version website. But I found that the function is not working in mobile browser in document.ready as below:
$(document).ready(function(){
var text_tts="say something";
speakText(text_tts);
});
function speakText(text_tts){
var u = new SpeechSynthesisUtterance();
u.text = text_tts;
u.lang = 'en-US';
u.rate = 1;
u.onend = function(event) { console.log('Finished in ' + event.elapsedTime + ' seconds.'); }
speechSynthesis.speak(u);
}
But when I use "click" event, it works:
$("body").on("click",".button",function(){
var tmp_body_text="say something";
var u = new SpeechSynthesisUtterance();
u.text = tmp_body_text;
u.lang = 'en-US';
u.rate = 1;
//u.onend = function(event) { console.log('Finished in ' + event.elapsedTime + ' seconds.'); }
speechSynthesis.speak(u);
});
Please help. Thank you.

I have seen several mentions (like this, for example) that speak only works when called from a user interaction, such as a click. From what I have seen, that seems to be the case for Safari on iOS, and that appears to be what you are describing in your question.
I have not actually found any kind of documentation that confirms this behavior, and would appreciate any references anyone can provide.

Related

Prevent search changes from spamming history

I'm using links like #!/OrderList?id=123, which shows the list of all orders and more details of the order 123. With reloadOnSearch=false and watching $routeUpdate it works fine, except for one thing: All such links get put into the browsers history, while I'd prefer to have only one such link there. For example, instead of
#!/OrderList?id=123
#!/OrderList?id=124
#!/OrderList?id=125
#!/AnotherList?id=678
#!/AnotherList?id=679
just the last member of each group, i.e.,
#!/OrderList?id=125
#!/AnotherList?id=679
I'm aware of $location.replace(), but I can't see where to place it when the change happens via following a link. I tried to place it in $scope.$on("$routeUpdate", ...), but it did nothing, probably because it's too late when the route has already changed.
I'm not using neither router-ui nor the HTML5 mode (just plain angular-route).
I'm afraid, I wasn't clear about me insisting on using href rather than a custom handler. I want the links to work with middle mouse click and bookmarks and everything. A combination of ng-href and ng-click might do what I want, but I've found a simple solution working with plain links.
Looks like you may want to update the URL query parameter using an ng-click function instead of relying on a link, then call a function like the one below to update the parameter... With replace state, the history should only track the current value. I haven't tested this case so if you try it, let me know if it works.
function changeUrlParam (param, value) {
var currentURL = window.location.href;
var urlObject = currentURL.split('?');
var newQueryString = '?';
value = encodeURIComponent(value);
if(urlObject.length > 1){
var queries = urlObject[1].split('&');
var updatedExistingParam = false;
for (i = 0; i < queries.length; i++){
var queryItem = queries[i].split('=');
if(queryItem.length > 1){
if(queryItem[0] == param){
newQueryString += queryItem[0] + '=' + value + '&';
updatedExistingParam = true;
}else{
newQueryString += queryItem[0] + '=' + queryItem[1] + '&';
}
}
}
if(!updatedExistingParam){
newQueryString += param + '=' + value + '&';
}
}else{
newQueryString += param + '=' + value + '&';
}
window.history.replaceState('', '', urlObject[0] + newQueryString.slice(0, -1));
}
Maybe what you can do is, istead of a regular <a ng-href="#!/OrderList?id={{your.id}}">Link to your ID</a> you can create a link with an ng-clickdirective bound to a function which retrieves the data and passes it to the view.
Your HTML
`<span ng-click="loadListItem(your.id)">Link to your ID</span>`
<div id="your-item-data">
{{item.id}} - {{item.name}}
</div>
Your controller
myApp.controller('someController', function($scope) {
$scope.loadListItem(itemId) = function (
var myItem;
// Get item by 'itemId' and assign it to 'myItem' var
$scope.item = myItem;
);
});
This way instead of changing your URL, you can retrieve the item data in your controller and pass it to your view.
You don't give much detail of your controller/service implementation, but I hope this helps.
I think you were on the right track with the $scope.$on("$routeUpdate", ...) thing. Rather than $routeUpdate, however, try binding on $routeChangeStart:
$scope.$on("$routeChangeStart", function(event, nextRoute, currentRoute){
if (nextRoute.yourCriteria === currentRoute.yourCriteria){
//do your location replacement magic
}
});
If you wanted, you could even define a dontUpdateHistory boolean property in your route definitions, and then check for that property in your run block:
myApp.config(function($routeProvider){
$routeProvider.when('/whatever' {
templateUrl: 'whatever',
dontUpdateHistory: true //something like this
});
}).run(function($rootScope){
$rootScope.on('$routeChangeStart', function(event, nextRoute, currentRoute){
if (nextRoute.dontUpdateHistory){
//do your location replacement magic
}
});
I haven't tested any of this, but hopefully it gets the idea across.
I wasn't satisfied with any answer and after quite some debugging I found this solution:
.run(function($rootScope, $location) {
var replacing;
$rootScope.$on("$locationChangeStart", function(event, newUrl, oldUrl) {
if (oldUrl === newUrl) return; // Nobody cares.
// Make urls relative.
var baseLength = $location.absUrl().length - $location.url().length;
newUrl = newUrl.substring(baseLength);
oldUrl = oldUrl.substring(baseLength);
// Strip search, leave path only.
var newPath = newUrl.replace(/\?.*/, "");
var oldPath = oldUrl.replace(/\?.*/, "");
// Substantial change, history should be written normally.
if (oldPath !== newPath) return;
// We're replacing, just let it happen.
if (replacing) {
replacing = false;
return;
}
// We're NOT replacing, scratch it ...
event.preventDefault();
// ... and do the same transition with replace later.
$rootScope.$evalAsync(function() {
$location.url(newUrl).replace();
replacing = true;
});
});
})

Protractor won't get repeater inside a ng-view

I've a tricky question for you guys out there. I've made a simple exercise webapp using AngularJS and ngRoute.
Inside my index.html I got an ng-view element which provide two pages search_page.html and detail_of_result_page.html. The code works pretty fine, I put something in the first page input field, hit search button and all results magically appears in my page. The troubles comes with protractor that seems to not see my result in results repeater with his:
element.all(by.repeater("result in results"))
I've tried to put in browser.pause() and watch for errors, but everything seems right.
I've forgot the error code from Protractor:
Failed: Index out of bound.
Trying to access element at index: 0, but there are only 0 elements that match locator by.repeater("result in results")
OK, under your searchTest.js line: 27
beforeEach(function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
});
Things seem alright until you do resultLink.click(); Assuming that it work fine for the first it ("should set the url to the selected detail view"). But when it come to second it("should see the details in the main page component") at this moment you are no longer on /#/splash route. Therefore your pereater no longer available to be located when your beforeEach() run again.
Solution
Your beforeEach doesn't seem useful and logically not run-able for your second it("should see the details in the main page component"). So just move all the thing like this:
it ("should set the url to the selected detail view",function () {
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf($('li')),5000);
var resultItem = element.all(by.repeater('result in results')).first(); //BUG
var resultLink = resultItem.element(by.css('a'));
resultLink.click();
resultId = resultLink.getAttribute('href').then(function (attr) {
//var res = attr.match('/\/TDDBook\/Search\/#\/detail\/(\d+)/')[1]; // TODO: fix this shit
return 1;
});
resultId.then(function (id) {
var expectedUrl = '/detail/'+id;
browser.getLocationAbsUrl().then(function (url) {
expect(url).toBe(expectedUrl);
})
})
})
P.S. just for your information, there is Page Object in protractor, which will be fit with the thing you attempt to do with your beforeEach() . Plus with a tiny bit of knowledge of commonJS (module.exports & require()) it will perfectly suit your needs ;) Cheer!

Can I use angular/di.js with an AngulrJS 1.3.0 project?

The new dependency injection that Volta spoke about at ng-conf and is contained here: https://github.com/angular/di.js is just what I am looking for for my AngularJS 1.3.0 project.
The problem is, it is not clear to me whether or not I can use it. There doesn't seem to be an example of using it for AngularJS v1 in the github project examples.
I came across an example using it in a Backbone project: http://teropa.info/blog/2014/03/18/using-angular-2-0-dependency-injection-in-a-backbone-app.html and I found an example of using ES6 in an AngularJS v1 project: https://github.com/mvolkmann/todo-es6/, but I can't find an example using the new DI in an Angular v1 project.
I'm confused. Any pointers?
Maybe don't use di.js but instead transpile similarly styled code into valid angular 1.X syntax (during a build step)
A small example and a possible start:
var falafel = require('falafel');
var traceur = require('traceur');
var src =
'#Inject(MyService,MyOtherService)' +
'class Thing{' +
' constructor(service,otherservice){' +
' }' +
'}';
src = traceur.compile(src, { annotations: true });
//console.log(src);
function tryGetPath(obj, path) {
path.split('.').forEach(function(key) {
obj = obj && obj[key];
});
return obj;
}
var output = falafel(src, function(node) {
//find `Object.defineProperty for 'annotations'`
if (node.type === 'CallExpression' && tryGetPath(node, 'arguments.1.value') === 'annotations') {
var injectable = tryGetPath(node, 'arguments.0.name');
var $inject = (tryGetPath(node, 'arguments.2.properties.0.value.body.body.0.argument.elements') || [])
.filter(function(a){return a.callee.name === 'Inject'})
.reduce(function(p,c){ p.push.apply(p,c.arguments); return p;},[])
.map(function(a){return "'"+a.name+"'";});
node.update(injectable + '.$inject = [' + $inject.toString() + '];');
}
});
console.log(output);
Perhaps you can even use certain attributes (eg #NgController etc) to register it on your module as a controller.

jQuery: looping array to compile url get variables and values

I have an array in jQuery, and I'm using $.each() to loop through the array. I'm trying to find a way to compile a URL link using the values in the array
var selectArr = ["numbers123", "more_array_values", "etc", "more"];
$.each(selectArr,function(k,v){
k++;
alert('fid'+k+'='+v);
});
This works,
But I would like to somehow get it so it'll be like
var url = fid1=numbers123&fid2=more_array_values&fid3=etc ...
This way, I can use url and append it to a <a href=''>
Thanks!
With some creative application of jQuery.map and the array method .join:
var url = $.map(selectArr, function(v,k) {
// encodeURIComponent makes the value "URL safe"
return 'fid' + (k + 1) + '=' + encodeURIComponent(v);
}).join('&');
Try this
var selectArr = ["numbers123", "more_array_values", "etc", "more"];
var parts = [];
$.each(selectArr,function(k,v){
parts.push('fid'+(k+1)+'='+v)
});
var url = parts.join('&');
Demo: Fiddle
You can clean up the answer by using the jQuery.map function as Felix said
I would recommend using that method over mine.

Multi Kml Layer on Google maps

I have developed a simple google map web page. I need help form anyone who has experience in google maps. In right panel of my web page I add some checkboxes and I want to link different kml with these checkboxes. When I check all layer all kml are show in google maps and when I uncheck any layer checkbox then kml related to that checkbox will disappear. Please guide me how I can do this.
Please check the page from this link
http://toptrippk.com/webgis/kml%20layers.html
I suggest you the following structure. Using this method you don't have to write seperate functions for every layer but only this one. Note that 'id' of 'input' tags here is used as KML filename. You probably want to see this question. If looking forward then geoxml3 may be an option for you.
var G = google.maps;
function toggle() {
if (!this.kml)
{this.kml = new G.KmlLayer('http://toptrippk.com/webgis/uploadfiles/kml/' + this.id + '.kml', {preserveViewport:true}); this.on = false};
if (this.on)
{this.kml.setMap(null); this.on = false} else {this.kml.setMap(map); this.on = true};
};
function initialize() {
var layers = document.getElementsByTagName('input');
var options = {};
map = new G.Map(document.getElementById('map_canvas'), options);
for (var i=0; i<layers.length; i++) {G.event.addDomListener(layers[i], 'click', toggle)};};
G.event.addDomListener(window, 'load', initialize);

Resources