Unable to add an Input element using Tampermonkey on Frontend Mentor - greasemonkey

Goal: I'm trying to add an input element on Frontend Mentor to sort/search solutions.
Problem: The input element appears on the page but disappears after a second.
Code:
// ==UserScript==
// #name FEM
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author bunee
// #match https://www.frontendmentor.io/solutions
// #icon https://www.google.com/s2/favicons?domain=frontendmentor.io
// #grant none
// ==/UserScript==
(function() {
'use strict';
window.addEventListener("load", () => {
const inputElement = document.createElement("INPUT");
inputElement.setAttribute("type", "text");
const navBar = document.querySelector(".NavigationSecondary__Wrapper-sc-13y3yjk-0>.wide-container");
navBar.append(inputElement);
console.log(inputElement);
}, false);
})();
Here's where I'm trying to add it.
If you can, Please run this script and let me know how I'm wrong.

Seems like some dynamic shenanigangs on the website delete your addition when it happens too early.
Have you tried just delaying it with a timeout?
Something like
// ==UserScript==
// #name FEM
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author bunee
// #match https://www.frontendmentor.io/solutions
// #icon https://www.google.com/s2/favicons?domain=frontendmentor.io
// #grant none
// ==/UserScript==
setTimeout(function(){
const inputElement = document.createElement("INPUT");
inputElement.setAttribute("type", "text");
const navBar = document.querySelector(".NavigationSecondary__Wrapper-sc-13y3yjk-0>.wide-container");
//navBar.append(inputElement);
navBar.insertBefore(inputElement, navBar.lastChild);
console.log(inputElement);
}, 2000);
just adapt it as needed.
P.S.
navBar.append(inputElement) adds your new element to the end while
navBar.insertBefore(inputElement, navBar.lastChild); adds it in the middle as per your question.

Related

share data between 2 greasemonkey scripts running on different pages

with Firefox 68 and Greasemonkey 4.9, i want to set a value from a script on a webpage and get that same value from another script on another webpage. it seems not working. How can i do that ? Here is what i tried :
script 1
// ==UserScript==
// #name My EDIT
// #version 1
// #match http://one.local/stuff/edit*
// #grant GM.setValue
// #grant GM.getValue
// ==/UserScript==
(async () => {
let count = await GM.getValue('count', 0);
await GM.setValue('count', count + 1);
console.log(count);
})();
script 2
// ==UserScript==
// #name My VIEW
// #version 1
// #match http://www.two.local/view
// #grant GM.getValue
// ==/UserScript==
(async () => {
let count = await GM.getValue('count', 0);
console.log(count);
})();
even if values are incremented when i visite http://one.local/stuff/edit many times, i can't get those when visiting http://www.two.local/view (it remains 0 !
Any good script manger should NOT allow script A access script B storage as it would be a serious security breach.
You can combine the scripts into one script, that runs on both pages. That way the storage would be shared.
Simple example:
// ==UserScript==
// #name Combined
// #version 1
// #match http://one.local/stuff/edit*
// #match http://www.two.local/view
// #grant GM.setValue
// #grant GM.getValue
// ==/UserScript==
(async () => {
// code for one.local
if (location.hostname === 'one.local') {
const count = await GM.getValue('count', 0);
await GM.setValue('count', count + 1);
console.log(count);
}
// code for www.two.local
else if (location.hostname === 'www.two.local') {
const count = await GM.getValue('count', 0);
console.log(count);
}
})();

Userscript works in Tampermonkey on Chrome and Firefox, but doesn't work in Greasemonkey on Firefox

