$ is not a valid function in a Chrome Extension? - tampermonkey

So I'm going to publish my Tampermonkey userscript (It's a button to get you to a random link in the list) to the Chrome Web Store, I have been testing it today and I got an error:
Uncaught TypeError: $ is not a function
at Chain Finder.user.js:258
at Chain Finder.user.js:261
If you want to take a look at my script:
(function() {
const $ = window.$;
var randomLink = function () {
// Beggining of target list
var links = [
"torn.com/profiles.php?XID=2410074",
"torn.com/profiles.php?XID=2393322",
"torn.com/profiles.php?XID=2049797",
"torn.com/profiles.php?XID=2268673",
"torn.com/profiles.php?XID=2059647"
// More links.... but I cut them so it's easier to understand.
];
// End of target list
// by counting the number of links in the array
var max = (links.length)
// now generate a random number
var randomNumber = Math.floor(Math.random()*max);
// use that random number to retrieve a link from the array
var link = links[randomNumber];
// change the location of the window object
return "https://" + link;
}
// Opens a new tab.
function openInNewTab(url) {
var win = window.open(url, '_blank');
win.focus();
}
function main() {
$('.buttons-list').append(`<a id="mc-btn" href="#" class="profile-button" style="width:99px;text-align:center">
<img src='https://i.imgur.com/TQdk3Pp.png'>
</a>`);
$('#mc-btn').on('click', () => openInNewTab(randomLink()));
}
//Here's the error:
$(document).ready(() => {
main();
});
})();
If you could tell me what's wrong in there, I will be very grateful.

Make sure that somewhere in your script that you include these two lines of code, put var $ = window.jQuery; above where you code starts, and put // #require http://code.jquery.com/jquery-3.4.1.min.js somewhere up top with the rest of the // lines.
Example:
// ==UserScript==
// #name Example
// #namespace http://tampermonkey.net/
// #version 0.1
// #description
// #author Username
// #require http://code.jquery.com/jquery-3.4.1.min.js
// #grant GM_addStyle
// #grant GM.listValues
// #grant GM_setValue
// #grant GM_getValue
// #match *
// #match https://*/*
// #match http://*/*
// #match *://*/*
// ==/UserScript==
(function() {
'use strict';
// Your code here...
})();
var $ = window.jQuery;

That error is usually displayed if Jquery is not included in the script.

Related

Multiple urls Tampermonkey

For the first time in my live i make a Tampermonkey script.
I want add multiple url's to a page: https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
But i don't know how to get all different licenseplates for the custom urls. He now take the first licenseplate div.
url must be: https://finnik.nl/kenteken/XX-XX-XX/gratis
As you can see, all urls have the same licenseplate
// ==UserScript==
// #name New Userscript
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author You
// #match https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
// #icon https://www.google.com/s2/favicons?sz=64&domain=voertuig.net
// #grant none
// ==/UserScript==
(function() {
"use strict"
// Your code here...
var adduserinfo = $("div.voertuig > a");
var Username = $("div.kenteken");
var words = Username.html().split(' ');
adduserinfo.each (function () {
var jThis = $(this);
jThis.parent ().append ('Finnik ' + words + ' ');
} );
})()
Something like this should do it.
Note that you need to modify the //#match line or it will only work for the given page (i.e. page 3 -- just end the line with a * at the point where the match can be wild-carded.
// ==UserScript==
// #name voertuig.net SO EXAMPLE
// #namespace http://tampermonkey.net/
// #match https://voertuig.net/zoek/merk/volkswagen/datum-tenaamstelling/30-05-2018?pagina=3
// #grant none
// ==/UserScript==
(function() {
'use strict';
//Look closely - this is not jQuery
const $ = document.querySelector.bind(document);
const $$ = document.querySelectorAll.bind(document);
$('body').insertAdjacentHTML('beforeend', init_css() );
$$('.voertuig').forEach((div) => {
const lp = div.getAttribute('data-kentekenplaat');
div.insertAdjacentHTML('beforeend', `<a target='_blank' class='finTagA' href='https://finnik.nl/kenteken/${lp}/gratis#historie'>Finnik ${lp}</a>`);
});
})();
function init_css(){
return `
<style id="jdInitCss">
.finTagA{width:100%;padding:5px;display:flex;justify-content:center;background:white;border-top:1px solid #ccccccCC;}
.finTagA:hover{color:blue;}
</style>
`;
}

Gulp-eslint throws errors on dynamically loaded JSs

