Double processing after context menu click - google-chrome

I have an extension that inserts text into textarea and similar.
The user can choose text from the popup or the contextmenu.
I have "all_frames" : true so that my content script can see all the iframes - in case a textarea is in an iframe.
After a save to the database (more on this in a bit), if the user uses the contextmenu to add text (handled via messaging from background.js to content.js) to a text area the text is added twice.
If the user uses the popup.html mechanism, which passes text to the same content.js, the text is only entered once (the desired outcomes!).
The message is only passed once by background.js.
For instance, if there are five iframes on a page, and therefore 5 instances of content.js, the contextmenu listener script runs 10 times (not 5, as it should).
If I remove "all_frames" : true from the manifest then the text only adds once.
But I need to keep this setting so I can reach textareas in iFrames. And the popup insert works beautifully everywhere.
The save to the database is what triggers the behavior - and that process is a simple json write to a php file. I wipe the contextmenu as part of that process - but again, it isn't a question of the background.js sending 2 messages. One message is sent, and for some reason the code runs through the messaging routine 2x.
I've tried creating flags to mark once a single successful insertion has happened, but everything is asynch and I don't think this would be helpful even if I could get it to work in some cases.
I feel like I need to kill some old content.js processes or something...
The kicker: if I Reload the Extension (in developer mode) the contextmenu works properly and inserts selected text only once. It's only after I save to the db that the problem occurs.
I'm very close to throwing out contextmenus as a feature and just using the elegant popup.html. But this seems like some weird edge case that would be worth understanding.
Many thanks for taking a look. I've included some code and also a screenshot of the console showing the 10x repeats of the content.js code when inserting into a Google Doc.
background.js
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {method: "insertComment", comment: tempText}, function(response) {
// console.log(response.status);
});
});
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.method == 'insertComment'){
var sValue = request.comment;
var currentEltag = document.activeElement.tagName.toLowerCase(); //Checking for currently selected area
console.log("currentEltag before logic: " + currentEltag + " / id: " + document.activeElement.id);
console.log("activeTag: " + activeTag);
// console.log("activeEl: " + activeEl.id + ' / name: ' + activeEl.name + ' /value: ' + activeEl.value);
if (activeTag === undefined || activeTag === null){
console.log('currentEltag in logic: ' + currentEltag);
if (currentEltag === 'iframe'){
activeTag = 'iframe';
console.log('Making activeTag equal iframe');
}
}
var sField = activeEl;
if (activeTag === 'input' || activeTag === 'textarea'){
console.log('Dealing with plain input/textarea - yes! sField is: ' + sField);
var nStart = sField.selectionStart;
var nEnd = sField.selectionEnd;
if (nStart || nEnd == '0'){
console.log("Inside insert sub with starting point: " + nStart + ' and end ' + nEnd + ' with value ' + sValue);
console.log('Print and increment...');
sField.value = sField.value.substring(0, nStart) + sValue + sField.value.substring(nEnd, sField.value.length);
sField.selectionStart = nStart + sValue.length;
sField.selectionEnd = nStart + sValue.length;
console.log('Printed value1...and flag is now: ');
chrome.storage.sync.get("flag", function(data) {
console.log("Flag", data.flag);
});
}
else {
sField.value = sValue;
console.log('Printed value2...');
}
} //End if input or textarea

Related

Chrome addon popup causes problem with the addon's button badge text

I have created a simple addon which bookmarks the active tab in a specific folder when clicked.
The addon is consisted of two .js files, the background.js and the popup.js. The background.js does the heavy work and sends some date values to the popup.js after the bookmarking is done, which then displays them in a popup window. it also displays an 'OK' text on the addon button. After 2 seconds both the popup window and the button text clear out automatically.
Everything works fine, the bookmarking and when showing and closing the popup and the badge.
Here is the problem though. If I happen to close the popup window during those 2 seconds by clicking anywhere in the page or the browser, the badge text doesnt clear out. Until I click the button again next time and repeat the process by letting the popup close by itself this time, then the text clears out.
So what causes this and how can I fix it? I need to clear out the badge text, even if I manually close the popup window during those 2 seconds.
here is the code for the popup.js
// Listener calls background
document.addEventListener("DOMContentLoaded", function () {
backGround = chrome.extension.getBackgroundPage();
backGround.browserOnClickAxn(insertText);
});
// Set text
function insertText (msg) {
var message = "To find your saved tabs, look in the folder ";
message = message + "<br>";
message = message + "<strong>";
message = message + "\""+msg+"\"";
message = message + "</strong>";
message = message + "<br><br>";
message = message + " under ";
message = message + "<br>";
message = message + "<strong>";
message = message + "\"Other Bookmarks/BOOKMARKS/VARIOUS\"";
message = message + "</strong>";
message = message + "<br><br>";
document.getElementById('msg').innerHTML = message;
setBadgeText('OK!');
setTimeout(function () {
setBadgeText('');
window.close();
}, 2000);
}
function setBadgeText(text) {
chrome.browserAction.setBadgeBackgroundColor({
color: '#32cd32'
});
chrome.browserAction.setBadgeText({
text: text
});
}

NetSuite Online HTML Form - link to list within NetSuite

I have an online html form that uses a select field to choose a record from an existing record list (postcodes specifically) which then auto-populates other fields on the form with the rest of the address. This all works fine except that the dropdown list on the form only goes up so far. I need to know if this a limit on the html dropdown or how many records can be passed to the list in the first place?
Also, ideally, it would be great if there was a way to do it via auto-complete so as you start typing the postcode, it only shows those beginning with said characters - is this possible?
I would set up a custom html template for your online form. You can then hide your standard field and include a custom text input field. Attach an event handler to the custom field to do the lookup via a suitelet. If the auto-population is already working then your event handler can update the hidden standard field once a match is made so Netsuite's built-in sourcing works.
A sample suitelet. The get... functions return anything that can be JSON used by your page.
function service(request, response) {
var obj = {
success: true
};
try {
var step = request.getParameter('step') || 'start';
switch (step) {
case 'start':
obj.choices = getChoiceRoots(request.getParameter('itemid'), request.getParameter('treeName'));
break;
case 'other':
obj.choices = getChoiceChildren(request.getParameterValues('choiceIds[]') || request.getParameterValues('choiceIds'));
break;
default:
throw "Unexpected step: " + step;
}
} catch (e) {
obj.success = false;
obj.message = e.message || e.toString();
nlapiLogExecution('ERROR', "error getting choices", (e.message || e.toString()) + "<br> " + request.getURL() + (e.getStackTrace ? "<br> " + e.getStackTrace().join("<br> ") : ''));
}
_sendJS(request, response, obj);
function _sendJS(request, response, respObject) {
response.setContentType('JAVASCRIPT'); //'application/json');
var callbackFcn = request.getParameter("jsoncallback") || request.getParameter('callback');
if (callbackFcn) {
response.writeLine(callbackFcn + "(" + JSON.stringify(respObject) + ");");
} else response.writeLine(JSON.stringify(respObject));
}
}
Then on your page you'd have a script that uses the suitelet. (the suitelet needs to be available without login and the audience needs to be All.
The code on your custom template or associated script file would look something like:
//suiteletURL will look like https://forms.netsuite.com. RegExp makes that root relative to the domain you are on.
//This is not actually necessary when using JSONP but it can be useful in some circumstances so I left it in this example.
$.getJSON(suiteletURL.replace(new RegExp(".*://[^/]+/"), '/')+"&"+$.param(params) +"&callback=?",
function(d, txtStatus,xhr){
if(d.errorCode){
if(fail){
fail(xhr, txtStatus, null);
}else{
showMessage("#appMessage", d.errorMessage || ("Request Failed with code: "+xhr.responseJSON.errorCode));
}
}else{
success(d, txtStatus, xhr); // your custom success handler. d is already an object; no parsing necessary.
}
});

Is it possible to include client-side script using HtmlService.createHtmlOutput or must alternative methods be used?

I recently started using Google Docs Spreadsheet and have started trying to script things in the sheet. In one particular sheet, I wanted to display a custom dialog box to the user and I've managed to do that.
Now, I want to make some table rows in the dialog clickable. I wrote some code that should do the job, but Caja seems to be removing the code that I wrote.
Simplified version of the code (that still has the problem):
function showSkillsForCurrentLevel() {
var html, out, app;
html = '<div>Text. Yay!</div>'
+ '<script>'
+ '(function(){'
+ ' "use strict";'
+ ' var toggleFinished = function toggleFinished(){'
+ ' this.classList.toggle("finished");'
+ ' },'
+ ' rows,'
+ ' i = 0;'
+ ' rows = document.getElementsByClassName("skill-leveling").getElementsByClassName("finishable");'
+ ' if (rows.length) {'
+ ' for (i = 0; i < rows.length; i += 1) {'
+ ' rows[i].onclick = toggleFinished;'
+ ' }'
+ ' }'
+ '}());'
+ '</script>';
out = HtmlService.createHtmlOutput(html).setSandboxMode(HtmlService.SandboxMode.NATIVE).setTitle('Leveling Plan').setWidth(400).setHeight(400);
app = SpreadsheetApp.getActiveSpreadsheet();
app.show(out);
};
What I get in the displayed dialog is:
<caja-v-body>
<div>Text. Yay!</div>
<script></script>
</caja-v-body>
I assume that Caja is stripping the code for some reason but I cannot seem to find any documentation, or an example, anywhere of how to properly include scripts in Code.gs so that they remain intact post-Caja sanitization.
I assume others have run across this problem, and I'm hoping that someone will share a solution.
TLDR Version:
How do I include JavaScript in a dialog created by HtmlService.createHtmlOutput?
My approach is to create the dialog like this :
var htmlOutput = HtmlService.createHtmlOutputFromFile("customdialog.html").setTitle("your title").setWidth(350);
htmlOutput.setSandboxMode(HtmlService.SandboxMode.NATIVE);
DocumentApp.getUi().showDialog(htmlOutput)
The actual content of the dialog is in a separate HTML file called customdialog.html. Example implementation could be :
<html>
<head>
<script src="http://remoteserver/yourscript.js"></script>
<script>... a local script</script>
<body>
<div>hello dialog</div>
</body>
</html>
You can even include the javascript from a remote server, as the example demonstrates. I managed to include jquery and use jquery in the dialog with this approach.

Building an Omnibox extension for Google Chrome

I'm trying to build an Omnibox that matches what the user types against suggestions taken from an external data source, via jQuery. No luck so far.
In essence, the whole thing would function just like an autocomplete in jQuery, assuming this how the Omnibox is supposed to work (details are scarce, even in Google's Chrome developer resources).
Warning: I am not a proficient jQuery developer by any means, and most of what I've written is an exercise in brute force cut-paste trial and error until either success or exhaustion.
So far, I only have an example taken from an article by a user here, Ido Green:
chrome.omnibox.onInputChanged.addListener(
function(text, suggest) {
suggest([
{content: "CRM", description: " fetch the internal CRM"},
{content: "ERP", description: " fetch the internal ERP"},
{content: "sales", description: " fetch the lastest sales report"}
]);
}
);
chrome.omnibox.onInputEntered.addListener(
function(text) {
if (text.indexOf("/") < 1) {
text += "/";
}
if (text.indexOf("http") < 0) {
text = "http://our-internal-portal/" + text;
}
alert('We are taking you to: "' + text + '"');
navigate(text);
});
function navigate(url) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.update(tab.id, {url: url});
});
}
However, I cannot make this do anything else, despite the various other sample code examples I've found.
Firstly, I have a data source which I'm presently using for an autocomplete in jQuery, which is working fine, and returns the data in JSON format:
$(function(){
$('#link-bookmarks').autocomplete({
source: base_url + "bookmarks/jq_get_bookmarks_by_search_as_object/",
appendTo: ".bookmark-link-results",
open: function(event, ui) {
$('ul.ui-autocomplete#link-bookmarks')
.removeAttr('style')
.hide()
.appendTo('.bookmark-link-results')
.show();
$('#links-bookmarks').show();
},
select: function(event, ui) {
$('.bookmark-link-results')
.append('<div class="bookmark-link-box" id="bookmark-link-box-' + ui.item.bookmark_id + '"><input type="checkbox" id="bookmark-link-item-' + ui.item.bookmark_id + '" name="bookmark-links-add[]" value="' + ui.item.bookmark_id + '" checked="checked">' + ui.item.title + ' [View] [Link] <textarea class="comment" id="bookmark-link-comment-box-' + ui.item.bookmark_id + '" name="bookmark-link-comments[]"></textarea></div>');
}
}).data("autocomplete")._renderItem = function(ul, item) {
return $('<li></li>')
.data("item.autocomplete", item)
.append('<a>' + item.snippet + '</a>')
.appendTo(ul);
};
});
So I'd like to use that as the source. I know that needs to go in the onInputChanged method, but everything I've tried has so far failed.
If anyone has any ideas or Omnibox examples, that would help tremendously.

