Getting current page url from an extension using crossrider - crossrider

I am trying to set up an extension for firefox, chrome, safari and internet explorer, I am using crossrider.
Basically I want to display a browser action when clicked displays a popup containing an input text with the current page url and a button that will open a new tab to another url passing the url as a parameter.
Here is what I did based on what I found in the documentation ;
extension.js :
appAPI.ready(function($) {
appAPI.message.addListener(function(msg) {
if (msg.request === 'getUrl'){
appAPI.message.toPopup({url:location.href});
}
});
});
background.js :
var activeTabUrl;
appAPI.ready(function($) {
appAPI.browserAction.setResourceIcon('logo-19.jpg');
appAPI.browserAction.setBadgeText('extn', [255,0,0,125]);
appAPI.browserAction.setTitle('Add Url to Site');
appAPI.browserAction.setPopup({resourcePath:'pin.html', height: 300, width: 300});
});
pin.html :
<!DOCTYPE html>
<html>
<head>
<title>
</title>
<script type="text/javascript">
function crossriderMain($) {
activeTabUrl = null;
appAPI.message.addListener(function(msg) {
if (msg.url) {
activeTabUrl = msg.url;
$('#url').val(activeTabUrl);
if(activeTabUrl){
$('#addurl').prop('disabled', false);
}
}
});
appAPI.message.toActiveTab({request:'getUrl'});
$('#addurl').click(function(){
var fullUrl = 'http://my.site.com/addurl?url=' + activeTabUrl;
appAPI.openURL(fullUrl, "tab");
});
}
</script>
</head>
<body>
<input id="url" name="url" readonly="true" type="text"/>
<input id="addurl" type="submit" value="Add Url" disabled/ >
</body>
</html>
Sometimes the field containing the url is not filled, it does not happen on a specific page, for the same page, sometimes it will be displayed, sometimes not. I can't pinpoint a specific cause.
Am I doing something wrong ?

The code looks fine, other than a minor point of declaring activeTabUrl in the pin.html code and not the background.js code as they are different scopes.
From experience, the only thing I can think of that may be causing the issue is that sometimes browsers return the message response before the message listener has initialized. To mitigate this, in the pin.html code, add a delay to sending the message requesting the URL, as follows:
setTimeout(funtion() {
appAPI.message.toActiveTab({request:'getUrl'});
}, 0);
[Disclosure] I am a Crossrider employee

Related

Google Analytics 4 (gtag js) 'set' command not adding data to events

I'm trying to add data to each event I send in GA4 via javascript by using the 'set' command:
https://developers.google.com/tag-platform/gtagjs/reference#set
From those docs, it appears to be similar to Serilog Enrichment, but it doesn't appear to work and I don't see this data coming through.
I'm using localhost + Google Analytics Debugger chrome extension. Then in the Analytics > Configure > DebugView I see the custom event 'hello-world' and the property 'test', but I don't see the data I add via the "set" command.
GA DebugView
I use the set command for 2 calls - first is the "user_id" property that does work. That must be a special case, since GA treats that differently. The 2nd is for the custom object that doesn't work.
Console shows some output for both set command calls, but nothing to tell me that something has succeeded or failed
<!DOCTYPE html>
<html>
<head>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-SOMECODE"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
window.dataLayer.push(arguments);
}
gtag('js', new Date());
// Set enrichment properties for every event "on this page"
// https://developers.google.com/tag-platform/gtagjs/reference#set
gtag('set', {
'foo': 'bar',
});
// Set the measurement id
// https://developers.google.com/analytics/devguides/collection/gtagjs/setting-values
gtag('config', 'G-SOMECODE');
//Set the GA4 user_id and keep it set for all events
gtag('set', {
'user_id': 'a24b935c-03cd-47f0-af68-c60a68b31303'
});
gtag('event', 'hello-world', {
'test': true
});
</script>
<title>Html Delivery</title>
<script type="text/javascript">
function myFunction() {
// Event with nested data test. It doesnt seem to display nicely
gtag('event', 'button-click-nested', {
'data': {
'type': 'nextButton'
}
});
gtag('event', 'button-click', {
'type': 'nextButton'
});
}
function LinkFunction() {
console.log("link click");
gtag('event', 'link click', {
'type': 'link'
});
}
</script>
</head>
<body>
<button onclick="myFunction()">Next</button>
<a id="myLink" href="#" title="Click to do something" onclick="LinkFunction()">link text</a>
</body>
</html>
I've tried to move the calls to above/below the config command, but it makes no difference either.
There is a similar question, but my rep wont let me comment on it to see if its still the case. The accepted answer doesn't really help me since I wanted to be able to add any arbitrary data in this way.
I have tried to setup a custom metric for this in GA (im not using GTM), but still. No data comes through.
Does this just not work?