I have a project structure like
There are approx 10 JS files in com. lab1 and lab2 has a config.json file which tells, out of 10 files which files to be concatenated and placed as app-min.js in dist/lab1 or dist/lab2.
In the gulp file I've created something like this.
var filesArr = [];
var labName;
// Player Task
gulp.task('player', function () {
return gulp.src(filesArr)
.pipe(eslint())
.pipe(babel())
.pipe(concat('app-min.js'))
.pipe(uglify({
compress: {
drop_console: true
}
}).on('error', gutil.log))
.pipe(gulp.dest('dist/' + labName));
});
// Clean
gulp.task('clean', function () {
if (readJson()) {
return del([
'dist/' + labName
]);
}
return null;
});
// Watch
gulp.task('watch', function () {
gulp.watch(filesArr, gulp.series('player'));
});
// Read Json and create JS Array
function readJson() {
// LAB STRUCTURE
var _n = prompt('Specify the LAB name. ');
labName = _n;
var _path = path.resolve('./src/' + _n);
var _exists = fs.existsSync(_path);
if (_exists) {
var _json = fs.readFileSync(path.resolve(_path + '/labstructure.json'), 'utf-8');
var _jObj = JSON.parse(_json).labObj.components;
for (var i = 0; i < _jObj.length; i++) {
var _jsName = 'src/com/component/' + _jObj[i].ref + '.js';
if (filesArr.indexOf(_jsName) === -1) {
filesArr.push(_jsName);
}
}
}
return _exists;
}
gulp.task('default', gulp.series('clean', 'player', 'watch'));
Here the filesArr looks like:
[ 'src/com/component/ColorActClass.js',
'src/com/component/PanelCompClass.js',
'src/com/component/ToggleCompClass.js',
'src/com/component/SliderCompClass.js',
'src/com/component/CheckBoxCompClass.js',
'src/com/component/ButtonCompClass.js',
'src/com/component/LabelCompClass.js',
'src/com/component/InputBoxClass.js',
'src/com/component/ColorMonitorClass.js',
'src/com/component/MsgBoxClass.js',
'src/com/component/ConfBoxClass.js',
'src/com/component/NumberPadClass.js',
'src/com/main/lib/webfontloader.js',
'src/com/main/lib/howler.core.min.js',
'src/com/main/PlayerClass.js',
'src/kl1001_color/BrainClass.js' ]
This works perfectly fine at the first place. But when any JS is modified then in watch player task throws eslint error on some files which are untouched. This doesn't happen always rather if watch is running for 10-20 mins then it throws error. Like this:
In this case CheckBoxCompClass.js is not the file which is modified, but still got the issue. On top of that, the semicolon is in place. If this file has issue then eslint should have thrown the error at the first place.
Please help.
Accidentally, my NVM was set to an older version. Solved the issue after updating the NVM and by setting the current NVM version to the latest one.

2 way binding issues with directives, controllers and services

