I have written a UserScript for Greasemonkey (Firefox) and am testing it for compatibility with Chrome's Tampermonkey, and getting errors in the developer console:
Uncaught TypeError: Cannot read property 'profile_url' of undefined
Uncaught TypeError: Cannot read property 'encoded_name' of undefined
The errors seem to be referencing the onreadystatechanged callback of GM_xmlhttpRequest which is called like this:
var flairs = document.querySelectorAll('span.flair');
var steam_re = /(?:(?:https?:\/\/)?www\.)?(?:steam|pc)(?:community\.com\/?(?:(id|profiles)\/?)?|[\s\-_]*id)?[\/:\s\|]*(.{2,}?)(?:[\/|:\-\[(] ?(?:\/?(?:ghost|enforcer|tech|mm|master))+[\[)]?)?$/i
function get_text(e) { return e.innerText || e.textContent; }
function set_text(e, t) {
if (e.innerText)
e.innerText = t;
else
e.textContent = t;
}
var parser = new DOMParser();
for (var i = 0; i < flairs.length; i++) {
var text = get_text(flairs[i]);
var match = steam_re.exec(text);
if (match == null || match.length < 3)
continue;
var type = match[1] || 'id';
var name = encodeURIComponent(match[2]);
var url = 'http://steamcommunity.com/' + type + '/' + name;
var xml_url = url + '?xml=1';
GM_xmlhttpRequest({
method: 'GET',
url: xml_url, // Link to a steam profile with ?xml=1 added
accept: 'text/xml',
context: {
flair_index: i,
flair_text: text, // textContent of span element
encoded_name: name,
profile_url: url, // Link to steam profile
query_url: xml_url
},
onreadystatechange: function(response) {
if (response.readyState != 4)
return;
// Attempt to fall back to alternate forms of context,
// none of which works. response.context works on Firefox/Greasemonkey.
var context = response.context || this.context || context;
var doc = parser.parseFromString(response.responseText, 'text/xml');
var validProfile = doc.documentElement.nodeName == 'profile';
var a = document.createElement('a');
a.href = validProfile ?
context.profile_url : // TypeError here, context is undefined
('http://steamcommunity.com/actions/SearchFriends?K=' + context.encoded_name);
a.className += (validProfile ? 'steam-profile-link' : 'steam-profile-search-link');
var a_text = document.createTextNode(context.flair_text);
a.appendChild(a_text);
set_text(flairs[context.flair_index], '');
flairs[context.flair_index].appendChild(a);
}
});
}
The function itself is called fine, and the callback is invoked, but once I try to access the context var inside it, it's undefined.
It all works as expected in Firefox. What it does is iterating over span elements that have the "flair" class and checking with a regex if they contain a Steam username, and if so, makes it a link to their SteamCommunity page. (Full source on github). The script runs on /r/PaydayTheHeistOnline.
I have tested using an array defined outside the function to store the data instead of using the context property passed to xmlhttpRequest, but I'm getting the exact same error.
Update:
Tampermonkey now reports that this feature is fixed as of version 3.8.4116 (in beta at the moment). See:
The bug report
The change log
Older/generic workaround:
The context property is a relatively new feature of GM_xmlhttpRequest(), in Firefox. I doubt it's been implemented in Tampermonkey yet; see Tampermonkey's docs for GM_xmlhttpRequest().
Meanwhile, the tried-and-true method for this kind of thing is to use a closure.
Change your GM_xmlhttpRequest() call to something like:
( function (flair_index, flair_text, encoded_name, profile_url, query_url) {
GM_xmlhttpRequest ( {
method: 'GET',
url: xml_url, // Link to a steam profile with ?xml=1 added
accept: 'text/xml',
onreadystatechange: function (response) {
if (response.readyState != 4)
return;
var doc = parser.parseFromString (response.responseText, 'text/xml');
var validProfile = doc.documentElement.nodeName == 'profile';
var a = document.createElement ('a');
a.href = validProfile ?
profile_url :
('http://steamcommunity.com/actions/SearchFriends?K=' + encoded_name);
a.className += (validProfile ? 'steam-profile-link' : 'steam-profile-search-link');
var a_text = document.createTextNode (flair_text);
a.appendChild (a_text);
set_text (flairs[flair_index], '');
flairs[flair_index].appendChild (a);
}
} );
} ) (
i,
text, // textContent of span element
name,
url, // Link to steam profile
xml_url
);
Related
I'm trying to add both Facebook and Twitter share counters together, however all my efforts have failed.
<script>
tweets = 0;
function getTwitterCount(url){
$.getJSON('http://urls.api.twitter.com/1/urls/count.json?url=' + url + '&callback=?', function(data){
tweets = data.count;
$('#twitterCount').html(tweets);
return true;
});
}
var urlBase='http://abcdfav4.com/About/KickStarterCampaign/Rewards/ThePeaceSensation.html';
getTwitterCount(urlBase);
$.ajax({
type: 'GET',
url: 'https://graph.facebook.com/http://abcdfav4.com/About/KickStarterCampaign/Rewards/ThePeaceSensation.html',
success: function(data) {
showCount(data);
}
});
var fbshares = 0;
function showCount(responseText) {
// Save the parsed JSON
var json = responseText;
// Check if the response contains a 'shares' property
// If it doesn't, we can just exit this function
if (!json.hasOwnProperty('shares'))
return;
// A shares property and value must exist, update
// the span element with the share count
fbshares = json.shares;
$('#fb-share-count').html(fbshares);
}
var TotalShares = tweets + fbshares;
$('#total-share-count').html(TotalShares);
</script>
I could really do with some outside insight as I've been working crazy to get this website up and running ASAP and I'm probably overlooking the most obvious of things...
Console Log Reads:
Uncaught ReferenceError: fbshares is not defined
sdk.js:64 Invalid App Id: Must be a number or numeric string representing the application id.
card.html?v=2:79 Uncaught ReferenceError: I18n is not defined
sdk.js:64 FB.getLoginStatus() called before calling FB.init().
However despite this message, the Facebook and Twitter counters are working 100%, I just cannot get them to add together.
Best Regards,
Tim
Here's a solution:
var tweets;
function getTwitterCount(url) {
$.getJSON('http://urls.api.twitter.com/1/urls/count.json?url=' + url + '&callback=?', function(data) {
tweets = data.count;
$('#twitterCount').html(tweets);
showTotal();
});
}
var urlBase = 'http://abcdfav4.com/About/KickStarterCampaign/Rewards/ThePeaceSensation.html';
getTwitterCount(urlBase);
$.ajax({
type: 'GET',
url: 'https://graph.facebook.com/http://abcdfav4.com/About/KickStarterCampaign/Rewards/ThePeaceSensation.html',
success: showCount
});
var fbshares;
function showCount(responseText) {
// Save the parsed JSON
var json = responseText;
// Check if the response contains a 'shares' property
// If it doesn't, we can just exit this function
if (!json.hasOwnProperty('shares'))
return;
// A shares property and value must exist, update
// the span element with the share count
fbshares = json.shares;
$('#fb-share-count').html(fbshares);
showTotal();
}
function showTotal() {
if (tweets !== undefined && fbshares !== undefined)
$('#total-share-count').html(tweets + fbshares);
}
Basically showTotal attempts to sum the two values after each callback. When both values are defined, it will place the sum into the HTML.
I've got a web app using ASP.NET with C# code behind using JQuery 11.1.2. I have a dropdownlist that is populated by an AJAX call to the server when it is clicked. The web method returns a list of options to include in the dropdown based on args. Some of the options return a 'confirm' appended to the end of the option, which my success function splits out of it and I use it on my front end to decide whether or not the option requires confirmation once selected. All good.
My problem occurs when the confirm dialog comes up, it crashes IE 11 no matter the chosen option. It works fine in Chrome and Firefox, but crashes every time in IE. My only thought is that IE doesn't like that this is all done in an AJAX success function? Not sure. Any ideas would be greatly appreciated! Thank you.
$('.lazy-load').click(function (e, k) {
if (e.currentTarget.length < 2) {
var callerId = getTarget(e);
callerId = callerId.id;
// parse out the true encrypted id
var encIdIndex = callerId.indexOf('ddlAction') + 9;
var encId = callerId.substring(encIdIndex);
if (encId == '' || encId == null)
return;
// get the valid workflow options for this clientice
$.ajax({
type: 'POST',
url: 'LandingSummary.aspx/GetWorkflowOptions',
async: false,
data: "{ 'csId': '{0}'}".format(encId),
success: function (data) {
ddlId = 'ddlAction' + encId;
$$(ddlId).get(0).options.length = 0; // clear
$$(ddlId).get(0).options[0] = new Option("Choose...", '');
$.each(data.d, function(index, item) {
if (item.Value.indexOf('CONFIRM') != -1) {
//strip text
var newText = item.Value.slice(0, 0 - 'CONFIRM'.length);
// add confirm attribute
var option = new Option(newText, item.Key);
option.setAttribute('data-confirm', 'confirm');
$$(ddlId).get(0).options[$$(ddlId).get(0).options.length] = option;
}
else {
$$(ddlId).get(0).options[$$(ddlId).get(0).options.length] = new Option(item.Value, item.Key);
}
});
var fullId = $$(ddlId)[0].id;
ExpandSelect(fullId);
// add warning hookup
$$(ddlId).change(function () {
var confirmAttribute = $$(ddlId).find('option:selected').attr('data-confirm');
//debugger;
if (confirmAttribute != undefined) {
var newState = $$(ddlId).find('option:selected').text();
var sure = confirm('Are you sure you want to move this authorization to the {0} state?'.format(newState));
if (!sure) {
$$(ddlId).val('');
return false;
}
}
});
},
error: function (result) {
alert('error retrieving workflow options');
}
});
}
return false;
})
function getTarget(obj) {
var targ;
var e = obj;
if (e.target != undefined) targ = e.target;
else if (e.srcElement != undefined) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
return targ;
}
});
Problem signature:
Problem Event Name: APPCRASH
Application Name: IEXPLORE.EXE
Application Version: 11.0.9600.17631
Application Timestamp: 54b31a70
Fault Module Name: MSHTML.dll
Fault Module Version: 11.0.9600.17631
Fault Module Timestamp: 54b33039
Exception Code: c0000005
Exception Offset: 0008d910
OS Version: 6.1.7601.2.1.0.256.48
Locale ID: 1033
Additional Information 1: 0a9e
Additional Information 2: 0a9e372d3b4ad19135b953a78882e789
Additional Information 3: 0a9e
Additional Information 4: 0a9e372d3b4ad19135b953a78882e789
Inside a web worker, I have an html string like:
"<div id='foo'> <img src='bar'></img> <ul id='baz'></ul> </div>"
Is there any library I can import to easily access id and src attributes of the different tags ? Is regex the only way inside a worker ?
There are two ways to solve this problem efficiently:
Regex
With the risk of getting false positives, you can use something like:
var pattern = /<img [^>]*?src=(["'])((?:[^"']+|(?!\1)["'])*)(\1)/i;
var match = string.match(pattern);
var src = match ? match[2] : '';
Built-in parser & messaging
If getting the HTML right is a critical requirement, just let the browser parse the HTML, by passing the string to the caller. Here's a full example:
Caller:
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
if (!e.data) return;
if (e.data.method === 'getsrc') {
// Unlike document.createElement, etc, the following method does not
// load the image when the HTML is parsed
var doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = e.data.data;
var images = doc.getElementsByTagName('img');
var result = [];
for (var i=0; i<images.length; i++) {
result.push(images[i].getAttribute('src'));
}
worker.postMessage({
messageID: e.data.messageID,
result: result
});
} else if (e.data.method === 'debug') {
console.log(e.data.data);
}
});
worker.js
// A simple generic messaging API
var callbacks = {};
var lastMessageID = 0;
addEventListener('message', function(e) {
if (callbacks[e.data.messageID]) {
callbacks[e.data.messageID](e.data.result);
}
});
function sendRequest(method, data, callback) {
var messageID = ++lastMessageID;
if (callback) callbacks[messageID] = callback;
postMessage({
method: method,
data: data,
messageID: messageID
});
}
// Example:
sendRequest('getsrc',
'<img src="foo.png">' +
"<img src='bar.png'>" +
'<textarea><img src="should.not.be.visible"></textarea>',
function(result) {
sendRequest('debug', 'Received: ' + result.join(', '));
}
);
I'm trying to make a menu with artist names and images pulled from the last.fm API. Currently, it generates the list of images and names fine, except it prints "undefined" at the top of the list. I've tried commenting out the part that populates the elements with artist names/images, and just had it generate the elements, but it still prints undefined. I'm totally stumped as to what it's saying is undefined.
First it makes an AJAX call to get the information it needs:
if (window.XMLHttpRequest) { // Non-IE browsers
httpRequest = new XMLHttpRequest();
}
else if (window.ActiveXObject) { // IE 8 and older
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.open("GET", 'http://ws.audioscrobbler.com/2.0/?method=chart.gettopartists&api_key=b25b959554ed76058ac220b7b2e0a026&format=json', true);
httpRequest.onreadystatechange = function () {
var done = 4, ok = 200;
// Parse the JSON into JavaScript immediately on receipt
if (httpRequest.readyState == done && httpRequest.status == ok) {
artistList = JSON.parse(httpRequest.responseText);
Then it processes that information and generates HTML that it implements with inner.HTML
var artistsAndImage = {};
for (var i=0; i < artistList["artists"]["artist"].length; i++) {
var name = artistList["artists"]["artist"][i]["name"];
artistsAndImage[name] = artistList["artists"]["artist"][i]["image"][0]["#text"];
};
var code;
// Generate HTML for sidebar with artist names and iamges
for (var name in artistsAndImage) {
nameid = name.
replace(/ /g, "_").
replace(/&/g, ":amp").
replace(/\+/g, ":plus");
code += "<element id=\"" + nameid + "\" onclick=\"details(this)\">\n<p style = \"cursor:default\">\n <img src=\"" + artistsAndImage[name] + "\" alt=\"alt\" width = \"50\" height = \"50\">\n" + name + "\n</p> </element>\n";
};
document.getElementById("sidebar").innerHTML = "<div style = \"padding:20px\">" + code + "</div>";
Where's the "undefined" coming from, and how can I fix it? You can see the current incarnation of the page uploaded here: http://test.yukot.corp.he.net/
Instantiate your var code; with an empty string:
var code = '';
At first glance, I can see three possible causes of the undefined showing up, either artistList["artists"]["artist"][i]["image"][0]["#text"] doesn't always exist (this line artistsAndImage[name] = artistList["artists"]["artist"][i]["image"][0]["#text"];)After that, you define a variable, code, but its value is (automatically) set to undefined, you don't initialize it to a string: var code = ''; should solve that. An other explanation is your looping through the object, without checking if the property you're processing is defined on the instance itself:
var code = ''; //initialize code to empty string
for (var name in artistsAndImage)
{
if (artistsAndImage.hasOwnProperty(name))
{
code += 'First string';
//only process those that are instance properties
}
}
To be absolutely sure, you could check for undefined, too:
var code = '';
for (var name in artistsAndImage)
{
if (artistsAndImage.hasOwnProperty(name) && artistsAndImage[name] !== undefined)
{
//only process those that are instance properties
}
}
Try this in your console:
var code;//code is set to undefined
code += 'part of a string';
console.log(code);//logs: undefinedpart of a string
I use FireFox as my main browser, especially when testing out my site, Avoru. However, when checking to see if my code was working properly across other major browsers (Google Chrome, Opera, and Safari), I found that none of my custom javascript seemed to work. Although the functions and code were clear in the page source, using typeof returned an 'undefined' value for all my functions.
What is the root of this problem? If it makes any difference, I use both the Prototype and jQuery libraries for my code, as well as load all javascript at the bottom of the page (for speed reasons). Thanks!
EDIT: Here is some of the code.
// === var $j frees up the $ selector. === //
var $j = jQuery.noConflict();
// === Function: loading(); and loaded(); Manually controls the #loading element. === //
function loading(){
$j('#load_ovrly').css({'display':'block'});
$j('#loader').fadeTo('fast',1);
}
function loaded(){
$j('#load_ovrly').css({'display':'none'});
$j('#loader').fadeTo('fast',.0001);
}
// === Function: content(); Using everything after the #, the hash is processed and requested. === //
function content(theHash){
var hashIndex = theHash.indexOf('-');
var commaIndex = theHash.indexOf(',');
// === Split the Hash accordingly. === //
if((hashIndex > commaIndex) || (commaIndex == -1 && hashIndex == -1)) newHash = theHash.split(',');
if((commaIndex > hashIndex) || (commaIndex == -1 && hashIndex != -1)) newHash = theHash.split('-');
// === Set some extra variables for proofing. === //
var url = newHash[0]+".php";
// === Get parameters if there are any. === //
if(newHash[1]){
var Json = jsonify(newHash[1]);
var pars = "p="+Json;
}else{
var pars = "p={\"forcepars\":\"true\"}";
}
// === Finally request the page. === //
request(url,pars);
}
// === Function: jsonify(); Turns the leftover hash from content(); into valid JSON. === //
function jsonify(str){
var Json = "{";
var split = str.split(",");
for(var a = 0; a < split.length; a++){
if(a > 0){Json = Json+",";}
var b = split[a].split(":");
if(b[1] != undefined) Json = Json+"\""+b[0]+"\":\""+b[1]+"\"";
}
return Json+"}";
}
// === Function: AJAX(); Sends an ajax request given the url, some parameters, and the onComplete. === //
function AJAX(url,parameters,complete){
$j.ajax({
type: 'POST',
url: url,
data: parameters,
complete: function($data){
var data = $data.responseText;
complete(data);
}
});
}
// === Function: request(); Takes the properly formatted url and parameters and requests the page. === //
function request(url,parameters){
AJAX(url,parameters,
function(data){
$j('#my_box').html(data);
}
);
}
// === Function: sendForm(); Sends the form and updates the page. === //
function sendForm(id,url){
var form = $j("form#"+id).serialize();
AJAX(url,form,function(data){$j("#my_box").html(data);});
}
// === Below are items that are activated once the DOM is loaded. === //
var curHashVal = window.location.hash;
document.observe("dom:loaded",function(){
$j("#loader").ajaxStart(function(){
loading();
}).ajaxStop(function(){
loaded();
});
if(window.location.hash.length > 1) content(curHashVal.substr(1));
new PeriodicalExecuter(function() {
if(curHashVal != window.location.hash){
content(window.location.hash.substr(1));
curHashVal = window.location.hash;
}
},.15);
});
If typeof returned undefined for your functions, chances are there was some parse-time error of the javascript. This means that something that firefox was lenient about accepting in your code, other browsers weren't.
What I'd do is pass the code through JSLint to see if there are any errors. I saw several errors in your code, but I'm not sure if it would be the cause of the problem. Once the JSLint errors are fixed, your code will either work directly, or the cause of the error will be obvious.
Possibly when you drill into the jQuery libraries, you'll find that there are browser specific implementations for its classes and methods. I noticed this with its AjaxManager.
Perhaps you've made the mistake I talked about here: http://my.opera.com/hallvors/blog/show.dml/26650 somewhere in your code?
Anyway, to really answer this question we'd need a link to a full page where the problem occurs.