How to Inject strings into tinyMCE from a Chrome Extension? - google-chrome-extension

My background_script.js sends a message such as this:
function genericOnClick(info, tab) {
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, {message: 'insert_string'}, function(){} );
});
};
The receiver.js catches this as:
function insert_string() {
var field = document.activeElement;
if(field.tagName == "IFRAME") {
field = field.contentDocument.activeElement;
}
field.value += 'This is my string';
}
Now, the extension works perfectly well on regular editable fields and textareas (it even works properly in tinyMCE on the textarea-tab!) but in the case of Visual-tab of tinyMCE I can't get this to work. I have noticed that the Visual-tab, as it's a WYSIWYG editor, is special and the only way I so far have figured out on how to solve this issue would be to mimic tinyMCE's behaviour for updating the Visual-tab. However, I would like to know if there's something simple and obvious I've missed. If not, how would I go about editing the Visual-tab contents?

All you need to issue to fill the editor is
tinymce.get('your_editor_id').setContent('This is my string');

Related

Cannot set input field value with Element.value [duplicate]

My extension has a context menu with items. What I'd like it to do: is when I right-click an editable html element (eg input or textarea) and then select and click on an item in my menu - some value defined by my extension gets entered into the input.
For now I have realised that with document.activeElement.value = myValue.
With simple inputs it works alright.
Problems start when there is an input with custom onChange event handling, eg a calendar or a phone input, or currency input - that transforms user-input in some way.
Since I am setting a value directly onto the element - the handling logic gets omitted, which causes all manner of problems.
Since javascript doesn't allow for KeySend-like features - what are my options here?
I have thought about testing tools like Puppeteer or Cypress - but they all seem not to be packageable into an extension. Puppeteer does have such an option, but it still requires a node instance running to connect to. And I would like my extension to be solely client-sided and distributed in Chrome webstore - so I cannot ask my users to spin up a node server.
There is a built-in DOM method document.execCommand.
In case of an extension, use this code in the content script.
// some.selector may be `input` or `[contenteditable]` for richly formatted inputs
const el = document.querySelector('some.selector');
el.focus();
document.execCommand('insertText', false, 'new text');
el.dispatchEvent(new Event('change', {bubbles: true})); // usually not needed
It imitates physical user input into the currently focused DOM element so all the necessary events will be fired (like beforeinput, input) with isTrusted field set to true. On some pages the change event should be additionally dispatched as shown above.
You may want to select the current text to replace it entirely instead of appending:
replaceValue('some.selector', 'new text');
function replaceValue(selector, value) {
const el = document.querySelector(selector);
if (el) {
el.focus();
el.select();
if (!document.execCommand('insertText', false, value)) {
// Fallback for Firefox: just replace the value
el.value = 'new text';
}
el.dispatchEvent(new Event('change', {bubbles: true})); // usually not needed
}
return el;
}
Note that despite execCommand being marked as obsolete in 2020, it'll work in the foreseeable future because a new editing API specification is not finished yet, and knowing how slow such things usually move it may take another 5-20 years.
#wOxxOm, thank you very much !
I used your code solved my problem which has bothered me for long time. I googled many code and article for nearly one month.
It works on Facebook and many strong website.
Because execCommand has depredated, I try below code it works well, include Facebook.
function imitateKeyInput(el, keyChar) {
if (el) {
const keyboardEventInit = {bubbles:false, cancelable:false, composed:false, key:'', code:'', location:0};
el.dispatchEvent(new KeyboardEvent("keydown", keyboardEventInit));
el.value = keyChar;
el.dispatchEvent(new KeyboardEvent("keyup", keyboardEventInit));
el.dispatchEvent(new Event('change', {bubbles: true})); // usually not needed
} else {
console.log("el is null");
}
}
The following code can only work on ordinary websites, but it is invalid for strong website.
function fireKeyEvent(el, evtType, keyChar) {
el.addEventListener(evtType, function(e) {el.value += e.key;}, false);
el.focus();
const keyboardEventInit = {bubbles:false, cancelable:false, composed:false, key:keyChar, code:'', location:0};
var evtObj = new KeyboardEvent(evtType, keyboardEventInit);
el.dispatchEvent(evtObj);
}