This is bugging me a bit.
I have a service that handles logo panels and a function that is used to navigate between the different panels.
When getPanels is invoked it sets the currentPanel, index and length on the service when all promises have completed (see $q.all in the getPanels method):
.service('ConfiguratorLogoService', ['$q', 'UploadService', 'LogoService', 'ArrayService', 'SvgService', function ($q, uploadService, logoService, arrayService, helper) {
// Private function to build a file array
var _buildFileArray = function (panels, files) {
//--- Omitted for brevity ---//
};
// Create our service
var service = {
// Create our arrays
panels: [],
files: [],
currentPanel: null,
index: 0,
length: 0,
// Get our panels
getPanels: function (container, garmentId) {
// Create a deferred promise
var deferred = $q.defer();
// Create our arrays
var panels = []
files = [],
promises = [];
// If we have a container
if (container) {
// Get the containers children
var children = container.children()
// Loop through our panel's children
for (var i = 0; i < children.length; i++) {
// Get the current child
var child = angular.element(children[i]),
childId = child.attr('id'),
childTitle = helper.extractText(childId, ':', 1);
// Create our item
var panel = {
id: childId,
title: childTitle
};
// Try to get our item
promises.push(logoService.get(garmentId, panel.id).then(function (response) {
// If we have any data
if (response) {
// Add the file to our array
files.push(response);
}
}));
// Add our child to the array
panels.push(panel);
}
}
// After all the promises have been handled
$q.all(promises).then(function () {
// Get our files
service.files = _buildFileArray(panels, files);
service.panels = panels;
service.currentPanel = panels[0];
service.length = panels.length;
// Resolve our promise
deferred.resolve({
files: service.files,
panels: panels
});
});
// Return our promise
return deferred.promise;
},
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
// Get our previous panel
navigatePrevious: function () {
// Get the previous index or set to the end of our list
service.index = service.index === 0 ? service.length - 1 : service.index -= 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
},
// Removes the file from azure
remove: function (index) {
//--- Omitted for brevity ---//
}
};
// Return our service
return service;
}])
which is fine, it works and the first panel is selected.
So, I have a controller, which is attached to a directive. The controller looks like this:
.controller('ConfiguratorLogosDirectiveController', ['ConfiguratorLogoService', 'RowService', function (service, rowService) {
var self = this;
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
// Initialization
self.init = function (container, garmentId) {
// Get our panels
return service.getPanels(container, garmentId).then(function (response) {
self.panels = response.panels;
self.files = response.files;
// If we have any panels
if (self.panels.length) {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
}
// Return our response
return response;
})
};
// Map our service functions
self.upload = service.upload;
self.next = service.navigateNext;
self.previous = service.navigatePrevious;
self.remove = service.remove;
}])
As you can see, when I get my panels, I set the currentPanel, index and length on the controller itself which I didn't think I would have to do because when the controller is invoked, it already has a reference to the service values. I figured 2 way binding would come into play and when the service values update, the controller would update too.
Anyway, I update the values after the getPanels method completes successfully. In my directive I have this:
// Invoke on controller load
controller.init(container, scope.garmentId).then(function (response) {
// Map our properties
scope.panels = controller.panels;
scope.files = controller.files;
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
which again works fine. In my template I can see the first panel and it looks fine.
So, then came the next step which was my navigate functions. I started with next which I have modified for testing purposes so I can output the controller.index as well as the console.log in the service navigation function.
The directive function looks like this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
});
};
When this method is invoked, I can see in my console that the service increases the index by 1 but the controller still shows 0 which means that 2 way binding is not working.
I am about to update my method in the controller to push the currentPanel and index to the controller, but before I do I thought I would ask here first.
So, does anyone know why my 2 way binding isn't working?
So my current workaround works, but I just don't like it.
In my directive I have done this:
scope.next = function () {
controller.next().then(function () {
console.log(controller.index);
scope.currentPanel = controller.currentPanel;
scope.index = controller.index;
scope.length = controller.length;
});
}
and in the directive controller I have done this:
self.next = function () {
// Try to navigate forward
return service.navigateNext().then(function () {
// Set our current panel
self.currentPanel = service.currentPanel;
self.index = service.index;
self.length = service.length;
console.log(self.index);
});
}
and in my service, it looks the same as before:
// Get our next panel
navigateNext: function () {
// Create a deferred promise
var deferred = $q.defer();
// Get the next index or reset if we reached the end of our list
service.index = service.index === (service.length - 1) ? 0 : service.index += 1;
// Set our active panel
service.currentPanel = service.panels[service.index];
console.log(service.index);
// Resolve our promise
deferred.resolve();
// Return our promise
return deferred.promise;
},
This works, but surely this is not the way it should work.
I have figured it out thanks to this article.
I just had to create an object in my directive and bind the values to that.
Doing that fixed the issues.

How to push data to factory/service in AngularJS/Ionic : (Case study ionic-framework-tutorial from Thinkster.io)