This script works just fine in Tampermonkey on both Chrome and Firefox, but doesn't work in Greasemonkey on Firefox. When I say it doesn't work I mean the button does not show up on the page. I'm using Chrome 61.0.3163.79 and Firefox 52.3.0.
The firefox console shows this warning but I'm not sure if it's relevant:
unreachable code after return statement[Learn More] jquery-1.3.2.min.js
Here's the code. Using greasemonkey on Firefox, I see "create button" in the console, but not "button has been created", which makes me think something is going wrong with the button creation. Any help greatly appreciated!
// ==UserScript==
// #name MyScript
// #namespace MyNS
// #description Adds a button to insert text into 2 text boxes
// #version 0.1
// #include *Removed for confidentiality*
// #compatible Greasemonkey
// ==/UserScript==
var descriptionText = "myDescription";
var testingText = "myTesting";
// Check if jQuery's loaded
function GM_wait() {
if (typeof unsafeWindow.jQuery == 'undefined') { window.setTimeout(GM_wait,100); }
else { $ = unsafeWindow.jQuery; init(); }
}
// All your GM code must be inside this function
function init() {
var description = $('#description');
var testingDone = $('#testing_done');
function insertText(template, editor) {
if (template) {
if (!editor._editing) {
editor.startEdit();
}
editor._field.queue(function() {
var oldVal = editor._field.val();
editor._field.val(template + (oldVal ? "\n" : "") + oldVal);
editor._field.keyup();
editor._field.dequeue();
});
}
}
function createButton(editor) {
console.log("create button:");
editor._insertTextButton = $('<input/>')
.attr('type', 'button')
.attr('value', 'Insert CR Template')
.css('margin-left', '1em')
.click(function() {
insertText(testingText, testingDoneEditor);
insertText(descriptionText, descriptionEditor);
});
console.log("button has been created");
editor._insertTextButton.insertAfter(editor._editIcon);
}
descriptionEditor = description.data('inlineEditor');
testingDoneEditor = testingDone.data('inlineEditor');
createButton(descriptionEditor);
}
GM_wait();

How to replace, remove or intercept jQuery's ready event from a Greasemonkey script? [duplicate]

This question already has answers here:
Changing Javascript on an HTML page out of my control [duplicate]
(2 answers)
Closed 4 years ago.
There's a website I use written in some mighty fine javascript. Hardly any globals, closures everywhere and it uses strict mode. This is making it really hard to inject my own functionality into the website.
The website client objects are initialised in a jQuery.ready() call:
$(window).ready(function () {
var a, b, c, d;
// Setup global data [...]
// Setup configuration [...]
a = GlobalFoo.ConstructorA();
b = GlobalFoo.ConstructorB(a);
// Really wish I could put stuff here
c = GlobalFoo.ConstructorC(a, b);
d = GlobalFoo.ConstructorD(b, c);
// etc.
});
How can I, for example, replace b.someMethod() with my own code before the other constructors are called?
Can I stop the ready event from happening or replace it with my own code? Since it's quite small I can just duplicate a modified version in my code.
After a bit more searching I found this wonderful page by dindog. There is also this page on the GreaseSpot wiki describing #run-at.
#run-at allows your userscript to run before all other code. The beforescriptexecute event allows you to check each script before it executes. You can then skip or modify it.
My final solution is:
// ==UserScript==
// #name ...
// #description ...
// #namespace ...
// #include ...
// #version 1
// #run-at document-start
// #grant none
// ==/UserScript==
(function (w) {
// Remove the current jQuery ready code
function pre_script_execute_handler (e) {
var target = e.target;
if (target.innerHTML.indexOf("$(window).ready(") != -1) {
console.log('Removed ready');
e.preventDefault();
e.stopPropagation();
addReady();
}
}
w.addEventListener('beforescriptexecute', pre_script_execute_handler);
// Add new jQuery ready code
function addReady () {
console.log('Adding new ready');
w.$(window).ready(function () {
console.log('Our ready called');
});
}
}) (unsafeWindow);

Accessing Marionette Apps from Views (using Require.js)

