Adobe DPS HTML Alert when tapping link that no internet connection available - html

I created an HTML page that has some external links, when the user taps the external link how can I prompt the user that there is no internet connection available? Thanks.

You will probably need some JavaScript and Adobe's store api (for banners or store) or reading api (for html articles or web view in folios). The api provides the singleton object adobeDPS.deviceService which can tell you if the device is online or not. Additionally it provides a signal to indicate a change.
For each link element you register an onclick event handler that checks online state and either passes the click through or catches it and gives a message to the user.
The following code could work:
<script src="js/AdobeLibraryAPI.min.js"></script>
<script>
document.addEventListener("DOMContentLoaded", myLinkChecker.register, false);
var myLinkChecker = {
register: function(){
if (typeof(adobeDPS) !== 'object') {
console.log("Adobe Library not loaded :-(");
this.check = function() { return true } // Fallback
}
var linkList = document.querySelectorAll("a, map > area");
for (var i=0; i < linkList.length; i++){
var e = linkList[i];
if (e.hasAttribute('href'))
e.addEventListener('click', myLinkChecker.check, false);
}
},
check: function(ev){
if (adobeDPS.deviceService.isOnline) { // let <a> process the click
console.log("online");
return true
} else { // cancel click event and show message
event.stopPropagation();
event.preventDefault();
alert("Sorry, your device is not online")
return false;
}
}
}
</script>
Remote debugging html in DPS apps can be done using iOS developer apps and desktop Safari, or Android apps with Google Chrome.

Related

Offline Ready using Service worker

I built an offline first app using the appcache a while ago and wanted to convert it to using the service-worker (my clients all use the latest chrome so I don't have any browser compatibility issues).
I'm using sw-precache to generate a service-worker that caches my local assets (specifically, my html/css/fonts and also some js) and it looks like when the service-worker installs, it does successfully add all the assets to cache storage and it does successfully start (install and activate both fire and complete successfully. And I have the self.skipWaiting() at the end of the install event to start the service-worker (which it does successfully as well)).
The issue is that the "fetch" event doesn't seem to ever fire. As such, if I go offline or open a browser (while already offline) and navigate to the site, I get the Chrome offline dinosaur. When I look at the network tab, it looks like the browser is trying to hit a server to retrieve the pages. I'm not sure what I'm doing wrong and I didn't touch the fetch method that was generated by the sw-precache utility...so I'm not sure what I'm missing. Any help would be greatly appreciated. My fetch event is below:
self.addEventListener('fetch', function(event) {
if (event.request.method === 'GET') {
var urlWithoutIgnoredParameters = stripIgnoredUrlParameters(event.request.url,
IgnoreUrlParametersMatching);
var cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
var directoryIndex = 'index.html';
if (!cacheName && directoryIndex) {
urlWithoutIgnoredParameters = addDirectoryIndex(urlWithoutIgnoredParameters, directoryIndex);
cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
}
var navigateFallback = '';
// Ideally, this would check for event.request.mode === 'navigate', but that is not widely
// supported yet:
// https://code.google.com/p/chromium/issues/detail?id=540967
// https://bugzilla.mozilla.org/show_bug.cgi?id=1209081
if (!cacheName && navigateFallback && event.request.headers.has('accept') &&
event.request.headers.get('accept').includes('text/html') &&
/* eslint-disable quotes, comma-spacing */
isPathWhitelisted([], event.request.url)) {
/* eslint-enable quotes, comma-spacing */
var navigateFallbackUrl = new URL(navigateFallback, self.location);
cacheName = AbsoluteUrlToCacheName[navigateFallbackUrl.toString()];
}
if (cacheName) {
event.respondWith(
// Rely on the fact that each cache we manage should only have one entry, and return that.
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
return cache.match(keys[0]).then(function(response) {
if (response) {
return response;
}
// If for some reason the response was deleted from the cache,
// raise and exception and fall back to the fetch() triggered in the catch().
throw Error('The cache ' + cacheName + ' is empty.');
});
});
}).catch(function(e) {
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
return fetch(event.request);
})
);
}
}
});

Getting the list of voices in speechSynthesis (Web Speech API)