I am trying to implement this tutorial from Thinkers.io : https://thinkster.io/ionic-framework-tutorial/
I am already in Step 3 : "Building Interface Functionality" and got a little problem on "Adding, Removing and Retrieving Favorited Songs".
I've followed the step from : "create a User factory in services.js" until "add the current song to our favorites at the beginning line of sendFeedback() method".
When i try to click favorite button, and go to favorite page, there are nothing happen.
The current song is note added to favorite list.
here's my code in controller.js
.controller('DiscoverCtrl', function($scope, $timeout, User) {
$scope.songs = [
{
"title":"Stealing Cinderella",
"artist":"Chuck Wicks",
"image_small":"https://i.scdn.co/image/d1f58701179fe768cff26a77a46c56f291343d68",
"image_large":"https://i.scdn.co/image/9ce5ea93acd3048312978d1eb5f6d297ff93375d"
},
{
"title":"Venom - Original Mix",
"artist":"Ziggy",
"image_small":"https://i.scdn.co/image/1a4ba26961c4606c316e10d5d3d20b736e3e7d27",
"image_large":"https://i.scdn.co/image/91a396948e8fc2cf170c781c93dd08b866812f3a"
},
{
"title":"Do It",
"artist":"Rootkit",
"image_small":"https://i.scdn.co/image/398df9a33a6019c0e95e3be05fbaf19be0e91138",
"image_large":"https://i.scdn.co/image/4e47ee3f6214fabbbed2092a21e62ee2a830058a"
}
];
// initialize the current song
$scope.currentSong = angular.copy($scope.songs[0]);
$scope.sendFeedback = function (bool) {
// first, add to favorites if they favorited
if (bool) User.addSongToFavorites($scope.currentSong);
// set variable for the correct animation sequence
$scope.currentSong.rated = bool;
$scope.currentSong.hide = true;
$timeout(function() {
// $timeout to allow animation to complete before changing to next song
// set the current song to one of our three songs
var randomSong = Math.round(Math.random() * ($scope.songs.length - 1));
// update current song in scope
$scope.currentSong = angular.copy($scope.songs[randomSong]);
}, 250);
}
})
.controller('FavoritesCtrl', function($scope, User) {
// get the list of our favorites from the user service
$scope.favorites = User.favorites;
})
This following code in service.js
angular.module('songhop.services', [])
.factory('User', function() {
var o = {
favorites: []
}
return o;
//Method for adding songs to the favorites array:
o.addSongToFavorites = function(song) {
// make sure there's a song to add
if (!song) {
return false; }
// add to favorites array
o.favorites.unshift(song);
}
});
Anyone have a clue?
It Would be greatly appreciated.
There is a small issue with your code, the return statement comes before the service declaration is complete. Move return to the last.

Userscript breaking pages outside of domain

Even though I have my userscript restricted to one domain, any site I visit that uses Jquery experiences all kinds of nasty issues when my script is active. Checking the error console in chrome reveals an identical error on all sites:
"Uncaught TypeError: Property '$' of object [object Window]"
What's causing this? My objective is to get my userscript running in noconflict mode on a site that uses both jquery and prototype. I didn't make the code above var = myFunction, so I don't know what about it is causing the problem I'm running into. Any suggestions?
// ==UserScript==
// #name Restore Dashboard Tags
// #namespace http://userstyles.org
// #description This script restores a user's tracked tag list to the sidebar on tumblr
// #author
// #homepage
// #history 1.0 first version
// #include http://www.tumblr.com/*
// #match http://www.tumblr.com/*
// ==/UserScript==
var jQuery, $ = null;
function addJQuery(callback) {
var p = null;
if(window.opera || window.navigator.vendor.match(/Google/)) {
var div = document.createElement("div");
div.setAttribute("onclick", "return window;");
p = div.onclick();
}
else {
p = Window;
}
jQuery = $ = p.jQuery.noConflict();
callback();
}
var myFunction = function() {
jQuery('div#right_column ul:first-child').after('<ul class="controls_section" id="tracked_tags"></ul>');
jQuery('div.tracked_tags a').each(function (i) {
var tagID = jQuery(this).attr("id");
var tagIDNumber = tagID.replace('tag_','');
var tagName = jQuery(this).attr("href");
var tagNameClean = tagName.replace('/tagged/','');
var tagContent ='';
tagContent += '<li><a href="'+tagName+'" id="'+tagID+'" class="tag">';
tagContent += '<div class="hide_overflow">'+tagNameClean+'</div>';
tagContent += '<span id="tag_unread_'+tagIDNumber+'" class="count" style=""></span></a></li>';
jQuery(tagContent).appendTo('div#right_column ul#tracked_tags');
});
};
var NewPosts = function(){
jQuery('div.tracked_tags > div').each(function (i) {
var thisIndex = jQuery(this).index();
if (jQuery(this).find('small').length){
var postCount = jQuery(this).find('small').text();
jQuery('div#right_column ul#tracked_tags li:eq('+thisIndex+')').find('.count').html(postCount.replace("new posts", "") );
}
});
setTimeout(NewPosts,30000);
}
addJQuery(myFunction);
addJQuery(NewPosts);
The problem has been solved! Someone on another site IDed the culprit as jQuery = $ = p.jQuery.noConflict();; since I wasn't loading my own copy of Jquery I didn't need noConflict, and its usage was hiding Jquery from the rest of the page.

Resources