getting data from MySQL on jquerymobile only when I refresh the page

ok so I'm trying to load data and move to another page once I'm clicking on a search button in my index.html
this is my search button
<a href="results.html" data-role="button" data-icon="search"
data-iconpos="notext">search</a>
and while it's loading I want the page to run this function and get data
$(function () { $.getJSON("API.php", {
command: "getBusiness",
orig_lat: myPos.lat,
orig_long: myPos.lon,
distance: 0.05 },
function (result) {
$("#locations").html("");
for (var i = 0; i < result.length; i++) {
$("<a href='business.html?ID=" + result[i].id + "&bsnName=" + "'>
<div>" + result[i].bsnName + " " + (parseInt(result[i].distance * 1000))
"</div></a>").appendTo("#locations");}});});
The page is loading without the DB only when I hit refresh it's showing me the result
I'm not sure what's wrong here, should I not use getJSON?? I have seen people talking about .Ajax() is it the same as getJSON() ?
is there a better idea on how to move to another page and simultaneously grab data from DB to the page your going to load on jquerymobile?
I tried to use the same function using onclick it worked when I gave it a div
the rest of the head
<link rel="stylesheet" href="styles/jquery.mobile.structure-1.1.0.min.css" />
<link rel="stylesheet" href="styles/jquery.mobile.theme-1.1.0.min.css" />
<link rel="stylesheet" href="styles/my.css" />
<script src="scripts/jquery-1.7.2.min.js"></script>
<script src="scripts/jquery.mobile-1.1.0.min.js"></script>
<script src="scripts/cordova-1.8.1.js"></script>
<script>
// Wait for Cordova to load
//
document.addEventListener("deviceready", onDeviceReady, false);
var watchID = null;
var myPos = { lat: 32.0791, lon: 34.8156 };
// Cordova is ready
//
function onDeviceReady() {
// Throw an error if no update is received every 30 seconds
var options = { timeout: 10000 };
watchID = navigator.geolocation.watchPosition(onSuccess, onError, options);
}
// onSuccess Geolocation
//
function onSuccess(position) {
var element = document.getElementById('geolocation');
//myPos.lat=position.coords.latitude;
//myPos.lon=position.coords.longitude;
element.innerHTML = 'Latitude: ' + position.coords.latitude + '<br />' +
'Longitude: ' + position.coords.longitude + '<br />' +
'<hr />' + element.innerHTML;
}
// onError Callback receives a PositionError object
//
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
Basically when jQuery mobile loads first or index page it load whole head section (Javascript, CSS etc) and body section. but When the user clicks a link in a jQuery Mobile-driven site, the default behavior of the navigation system is to use that link's href to formulate an Ajax request (instead of allowing the browser's default link behavior of requesting that href with full page load).When that Ajax request goes out, the framework will receive its entire text content, but it will only inject the contents of the response's body element.
There can be multiple solutions to this problem e.g.
The simplest approach when building a jQuery Mobile site is to reference the same set of stylesheets and scripts in the head of every page.
Linking without Ajax by using an attribute data-ajax="false" in your link this attribute will load the next page without ajax and animation so both head and body section would load.
If you need to load in specific scripts or styles for a particular page, It is recommended binding logic to the pageInit e.g. "#aboutPage" is id="aboutPage" attribute .
$( document ).delegate("#aboutPage", "pageinit", function() {
//you can place your getJson script here. that will execute when page loads
alert('A page with an ID of "aboutPage" was just created by jQuery Mobile!');
});
So in your case better solution is to bind your ajax call or other particuler script with pageinit event.
You can get help from these pages of jQuery Mobile documentation.
http://jquerymobile.com/demos/1.1.0/docs/pages/page-links.html
http://jquerymobile.com/demos/1.1.0/docs/pages/page-scripting.html