Following HTML shows empty array in console on first click:
<!DOCTYPE html>
<html>
<head>
<script>
function test(){
console.log(window.speechSynthesis.getVoices())
}
</script>
</head>
<body>
Test
</body>
</html>
In second click you will get the expected list.
If you add onload event to call this function (<body onload="test()">), then you can get correct result on first click. Note that the first call on onload still doesn't work properly. It returns empty on page load but works afterward.
Questions:
Since it might be a bug in beta version, I gave up on "Why" questions.
Now, the question is if you want to access window.speechSynthesis on page load:
What is the best hack for this issue?
How can you make sure it will load speechSynthesis, on page load?
Background and tests:
I was testing the new features in Web Speech API, then I got to this problem in my code:
<script type="text/javascript">
$(document).ready(function(){
// Browser support messages. (You might need Chrome 33.0 Beta)
if (!('speechSynthesis' in window)) {
alert("You don't have speechSynthesis");
}
var voices = window.speechSynthesis.getVoices();
console.log(voices) // []
$("#test").on('click', function(){
var voices = window.speechSynthesis.getVoices();
console.log(voices); // [SpeechSynthesisVoice, ...]
});
});
</script>
<a id="test" href="#">click here if 'ready()' didn't work</a>
My question was: why does window.speechSynthesis.getVoices() return empty array, after page is loaded and onready function is triggered? As you can see if you click on the link, same function returns an array of available voices of Chrome by onclick triger?
It seems Chrome loads window.speechSynthesis after the page load!
The problem is not in ready event. If I remove the line var voice=... from ready function, for first click it shows empty list in console. But the second click works fine.
It seems window.speechSynthesis needs more time to load after first call. You need to call it twice! But also, you need to wait and let it load before second call on window.speechSynthesis. For example, following code shows two empty arrays in console if you run it for first time:
// First speechSynthesis call
var voices = window.speechSynthesis.getVoices();
console.log(voices);
// Second speechSynthesis call
voices = window.speechSynthesis.getVoices();
console.log(voices);
According to Web Speech API Errata (E11 2013-10-17), the voice list is loaded async to the page. An onvoiceschanged event is fired when they are loaded.
voiceschanged: Fired when the contents of the SpeechSynthesisVoiceList, that the getVoices method will return, have changed. Examples include: server-side synthesis where the list is determined asynchronously, or when client-side voices are installed/uninstalled.
So, the trick is to set your voice from the callback for that event listener:
// wait on voices to be loaded before fetching list
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
...
};
You can use a setInterval to wait until the voices are loaded before using them however you need and then clearing the setInterval:
var timer = setInterval(function() {
var voices = speechSynthesis.getVoices();
console.log(voices);
if (voices.length !== 0) {
var msg = new SpeechSynthesisUtterance(/*some string here*/);
msg.voice = voices[/*some number here to choose from array*/];
speechSynthesis.speak(msg);
clearInterval(timer);
}
}, 200);
$("#test").on('click', timer);
After studying the behavior on Google Chrome and Firefox, this is what can get all voices:
Since it involves something asynchronous, it might be best done with a promise:
const allVoicesObtained = new Promise(function(resolve, reject) {
let voices = window.speechSynthesis.getVoices();
if (voices.length !== 0) {
resolve(voices);
} else {
window.speechSynthesis.addEventListener("voiceschanged", function() {
voices = window.speechSynthesis.getVoices();
resolve(voices);
});
}
});
allVoicesObtained.then(voices => console.log("All voices:", voices));
Note:
When the event voiceschanged fires, we need to call .getVoices() again. The original array won't be populated with content.
On Google Chrome, we don't have to call getVoices() initially. We only need to listen on the event, and it will then happen. On Firefox, listening is not enough, you have to call getVoices() and then listen on the event voiceschanged, and set the array using getVoices() once you get notified.
Using a promise makes the code more clean. Everything related to getting voices are in this promise code. If you don't use a promise but instead put this code in your speech routine, it is quite messy.
You can write a voiceObtained promise to resolve to a voice you want, and then your function to say something can just do: voiceObtained.then(voice => { }) and inside that handler, call the window.speechSynthesis.speak() to speak something. Or you can even write a promise speechReady("hello world").then(speech => { window.speechSynthesis.speak(speech) }) to say something.
heres the answer
function synthVoice(text) {
const awaitVoices = new Promise(resolve=>
window.speechSynthesis.onvoiceschanged = resolve)
.then(()=> {
const synth = window.speechSynthesis;
var voices = synth.getVoices();
console.log(voices)
const utterance = new SpeechSynthesisUtterance();
utterance.voice = voices[3];
utterance.text = text;
synth.speak(utterance);
});
}
At first i used onvoiceschanged , but it kept firing even after the voices was loaded, so my goal was to avoid onvoiceschanged at all cost.
This is what i came up with. It seems to work so far, will update if it breaks.
loadVoicesWhenAvailable();
function loadVoicesWhenAvailable() {
voices = synth.getVoices();
if (voices.length !== 0) {
console.log("start loading voices");
LoadVoices();
}
else {
setTimeout(function () { loadVoicesWhenAvailable(); }, 10)
}
}
setInterval solution by Salman Oskooi was perfect
Please see https://jsfiddle.net/exrx8e1y/
function myFunction() {
dtlarea=document.getElementById("details");
//dtlarea.style.display="none";
dtltxt="";
var mytimer = setInterval(function() {
var voices = speechSynthesis.getVoices();
//console.log(voices);
if (voices.length !== 0) {
var msg = new SpeechSynthesisUtterance();
msg.rate = document.getElementById("rate").value; // 0.1 to 10
msg.pitch = document.getElementById("pitch").value; //0 to 2
msg.volume = document.getElementById("volume").value; // 0 to 1
msg.text = document.getElementById("sampletext").value;
msg.lang = document.getElementById("lang").value; //'hi-IN';
for(var i=0;i<voices.length;i++){
dtltxt+=voices[i].lang+' '+voices[i].name+'\n';
if(voices[i].lang==msg.lang) {
msg.voice = voices[i]; // Note: some voices don't support altering params
msg.voiceURI = voices[i].voiceURI;
// break;
}
}
msg.onend = function(e) {
console.log('Finished in ' + event.elapsedTime + ' seconds.');
dtlarea.value=dtltxt;
};
speechSynthesis.speak(msg);
clearInterval(mytimer);
}
}, 1000);
}
This works fine on Chrome for MAC, Linux(Ubuntu), Windows and Android
Android has non-standard en_GB wile others have en-GB as language code
Also you will see that same language(lang) has multiple names
On Mac Chrome you get en-GB Daniel besides en-GB Google UK English Female and n-GB Google UK English Male
en-GB Daniel (Mac and iOS)
en-GB Google UK English Female
en-GB Google UK English Male
en_GB English United Kingdom
hi-IN Google हिन्दी
hi-IN Lekha (Mac and iOS)
hi_IN Hindi India
Another way to ensure voices are loaded before you need them is to bind their loading state to a promise, and then dispatch your speech commands from a then:
const awaitVoices = new Promise(done => speechSynthesis.onvoiceschanged = done);
function listVoices() {
awaitVoices.then(()=> {
let voices = speechSynthesis.getVoices();
console.log(voices);
});
}
When you call listVoices, it will either wait for the voices to load first, or dispatch your operation on the next tick.
I used this code to load voices successfully:
<select id="voices"></select>
...
function loadVoices() {
populateVoiceList();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = populateVoiceList;
}
}
function populateVoiceList() {
var allVoices = speechSynthesis.getVoices();
allVoices.forEach(function(voice, index) {
var option = $('<option>').val(index).html(voice.name).prop("selected", voice.default);
$('#voices').append(option);
});
if (allVoices.length > 0 && speechSynthesis.onvoiceschanged !== undefined) {
// unregister event listener (it is fired multiple times)
speechSynthesis.onvoiceschanged = null;
}
}
I found the 'onvoiceschanged' code from this article: https://hacks.mozilla.org/2016/01/firefox-and-the-web-speech-api/
Note: requires JQuery.
Works in Firefox/Safari and Chrome (and in Google Apps Script too - but only in the HTML).
async function speak(txt) {
await initVoices();
const u = new SpeechSynthesisUtterance(txt);
u.voice = speechSynthesis.getVoices()[3];
speechSynthesis.speak(u);
}
function initVoices() {
return new Promise(function (res, rej){
speechSynthesis.getVoices();
if (window.speechSynthesis.onvoiceschanged) {
res();
} else {
window.speechSynthesis.onvoiceschanged = () => res();
}
});
}
While the accepted answer works great but if you're using SPA and not loading full-page, on navigating between links, the voices will not be available.
This will run on a full-page load
window.speechSynthesis.onvoiceschanged
For SPA, it wouldn't run.
You can check if it's undefined, run it, or else, get it from the window object.
An example that works:
let voices = [];
if(window.speechSynthesis.onvoiceschanged == undefined){
window.speechSynthesis.onvoiceschanged = () => {
voices = window.speechSynthesis.getVoices();
}
}else{
voices = window.speechSynthesis.getVoices();
}
// console.log("voices", voices);
I had to do my own research for this to make sure I understood it properly, so just sharing (feel free to edit).
My goal is to:
Get a list of voices available on my device
Populate a select element with those voices (after a particular page loads)
Use easy to understand code
The basic functionality is demonstrated in MDN's official live demo of:
https://github.com/mdn/web-speech-api/tree/master/speak-easy-synthesis
but I wanted to understand it better.
To break the topic down...
SpeechSynthesis
The SpeechSynthesis interface of the Web Speech API is the controller
interface for the speech service; this can be used to retrieve
information about the synthesis voices available on the device, start
and pause speech, and other commands besides.
Source
onvoiceschanged
The onvoiceschanged property of the SpeechSynthesis interface
represents an event handler that will run when the list of
SpeechSynthesisVoice objects that would be returned by the
SpeechSynthesis.getVoices() method has changed (when the voiceschanged
event fires.)
Source
Example A
If my application merely has:
var synth = window.speechSynthesis;
console.log(synth);
console.log(synth.onvoiceschanged);
Chrome developer tools console will show:
Example B
If I change the code to:
var synth = window.speechSynthesis;
console.log("BEFORE");
console.log(synth);
console.log(synth.onvoiceschanged);
console.log("AFTER");
var voices = synth.getVoices();
console.log(voices);
console.log(synth);
console.log(synth.onvoiceschanged);
The before and after states are the same, and voices is an empty array.
Solution
Although i'm not confident implementing Promises, the following worked for me:
Defining the function
var synth = window.speechSynthesis;
// declare so that values are accessible globally
var voices = [];
function set_up_speech() {
return new Promise(function(resolve, reject) {
// get the voices
var voices = synth.getVoices();
// get reference to select element
var $select_topic_speaking_voice = $("#select_topic_speaking_voice");
// for each voice, generate select option html and append to select
for (var i = 0; i < voices.length; i++) {
var option = $("<option></option>");
var suffix = "";
// if it is the default voice, add suffix text
if (voices[i].default) {
suffix = " -- DEFAULT";
}
// create the option text
var option_text = voices[i].name + " (" + voices[i].lang + suffix + ")";
// add the option text
option.text(option_text);
// add option attributes
option.attr("data-lang", voices[i].lang);
option.attr("data-name", voices[i].name);
// append option to select element
$select_topic_speaking_voice.append(option);
}
// resolve the voices value
resolve(voices)
});
}
Calling the function
// in your handler, populate the select element
if (page_title === "something") {
set_up_speech()
}
Android Chrome - turn off data saver. It was helpfull for me.(Chrome 71.0.3578.99)
// wait until the voices load
window.speechSynthesis.onvoiceschanged = function() {
window.speechSynthesis.getVoices();
};
let voices = speechSynthesis.getVoices();
let gotVoices = false;
if (voices.length) {
resolve(voices, message);
} else {
speechSynthesis.onvoiceschanged = () => {
if (!gotVoices) {
voices = speechSynthesis.getVoices();
gotVoices = true;
if (voices.length) resolve(voices, message);
}
};
}
function resolve(voices, message) {
var synth = window.speechSynthesis;
let utter = new SpeechSynthesisUtterance();
utter.lang = 'en-US';
utter.voice = voices[65];
utter.text = message;
utter.volume = 100.0;
synth.speak(utter);
}
Works for Edge, Chrome and Safari - doesn't repeat the sentences.