I'm trying to share a Marionette App with some of its Views. I've read the wiki here, but the example leaves me with a question.
I've got a file with a couple of views in it that will all need to use the request/response system and possibly the commands. I don't want to do var MyApp = require('app'); in all of the Views in the file. I came up with the following, but I think there's probably a better way to do it.
Example:
//Views.js
define( ["marionette"], function (Marionette) {
var App = function(){
return require('app');
};
var ExampleItemView = Marionette.ItemView.extend({
initialize: function(){
App().request("getInfo", "aboutStuff");
}
});
return Marionette.CollectionView.extend({
itemView: ExampleItemView,
initialize: function(){
App().request("getInfo", "aboutStuff");
}
});
Is there a better way to do this?
I'd definitely not inject your app into your views since it can create circular dependencies which are ALWAYS a code smell (regardless of whether they can be solved or not) The simples solution by far is to create a separate (singleton) reqres object which is handled by the app and injected into the views.
//reqres.js
define(['backbone.wreqr'], function( Wreqr ){
return new Wreqr.RequestResponse();
});
//app
define(['reqres'], function(reqres){
reqres.setHandlers({
'getInfo' : function(){
return 'foo';
}
});
});
//Views.js
define( ["marionette", "reqres"], function (Marionette, reqres) {
var ExampleItemView = Marionette.ItemView.extend({
initialize: function(){
reqres.request("getInfo", "aboutStuff");
}
});
return Marionette.CollectionView.extend({
itemView: ExampleItemView,
initialize: function(){
reqres.request("getInfo", "aboutStuff");
}
});
Backbone Wreqr is to be replaced by Backbone Radio in the next major release of Marionette, v3.
To use Backbone Radio you could create a channel and do the following:
/**
* App.js (for example)
*/
var Radio = require('backbone.radio');
// Use the 'data' channel - this could be whatever channel name you want
var dataChannel = Radio.channel('data');
// Handler for a request
dataChannel.reply('getMessage', function(name) {
return 'Hello ' + name + '. Alba gu brath!';
});
/**
* View.js (for example)
*/
var Radio = require('backbone.radio');
var dataChannel = Radio.channel('data');
// Make the request
console.log( dataChannel.request('getMessage', 'Craig') ); // -> Hello Craig. Alba gu brath!

Remove MooTools events using a Greasemonkey script?

I'm trying to modify a page that uses MooTools to add event-listeners to some fields, like so:
$('inputPassword').addEvents({
keypress: function(){
new WorkspaceModalbox({'href':'someurl.phtml', 'width':500, 'height':140, 'title':'foo'});
}
});
I need to remove this behavior using Greasemonkey/Tampermonkey. I tried:
// ==UserScript==
// #require http://userscripts.org/scripts/source/44063.user.js
// ==/UserScript==
window.addEventListener("load", function(e) {
$('inputPassword').removeEvents('keypress');
}, false);
where removeEvents is a function from MooTools, the opposite one to addEvents.
But the script doesn't work. (Editor's note: There are no reported errors)
Why?
Is it because my code is executed before the code from the real page?
The event was installed in page scope but the script is running in script scope. Also, depending on the browser and on the #grant settings, there may be a sandbox involved.
So, to remove that event, you must use script injection (Moo tools doesn't seem to play nice with unsafeWindow.)
This script works on both Greasemonkey and Tampermonkey:
// ==UserScript==
// #name _Remove a Moo event listener
// #include http://YOUR_SERVER.COM/YOUR_PATH/*
// #grant GM_addStyle
// ==/UserScript==
/*- The #grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
window.addEventListener ("load", function (e) {
addJS_Node ("$('inputPassword').removeEvents('keypress');");
}, false);
//-- addJS_Node is a standard(ish) function
function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
var D = document;
var scriptNode = D.createElement ('script');
if (runOnLoad) {
scriptNode.addEventListener ("load", runOnLoad, false);
}
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
targ.appendChild (scriptNode);
}
Note that there is no need to #require-in Moo tool just for this, since the page's instance of Moo tools must be the one to remove the event(s).

Resources