Leaflet Control Search: open Popup for search result

I'm using the wonderful plugin Leaflet.Control.Search in order to search for markers (from a geoJson marker group) on my map – which works great.
I only have one simple question now:
how can I open a popup for the search result marker?
I'm using custom marker icons with popups (which open on click) already bound to them – but I would like to open the respective popup automatically once it has been found via the search.
My code looks like this:
var searchControl = new L.Control.Search({layer: markers2, propertyName: 'Name', circleLocation:true});
searchControl.on('search_locationfound', function(e) {
e.layer.bindPopup(feature.properties.Name).openPopup();
}).on('search_collapsed', function(e) {
markers2.resetStyle(layer);
});
map.addControl( searchControl ); //inizialize search control
and thought it might work with that line:
e.layer.bindPopup(feature.properties.Name).openPopup();
but unfortunately it doesn't.. ;)
–
Oh, and a second question: at the moment I'm searching only in 1 geoJson layer ("markers2") – does anyone know whether it's possible to search in multiple layers at once?
Any suggestions? I'd be grateful for any help, thanks in advance!
got it: it works like this: e.layer.openPopup().openOn(map);
event.layer is set only for preloaded layer, if you search marker by ajax,jsonp or callData.. event.layer is undefined.
var geojsonLayer = new L.GeoJSON(data, {
onEachFeature: function(feature, marker) {
marker.bindPopup(feature.properties.name);
}
});
map.addLayer(geojsonLayer);
var controlSearch = new L.Control.Search({layer: geojsonLayer, initial: false});
controlSearch.on('search_locationfound', function(event) {
event.layer.openPopup();
});
Look at GeoJSON demo:
https://opengeo.tech/maps/leaflet-search/examples/geojson-layer.html
Recently, I was looking for an answer, and here is my solution for it
searchControl.on("search:locationfound", function (e) {
if (e.layer._popup) e.layer.openPopup();
});

click to replace value, shift + click to append value using jquery

I have a list with items.
When I click any of these items, I copy its id-value into a form text-field.
Everytime I click, it replaces the value, which is correct by default. But what I would like to add, is a way for the user to hold down a key on their keyboard, and when they then click, they just .append whatever they just clicked into the same form field.
Here's my jQuery-code I'm using for the first/default scenario:
$(function(){
$('ul#filter-results li').click(function(){
var from = $(this).attr('id'); // get the list ID and
$('input#search').val(from+' ').keyup(); // insert into text-field then trigger the search and
$('input#search').focus(); // make sure the field is focused so the user can start typing immediately
});
});
Is there a way to implement some sort of keyboard key-listener?
Something like:
if (e.shiftKey){
.append('this text instead')
}
haven't tried out to see if shiftKey is even any valid name here
shiftKey is of one of the properties of the event object and is valid to be used. try this:
$(document).on('keyup click', function(e){
if (e.shiftKey) {
$('input#search').focus()
$('input#search').val(e.target.id)
}
})
DEMO
$('ul#filter-results').on('click', 'li', function(e) {
if(e.shiftKey) {
do something;
} else {
do something else;
}
});
There is a jQuery plugin for extended click.
You could try that or see how they have done it and implement it yourself.
ExtendedClick plugin
Hope this helps.
This is what I ended up with:
I switched over to altKey because shiftKey marked a lot of text when I clicked.
Didn't do anything besides it doesn't look good...
$(function(){
$('ul#filter-results li').click(function(e){
var text = $(this).attr('id'); // get the ID
var input = $('#search'); // form field to insert text into
if (e.altKey){ input.val(input.val()+', '+text+' ').keyup(); } // fetch whatever is already there, and add some more
else { input.val(text+' ').keyup(); } // just replace whatever is already there
$('#search').focus();
});
});
Thanks for good suggestions...

How do I store values for specific tab for Chrome, and Safari extensions?