Sending message from popup.js in Chrome extension to background.js

What is the proper way to send a message (and get a response) to background.js from popup.js in a Chrome extension? Every method I try ends up with an error that:
"Port: Could not establish connection. Receiving end does not exist."
I would prefer to use chrome.extension.sendMessage() over chrome.extension.connect() with port.postMessage(), but neither method seems to have worked.
What I am trying to do is wire a button in the popup.html to call into some javascript in popup.js which calls back to background.js in an effort to get info about the currentTab() that was topMost (ie:to get the current URL string to show in the popup.html)
Right now in popup.js (wired to the action of the button) I have:
function getURL()
{
chrome.extension.sendMessage({greeting: "GetURL"},
function(response) { tabURL = response.navURL });
$("#tabURL").text(tabURL);
}
In background.js I have:
chrome.extension.onMessage.addListener( function(request,sender,sendResponse)
{
if( request.greeting == "GetURL" )
{
var tabURL = "Not set yet";
chrome.tabs.getCurrent(function(tab){
tabURL = tab.url;
});
sendResponse( {navURL:tabURL} );
}
}
Any ideas?
Just to clarify, we talking about communication between popup page from browserAction and background script?
Anyway you have quite a few errors in your code.
First your totally ignore the fact that all callbacks in chrome api are asynchronous.
In background page
var tabURL = "Not set yet";
chrome.tabs.getCurrent(function(tab){
tabURL = tab.url;
}); //this will be invoked somewhere in the future
sendResponse( {navURL:tabURL} );
//navUrl will be always Not set yet because callback of getCurrent hasn't been called yet
Same in popup.js
chrome.runtime.sendMessage({greeting: "GetURL"},
function(response) { tabURL = response.navURL });//callback will be invoked somewhere in the future
$("#tabURL").text(tabURL)//tabURL will display something totally different from what you have been expected
Second error is that chrome.tabs.getCurrent doesn't give you the current tab selected by user in main window. The docs says:
Gets the tab that this script call is being made from. May be
undefined if called from a non-tab context (for example: a background
page or popup view).
So you will get undefined for all of your requests, because you call it in background page. What you need to do is to use method chrome.tabs.query to obtain currently active tabs.
So after fixing all problems new code should look something like this:
background.js
chrome.runtime.onMessage.addListener( function(request,sender,sendResponse)
{
if( request.greeting === "GetURL" )
{
var tabURL = "Not set yet";
chrome.tabs.query({active:true},function(tabs){
if(tabs.length === 0) {
sendResponse({});
return;
}
tabURL = tabs[0].url;
sendResponse( {navURL:tabURL} );
});
}
}
popup.js
function getURL() {
chrome.runtime.sendMessage({greeting: "GetURL"},
function (response) {
tabURL = response.navURL;
$("#tabURL").text(tabURL);
});
}

Upgrading permissions of a Chrome Extensions

We currently have an extension in the Chrome Web Store with many users. We currently request access to foo.site.com but now we'd like to update our permissions so that we can get bar.site.com.
As I understand it now, if we push a new update (new extension version to the Chrome Web Store) requiring these two permissions, existing users will have their extension disabled until they manually re-enable. New users who download the extension after the update will be fine and have both permissions. However, disabling our current users is not an option.
Any way to get around this?
We've looked into optional permissions, which would solve the issue for existing users (we would just ask them to click a button to upgrade the permissions and be on our way). However for new users, they would not only have to install the extension but also accept the optional permissions which sucks. Is there any way for new users to accept all the permission (optional and required) at install time?
Yes, there is solution.
You set the new permissions as optional, and then you request the permission before the user using the new feature.
The is work 100%.
This is what you add to your manifest:
"permissions": ["tabs","http://*/*","https://*/*"],
After that you can use:
chrome.permissions.request
and
chrome.permissions.contains
Facing the same issue with my extension, I came about this post searching for the same question. There is an update that seems acceptable in some cases.
According to:
https://support.google.com/chrome_webstore/answer/1054246
and the example on http://developer.chrome.com/extensions/permission_warnings.html
Seems that updating your permissions will in fact disable your extension, however it will prompt the user about your change and allow an easy "re-enable".
It might not be acceptable in your scenario, however in my case the win of new users with the added permission by default trumps the risk of existing user not re-enabling. As this is drastically better to the way it was before where your existing users were not aware of the extension being disabled...
I know this post is old, but as it is the top Google result for this question thought an update be good for future reference...
Since chrome 16 you can set optional_permission at install time and ask for elevated permission at run time. See https://developer.chrome.com/extensions/permissions
in manifest.json:
{
"name": "My extension",
...
"optional_permissions": [ "tabs", "http://bar.site.com/" ],
...
}
in popup.json:
document.querySelector('#my-button').addEventListener('click', function(event) {
// Permissions must be requested from inside a user gesture, like a button's
// click handler.
chrome.permissions.request({
permissions: ['tabs'],
origins: ['http://bar.site.com/']
}, function(granted) {
// The callback argument will be true if the user granted the permissions.
if (granted) {
doSomething();
} else {
doSomethingElse();
}
});
});
There is a way (using optional permissions) to add new extension permissions without disabling the extension for the existing users, while also ensuring new users get prompted to accept both required and optional permissions after installation.
Optional permissions can be requested using chrome.permissions.request API, but it functions only if the request is preceded by a user action. The simplest way to fulfill this requirement and still provide a seamless user experience is to check for the permissions in the background page and request the optional permissions in a popup at runtime.
Permission request can be made in the background page right after loading the extension (after installation and subsequent browser launches). Any existing user missing any of the permissions will be prompted to accept the permission after relaunching the browser.
The following example includes:
The background page JavaScript (background_page.js)
Notification popup html file (notification_popup.html)
Notification popup JavaScript (notification_popup.js)
You will also need declare the "optional_permissions" in manifest.json for the example to work. The example will work with both a Chrome Extension and a Firefox Add-on.
background_page.js
var brwsr = null;
if (typeof chrome != "undefined") {
brwsr = chrome;
}
else{
brwsr = browser;
}
var opt_perms = brwsr.runtime.getManifest().optional_permissions;
var requiredOptionalPermissions = {}
if(typeof opt_perms!="undefined" && opt_perms.length>0){
var perms = []
var origins = []
var re = new RegExp("^(http|https)://", "i");
for(var i=0; i<opt_perms.length; i++){
if(opt_perms[i]==="<all_urls>"|| re.test(opt_perms[i])){
origins.push(opt_perms[i])
}
else{
perms.push(opt_perms[i])
}
}
if(perms.length>0){
requiredOptionalPermissions.permissions = perms
}
if(origins.length>0){
requiredOptionalPermissions.origins = origins
}
}
var requiresPermission = ()=>{
// add your code here
}
// check if optional permission exists and request if not
var runFunctionsRequiringOptPermissions = function(requiredPermissions={}, userGesture=false, callback=()=>{}){
if(typeof requiredPermissions.permissions!="undefined" || typeof requiredPermissions.origins!="undefined"){
brwsr.permissions.contains(requiredPermissions, function(res) {
if (!res) {
// The extension doesn't have the permissions.)
if(userGesture){
brwsr.permissions.request(requiredPermissions, function(granted){
if(granted){
// perform actions that required the permission
requiresPermission()
}
callback();
});
return;
}
// open the notification popup
window.open("notification_popup.html", "extension_popup", "width=530,height=190,status=no,scrollbars=yes,resizable=no,screenY=+"+(screen.height/2-95)+",screenX="+(screen.width/2-265));
}
else{
// perform actions that required the permission
requiresPermission()
}
});
}
}
runFunctionsRequiringOptPermissions(requiredOptionalPermissions)
// add listener
brwsr.runtime.onConnect.addListener(function(port) {
if(port.name == "optionalPermRequestPopup"){
port.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.okButtonClicked === true) {
runFunctionsRequiringOptPermissions(requiredOptionalPermissions, true, function(){
// callback to close the notification popup
port.postMessage({'close':true});
})
return;
}
port.postMessage({'close':true})
}
);
}
});
notification_popup.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Extension Name</title>
<style>
.container {
font-size:medium;
text-align:center;
}
.button{
height: 30px;
width: 70px;
background-color: #448be9;
color: white;
border-color: #448be9;
border-width: thin;
margin-right: 10px;
float: right;
}
.btnCancel{
background-color: white;
color: #448be9
}
</style>
</head>
<body>
<div class="container">
<p class='text'>Please upgrade the permissions to enable the latest features.</p>
<button class="button btnCancel">Cancel</button>
<button class="button btnOk">OK</button>
</div>
<script src="notification_popup.js"></script>
</body>
</html>
notification_popup.js
var brwsr = null;
if (typeof chrome != "undefined") {
brwsr = chrome;
}
else{
brwsr = browser;
}
var port = brwsr.runtime.connect({
name : "optionalPermRequestPopup"
});
window.addEventListener("load", function(event) {
var okButtons = document.querySelectorAll(".btnOk");
okButtons.forEach(function(okButton) {
okButton.addEventListener("click", function(event) {
port.postMessage({okButtonClicked: true})
});
});
port.onMessage.addListener(function(request) {
for (var key in request) {
switch(key){
case "close":
window.close();
break;
default:
break;
}
}
});
});