Opening New Web Page From HTML Using Play Framework

I'm trying to have a webpage, take some user search input and then pass that input to a new page using the Play Framework. But, I'm having some problems doing that. I feel like this is probably rather straightforward, but I just can't quite get it working.
My Routes:
# Home page
GET / controllers.Application.index
GET /:search controllers.Application.getSearchResult(search)
GET /push-notifications controllers.Application.socket
GET /assets/*file controllers.Assets.versioned(path="/public/app", file: Asset)
Controller:
def index = Action { request =>
Ok(views.html.index.render)
}
def getSearchResult(search: String) = Action { request =>
val databaseSupport = new InteractWithDatabase(comm, db)
val put = Future {
while (true) {
val data = databaseSupport.getFromDatabase(search)
if (data.nonEmpty) {
comm.communicator ! data.head
}
}
}
Logger.info("Gotcha")
Ok(views.html.singleElement.render)
}
index.scala.html:
#()
<!DOCTYPE html>
<html>
<body>
<h3>A demonstration of how to access a Search field</h3>
<input type="search" id="search" value="mySearch" placeholder="Search for something...">
<button onclick="myFunction()">Try it</button>
<p>Click the button to get the placeholder text of the search field.</p>
<p id="demo"></p>
<script>
function myFunction() {
var input = document.getElementById("search").value.toString();
window.location = #routes.Application.getSearchResult(input);
}
</script>
</body>
</html>
I feel like I'm making some big conceptual error here. Any and all help would be greatly appreciated.
Thanks in advance.

How do i return JSON results from BING Search Engine API

At the moment i am only able to do my searches based on logging in to datamarket azure.
Results returned are formatted in a table form and i dont fidn any way to return them in JSON format.
A link is displayed after results are returned but when that link is pasted in the URL section of a browser it requires a username and a password.
Example of returned URL
https://api.datamarket.azure.com/Bing/Search/v1/Web?Query=%27car%27
There used to be an api Using REST for it but now it only return errors and is no longer working.
Is there any way to use this BING API and retrieve it's return queries?
Returned Error after failing to attempt to log in to azure
The authorization type you provided is not supported. Only Basic and OAuth are supported
You need to remove the v1 from your URL, and add $format=json to the end of your query, as detailed in the schema guide:
https://api.datamarket.azure.com/Bing/Search/Web?Query=%27Xbox%27&$format=json
To get the link to work, you need to provide it your hashed credentials, to get that, follow these steps:
Login to the Azure Marketplace.
Browse to the search api listing
Click on the "Explore this Dataset" link (its only visible if you are logged in).
Once it opens, on the top click on "Show" next to "Primary Account Key"
Save this hashed value and use it in your code as the authentication digest.
Unfortunately Azure DataMarket and its APIs has been closed so you will need to switch to a new API like Google or DuckDuckGo
Microsoft Closing Azure DataMarket
// Old Answer
The way the new Bing API works is different, but you can use it without logging in, by base64 encoding your AppId and then setting it as the "Basic" authorization in the headers. I got the idea from this answer on stackoverflow. The trick is that you need to add a colon at the beginning of your AppId before base64 encoding it.
Here is a working example I made that searches Bing and returns a random image from the results. If you want to make it work, just add your own AppId:
'use strict';
$(document).ready(function() {
//Declare variables
var $searchButton = $('#searchButton');
//add a colon to the beginning of your AppId string
var appId = ':TZYNotARealAppId';
//Function to get images
function getImage() {
//base64 encode the AppId
var azureKey = btoa(appId);
//get the value from the search box
var $searchQuery = $('#searchBox').val();
//Create the search string
var myUrl = 'https://api.datamarket.azure.com/Bing/Search/v1/Composite?Sources=%27image%27&$top=50&$format=json&Query=%27' + $searchQuery + '%27';
//Make post request to bing
$.ajax({
method: 'post',
url: myUrl,
//Set headers to authorize search with Bing
headers: {
'Authorization': 'Basic ' + azureKey
},
success: function(data) {
//Insert random image in dom
var randomIndex = Math.floor(Math.random() * 50);
var imgLink = '<img width="500px" src="' + data.d.results[0].Image[randomIndex].MediaUrl + '" />';
$('#output').html(imgLink);
},
failure: function(err) {
console.error(err);
}
});
};
//Trigger function when button is clicked
$searchButton.click(function(e) {
e.preventDefault();
getImage();
});
});
<html>
<head>
<title>Image search widget</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="test search widget" />
</head>
<body>
<main>
<form>
<input id="searchBox" type="text" type="submit" name="searchBox" required/>
<button id="searchButton" type="submit">Get Image</button>
</form>
<div id="output"></div>
</main>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="./js/search.js"></script>
</body>
</html>

How to get popcorn.js working on dynamically loaded content?

I've followed this tutorial:
http://popcornjs.org/popcorn-101
Tutorial Code
<!doctype html>
<html>
<head>
<script src="http://popcornjs.org/code/dist/popcorn-complete.min.js"></script>
<script>
// ensure the web page (DOM) has loaded
document.addEventListener("DOMContentLoaded", function () {
// Create a popcorn instance by calling Popcorn("#id-of-my-video")
var pop = Popcorn("#ourvideo");
// add a footnote at 2 seconds, and remove it at 6 seconds
pop.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
// play the video right away
pop.play();
}, false);
</script>
</head>
<body>
<video height="180" width="300" id="ourvideo" controls>
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.mp4">
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.ogv">
<source src="http://videos.mozilla.org/serv/webmademovies/popcornplug.webm">
</video>
<div id="footnotediv"></div>
</body>
</html>
And can run this locally.
In Firebug, I see the footnote div update from:
<div style="display: none;">Pop!</div>
to:
<div style="display: inline;">Pop!</div>
On a live site however, I am loading my page html from a MongoDB database via Ajax and the footnote display functionality doesn't seem to be working.
Thinking this might have something to do with needing to 're-initialise' after the content has loaded, I've added the popcorn.js functionality to a function called on click:
Function
<script>
function myPopcornFunction() {
var pop = Popcorn("#ourvideo");
pop.footnote({
start: 2,
end: 6,
text: "Pop!",
target: "footnotediv"
});
pop.play();
}
</script>
Call
$(document).on("click","a.video", function (e) {
// passing values to python script and returning results from database via getJSON()
myPopcornFunction();
});
This doesn't seem to have an effect.
No footnotediv content is loaded when the video plays.
The video is also not playing automatically.
It's hard to reproduce in jsFiddle with dynamic content, so is there a generic approach to ensuring popcorn works with dynamically loaded content?
Firebug Error on click
TypeError: k.media.addEventListener is not a function
It seems to have been a timing issue in that originally I had made a call to the myPopcornFunction() outside of the function which loaded the content (a getJSON() function). When I placed the call within the same block as the getJSON() function, things seemed to maintain their 'order' and popcorn could work correctly.
Before
$(document).on("click","a.video", function (e) {
$.getJSON("/path", {cid: my_variable, format: 'json'}, function(results){
$("#content_area").html("");
$("#content_area").append(results.content);
});
e.preventDefault();
myPopcornFunction(); // the call WAS here
});
After
$(document).on("click","a.video", function (e) {
$.getJSON("/path", {cid: my_variable, format: 'json'}, function(results){
$("#content_area").html("");
$("#content_area").append(results.content);
myPopcornFunction(); // the call is now HERE
});
e.preventDefault();
});
The myPopcornFunction() was the same as in the original post.

Google Autocomplete - enter to select

I have Google Autocomplete set up for a text field of an HTML form, and it's working perfectly.
However, when the list of suggestions appear, and you use the arrows to scroll and select using enter, it submits the form, though there are still boxes to fill in. If you click to select a suggestion it works fine, but pressing enter submits.
How can I control this? How can I stop enter from submitting the form, and instead be the selection of a suggestion from autocomplete?
Thanks!
{S}
You can use preventDefault to stop the form being submitted when enter is hit, I used something like this:
var input = document.getElementById('inputId');
google.maps.event.addDomListener(input, 'keydown', function(event) {
if (event.keyCode === 13) {
event.preventDefault();
}
});
Using the Google events handling seems like the proper solution but it's not working for me. This jQuery solution is working for me:
$('#inputId').keydown(function (e) {
if (e.which == 13 && $('.pac-container:visible').length) return false;
});
.pac-container is the div that holds the Autocomplete matches. The idea is that when the matches are visible, the Enter key will just choose the active match. But when the matches are hidden (i.e. a place has been chosen) it will submit the form.
I've amalgamated the first two answers from #sren and #mmalone to produce this:
var input= document.getElementById('inputId');
google.maps.event.addDomListener(input, 'keydown', function(e) {
if (e.keyCode == 13 && $('.pac-container:visible').length) {
e.preventDefault();
}
});
works perfectly on the page. prevents the form from being submitted when the suggestion container (.pac-container) is visible. So now, an option from the autocomplete dropdown is selected when the users presses the enter key, and they have to press it again to submit the form.
My main reason for using this workaround is because I found that if the form is sent as soon as an option is selected, via the enter key, the latitude and longitude values were not being passed fast enough into their hidden form elements.
All credit to the original answers.
This one worked for me:
google.maps.event.addDomListener(input, 'keydown', e => {
// If it's Enter
if (e.keyCode === 13) {
// Select all Google's dropdown DOM nodes (can be multiple)
const googleDOMNodes = document.getElementsByClassName('pac-container');
// Check if any of them are visible (using ES6 here for conciseness)
const googleDOMNodeIsVisible = (
Array.from(googleDOMNodes).some(node => node.offsetParent !== null)
);
// If one is visible - preventDefault
if (googleDOMNodeIsVisible) e.preventDefault();
}
});
Can be easily converted from ES6 to any browser-compatible code.
The problem I had with #sren's answer was that it blocks the submit event always. I liked #mmalone's answer but it behaved randomly, as in sometimes when I hit ENTER to select the location, the handler ran after the container is hidden. So, here's what I ended up doing
var location_being_changed,
input = document.getElementById("js-my-input"),
autocomplete = new google.maps.places.Autocomplete(input),
onPlaceChange = function () {
location_being_changed = false;
};
google.maps.event.addListener( this.autocomplete,
'place_changed',
onPlaceChange );
google.maps.event.addDomListener(input, 'keydown', function (e) {
if (e.keyCode === 13) {
if (location_being_changed) {
e.preventDefault();
e.stopPropagation();
}
} else {
// means the user is probably typing
location_being_changed = true;
}
});
// Form Submit Handler
$('.js-my-form').on('submit', function (e) {
e.preventDefault();
$('.js-display').text("Yay form got submitted");
});
<p class="js-display"></p>
<form class="js-my-form">
<input type="text" id="js-my-input" />
<button type="submit">Submit</button>
</form>
<!-- External Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="//maps.googleapis.com/maps/api/js?sensor=false&libraries=places"></script>
The flag ensures that if the location is being changed & user hits enter, the event is blocked. Eventually the flag is set to false by google map's place_changed event, which then allows the form to be submitted on hitting the enter key.
Here's a simple code that worked well for me (uses no jquery).
const googleAutcompleteField = this.renderer.selectRootElement(this.elem.nativeElement);
this.selectOnEnter(googleAutcompleteField);
This piece of code, to follow the code above, is used to implement google maps autocomplete (with or without the Enter key functionality sought in this question):
this.autocomplete = new google.maps.places.Autocomplete(googleAutcompleteField, this.googleMapsOptions);
this.autocomplete.setFields(['address_component', 'formatted_address', 'geometry']);
this.autocomplete.addListener('place_changed', () => {
this.zone.run(() => {
this.googleMapsData.emit([this.autocomplete.getPlace()]);
})
})
selectOnEnter (called above in the first piece of code) defined:
selectOnEnter(inputField) {
inputField.addEventListener("keydown", (event) => {
const selectedItem = document.getElementsByClassName('pac-item-selected');
if (event.key == "Enter" && selectedItem.length != 0) {
event.preventDefault();
}
})
}
This code makes the google maps autocomplete field select whichever item user selects with the down arrow keypress. Once user selects an option with a press of the Enter key, nothing happens. User has to press Enter again to trigger onSubmit() or other command
You can do it in vanilla :
element.addEventListener('keydown', function(e) {
const gPlaceChoices = document.querySelector('.pac-container')
// No choices element ?
if (null === gPlaceChoices) {
return
}
// Get choices visivility
let visibility = window.getComputedStyle(gPlaceChoices).display
// In this case, enter key will do nothing
if ('none' !== visibility && e.keyCode === 13) {
e.preventDefault();
}
})
I tweaked Alex's code, because it broke in the browser. This works perfect for me:
google.maps.event.addDomListener(
document.getElementById('YOUR_ELEMENT_ID'),
'keydown',
function(e) {
// If it's Enter
if (e.keyCode === 13) {
// Select all Google's dropdown DOM nodes (can be multiple)
const googleDOMNodes = document.getElementsByClassName('pac-container');
//If multiple nodes, prevent form submit.
if (googleDOMNodes.length > 0){
e.preventDefault();
}
//Remove Google's drop down elements, so that future form submit requests work.
removeElementsByClass('pac-container');
}
}
);
function removeElementsByClass(className){
var elements = document.getElementsByClassName(className);
while(elements.length > 0){
elements[0].parentNode.removeChild(elements[0]);
}
}
I've tried the above short answers but they didn't work for me, and the long answers I didn't want to try them, so I've created the following code which worked pretty well for me. See Demo
Suppose this is your form:
<form action="" method="">
<input type="text" name="place" id="google-places-searchbox" placeholder="Enter place name"><br><br>
<input type="text" name="field-1" placeholder="Field 1"><br><br>
<input type="text" name="field-2" placeholder="Field 2"><br><br>
<button type="submit">Submit</button>
</form>
Then the following javascript code will solve the problem:
var placesSearchbox = $("#google-places-searchbox");
placesSearchbox.on("focus blur", function() {
$(this).closest("form").toggleClass('prevent_submit');
});
placesSearchbox.closest("form").on("submit", function(e) {
if (placesSearchbox.closest("form").hasClass('prevent_submit')) {
e.preventDefault();
return false;
}
});
And here is how the full code looks like in the HTML page (Note that you need to replace the YOUR_API_KEY with your google api key):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Prevent form submission when choosing a place from google places autocomplete searchbox</title>
</head>
<body>
<form action="" method="">
<input type="text" name="place" id="google-places-searchbox" placeholder="Enter place name"><br><br>
<input type="text" name="field-1" placeholder="Field 1"><br><br>
<input type="text" name="field-2" placeholder="Field 2"><br><br>
<button type="submit">Submit</button>
</form>
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- Google Maps -->
<!-- Note that you need to replace the next YOUR_API_KEY with your api key -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"
async defer></script>
<script>
var input = document.getElementById("google-places-searchbox");
var searchBox = new google.maps.places.SearchBox(input);
var placesSearchbox = $("#google-places-searchbox");
placesSearchbox.on("focus blur", function() {
$(this).closest("form").toggleClass('prevent_submit');
});
placesSearchbox.closest("form").on("submit", function(e) {
if (placesSearchbox.closest("form").hasClass('prevent_submit')) {
e.preventDefault();
return false;
}
});
</script>
</body>
</html>
$("#myinput").on("keydown", function(e) {
if (e.which == 13) {
if($(".pac-item").length>0)
{
$(".pac-item-selected").trigger("click");
}
}
Use $('.pac-item:first').trigger('click'); if you want to select first result