I am developing an extension for all the browsers. How do I store tab specific values in the session? I solved this problem in Firefox with an NSISessionStore object. In Safari and Google Chrome, I used SessionStorage; this object stores values for a specific tab with a specific domain. I want a solution for how to store values for a specific tab.
If you're asking how to manage data throughout the life of a tab you can simply create an object for the tab when it's created and delete it when it is closed.
// Create data store
var tabDataStore = {};
// Create listeners
chrome.tabs.onCreated.addListener(function (tab) {
tabDataStore['tab_' + tab.id] = {
urls: []
};
});
chrome.tabs.onRemoved.addListener(function (tabId) {
delete tabDataStore['tab_' + tabId];
});
// Save something against that tab's data
function saveUrl(tab) {
tabDataStore['tab_' + tab.id].urls.push(tab.url);
}
// Load something from tab's data
function loadOriginalUrl(tab) {
tabDataStore['tab_' + tab.id].urls[0];
}
However, this is all an assumption and you may want something completely different. Also, it depends when and what exactly you want to store.
Further information on tabs can be found in the official documentation.
If you want to persist anything you can use localStorage.
a simple way to do it, though not ideal as it would look messy would be to store the values in the URL hash
say the URL of the tab was http://whatever.com/
you could store the value in the hash like so
http://whatever.com/#value1=12&value2=10&value3=15212
there will also be a problem if the website uses the hash object for anyhting such as "in page" anchors, or ajaxy type stuff
Safari Answer
In your global page save directly to the tab.. so for instance on message from injected script
// global page
safari.application.addEventListener("message", function(event){
switch(event.name){
case "saveData":
event.target.page.tabData = { data: myData }
break;
case "getData":
event.target.page.dispatchMessage("tabData", myData);
break;
}
}, false);
-
// injected page
// first save data
safari.self.tab.dispatchMessage("saveData", {firstname:"mike", age: 25} );
// setup listner to recevie data
safari.self.addEventListener("message", function(event){
switch(event.name){
case "tabData":
// get data for page
console.debug(event.message);
// { firstname: "mike", age: 25 }
break;
}
}, false);
// send message to trigger response
safari.self.tab.dispatchMessage("getData", {} );

Chrome Extension iframe dom reference disqus

How do I get a reference to the dom of a cross domain iframe/frame?
I want to do some stuff to disqus comments with an extension.
My manifest has the following:
"all_frames": true,
"matches": ["*://*.disqus.com/*","*://disqus.com/*", "http://somesite.com"]
I am not trying to communicate outside of the frame - that is the js will take care of the work without needing to 'tell' me anything.
all_frames should inject the listed js files into every frame, no?
When I do this:
if (window != window.top){
alert('In an IFRAME: ' + window.location.href);
}
...I get the expected disqus URLs.
But when I do this:
var btnCommentBlock = document.getElementsByClassName('dsq-comment-buttons');
alert('btnCommentBlock length: ' + $(btnCommentBlock).length);
...I get 0 for length.
I updated my answer to Javascript to access Disqus comment textbox?
Basically, Disqus changed the selector. They no longer use textarea, they use contenteditable divs.
Something like this should work:
// We just need to check if the IFrame origin is from discus.com
if (location.hostname.indexOf('.disqus.com') != -1) {
// Extract the textarea (there must be exactly one)
var commentBox = document.querySelector('#comment');
if (commentBox) {
// Inject some text!
commentBox.innerText = 'Google Chrome Injected!';
}
}
Source Code:
https://gist.github.com/1034305
Woohoo! I found the answer on github:
https://gist.github.com/471999
The working code is:
$(document).ready(function() {
window.disqus_no_style = true;
$.getScript('http://sitename.disqus.com/embed.js', function() {
var loader = setInterval(function() {
if($('#disqus_thread').html().length) {
clearInterval(loader);
disqusReady();
}
}, 1000);
});
function disqusReady() {
//whatever you can imagine
}
});
I put this in the disqusReady() function:
var aTestHere = document.getElementsByClassName('dsq-comment-body');
alert(aTestHere[0].innerHTML);
...and got back the innerHTML as expected.
Mohamed, I'd really like to thank you for taking the time to interact with my question. If you hadn't posted that link to github there's no telling when if ever I'd have figured it out or found the other code.
edit: After a few minutes of experimenting it looks like it is not necessary to call getScript so you should be able to comment that out.
Also unnecessary is window.disqus_no_style so I commented that out too.
I'll experiment some more and update the answer later. One of those two things prevented me from being able to actually post a comment at the disqus site I use. //them out still allows access to the dom and the ability to post.

Resources