Chrome Extension: how to capture selected text and send to a web service

For the Google Chrome extension, I need to capture selected text in a web page and send to a web service. I'm stuck!
First I tried a bookmarklet, but Chrome on Mac seems to have some bookmarklet bugs so I decided to write an extension.
I use this code in my ext:
function getSelText(){
var txt = 'nothing';
if (window.getSelection){
txt = "1" + window.getSelection();
} else if (document.getSelection) {
txt = "2" + document.getSelection();
} else if (document.selection) {
txt = "3" + document.selection.createRange().text;
} else txt = "wtf";
return txt;
}
var selection = getSelText();
alert("selection = " + selection);
When I click on my extension icon, I get a "1". So I think the act of selecting outside the browser window is causing the text to not be seen by the browser as "selected" any more.
Just a theory....
thoughts?
You can do this by using Extensions Messaging. Basically, your "background page" will send the request to your service. For example, lets say you have a "popup" and once you click on it, it will do a "Google search" which is your service.
content_script.js
In your content script, we need to listen for a request coming from your extension, so that we send it the selected text:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getSelection")
sendResponse({data: window.getSelection().toString()});
else
sendResponse({}); // snub them.
});
background.html
Now in background page you can handle the popup onclick event so that we know we clicked on the popup. Once we clicked on it, the callback fires, and then we can send a request to the content script using "Messaging" to fetch the selected text.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"}, function(response){
sendServiceRequest(response.data);
});
});
function sendServiceRequest(selectedText) {
var serviceCall = 'http://www.google.com/search?q=' + selectedText;
chrome.tabs.create({url: serviceCall});
}
As you have seen, I registered a listener in a content script to allow my extension to send and receive messages from it. Then once I received a message, I handle it by searching for Google.
Hopefully, you can use what I explained above and apply it to your scenario. I just have to warn you that the code written above is not tested, so their might be spelling, or syntax errors. But those can easily be found by looking at your Inspector :)
content script
document.addEventListener('mouseup',function(event)
{
var sel = window.getSelection().toString();
if(sel.length)
chrome.extension.sendRequest({'message':'setText','data': sel},function(response){})
})
Background Page
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
manifest.json
{
"name": "Word Reminder",
"version": "1.0",
"description": "Word Reminder.",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background_page": "background.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/myscript.js"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
]
}
and here is the link where i have all in one extension to download.
after reading this i tried of my own and have published.
and here is the complete source
http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm
Enjoy
Using a content_scripts is not a great solution as it injection to all documents including iframe-ads etc. I get an empty text selection from other pages than the one I expect half the times on messy web sites.
A better solution is to inject code into the selected tab only, as this is where your selected text lives anyhow. Example of jquery doc ready section:
$(document).ready(function() {
// set up an event listener that triggers when chrome.extension.sendRequest is fired.
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
// text selection is stored in request.selection
$('#text').val( request.selection );
});
// inject javascript into DOM of selected window and tab.
// injected code send a message (with selected text) back to the plugin using chrome.extension.sendRequest
chrome.tabs.executeScript(null, {code: "chrome.extension.sendRequest({selection: window.getSelection().toString() });"});
});
It is not clear from your code where it is. What I mean, is that if this code is either in popup html or background html then the results you are seeing are correct, nothing in those windows will be selected.
You will need to place this code in a content script so that it has access to the DOM of the page, and then when you click your browser action, you will need to send a message to the content script to fetch the current document selection.
You don't need a Google API for something as simple as this...
I'll use the Bing online service as an example. Note that the URL is set up to accept a parameter:
var WebService='http://www.bing.com/translator/?text=';
frameID.contentWindow.document.body.addEventListener('contextmenu',function(e){
T=frameID.contentWindow.getSelection().toString();
if(T!==''){e.preventDefault(); Open_New_Tab(WebService+encodeURIComponent(T)); return false;}
},false);
NB: The function "Open_New_Tab()" used above is an imaginary one that accepts the webservice URL with the encoded selected text as a parameter.
That's the idea basically.