I have searched around and can't seem to find what I'm looking for.
How can I use the results of api.php?action=query&list=allusers&augroup=sysop&aulimit=max&format=json in a javascript?
What I'm trying to do is create a script to simply change the color of usernames on the wiki if they are in certain groups, like sysop, bureaucrat, etc.
Although I'm usually pretty good at figuring these things out, I've been working on this all day and I've gotten nowhere with it. Can anyone help me out with maybe some examples or something? If it can be done with mostly jQuery that would be preferable.
Thanks in advance.
Edit: (in response to comment by ahren):
Well I started out trying to clean up and modify a script written by someone else to add more functionality/make it work as expected, but I had trouble making sense out of it:
/* HighlightUsers by Bobogoobo
* Changes color of links to specified groups and users
* TODO: redo but much better (recursive would be easier - I've learned a lot since I wrote this thing)
*/
function highlightUsers () {
"use strict";
var highlight = window.highlight || {}, selector = '', that, userstr,
indices = [],
i = 0,
user,
ns,
x,
y;
for (ns in mw.config.get('wgNamespaceIds')) {
if (i === 4) {
userstr = ns;
}
i++;
}
userstr = userstr.charAt(0).toUpperCase() + userstr.substring(1);
if (highlight['selectAll']) {
selector = 'a[href$=":';
} else {
selector = 'a[href="/wiki/' + userstr + ':';
}
for (y in highlight) {
indices.push(y);
}
for (x in highlight) {
that = highlight[x];
if (x === 'selectAll') {
continue;
} else if (x === 'users') {
for (user in that) {
$(selector + user.replace(/ /g, '_') + '"]').css({
'color': that[user],
'font-weight': 'bold'
}).attr('data-highlight-index',
$.inArray('users', indices));
}
} else {
(function (userColor, userGroup) { //JavaScript doesn't like to cooperate with me
$.getJSON('/api.php?action=query&list=allusers&augroup=' + userGroup +
'&aulimit=max&format=json', function (data) {
var stuff = data.query.allusers, //, select = '';
user;
for (user in stuff) {
//select += selector + stuff[user].name.replace(/ /g, '_') + '"], ';
$(selector + stuff[user].name.replace(/ /g, '_') + '"]').each(function () {
if (($(this).attr('data-highlight-index') || -1) < $.inArray(userGroup, indices)) {
$(this).attr('data-highlight-index', $.inArray(userGroup, indices));
$(this).css({
'color': userColor,
'font-weight': 'bold'
});
}
});
}
//select = select.substring(0, select.length - 2);
//$(select).css('color', userColor);
});
}(that, x));
}
}
}
That is my latest draft of it, I managed to accomplish a few things, like making the names bold, and correcting syntax mishaps, but I've decided I may be better off starting from scratch than trying to understand someone else's code.
i would prefer using jQuery AJAX functionality.
Usage is simple :
$.ajax({
url : 'api.php',
type : 'post',
datatype : 'json',
data : {
'list' : allusers,
'augroup' : 'sysop'
},
success : function(success_record) {
//here you can do Js dom related modifications like changing color etc.
// after php(server side) completes
}
});
I tried the AJAX solution described by Markrand, but unfortunately I wasn't able to get it to work. First off I was getting "allusers is not defined", so I wrapped it in quotes so that it wasn't treated as a var, then I had to add change 'api.php' to '/api.php' because it was becoming '/wiki/api.php' which doesn't exist, and adding the slash got it to use the base URL. It would then execute and return an object, however there was nothing useful in that object that I could use (such as an array of usernames), all it gave me was the API documentation... So I ended up doing this instead:
function highlightAdmins() {
$.getJSON('/api.php?action=query&list=allusers&augroup=sysop&aulimit=max&format=json',
function(data) {
for (var i = 0; i < data.query.allusers.length; i++) {
$('a[href$="User:' + data.query.allusers[i].name + '"]').css('color', '#FF6347');
}
});
}
This gave me an object containing the results of the query, in this case an array of sysop usernames (data.query.allusers[i].name) which I could iterate though and perform actions with.
Related
I develop small backend service/application with Spring Boot and some jQuery/AJAX.
Recently while testing admin functionality I faced strange issue related with the code provided below:
function updateUserRow(id) {
$.get(ajaxUrl + id, function (data) {
$('.userPlaceBtns').empty();
$('.userArticleBtns').empty();
$.each(data, function (key, value) {
form.find("textarea[name='" + key + "']").val(value);
form.find("input[name='" + key + "']").val(value);
form.find('input:checkbox').prop(value ? 'checked' : '');
if(key == 'placeIds' && value.length != 0){
for(i = 0; i < value.length; i++) {
$('<button/>', {
text: value[i],
id: 'btn_place_'+i,
click: function () {
inspectOwnedPlace(value[i]);
}
}).appendTo('.userPlaceBtns');
}
}
if(key == 'articleIds' && value.length != 0){
for(i = 0; i < value.length; i++) {
$('<button/>', {
text: value[i],
id: 'btn_article_'+i,
click: function () {
inspectOwnedArticle(value[i]);
}
}).appendTo('.userArticleBtns');
}
}
});
$('.load-bar').hide();
$('#userEditRow').modal();
});
}
So, besides modal forms data filling, this function helps me to iterate through user-related data and place a button on prepared divs for each of this data.
Strange things happen when I try to inspect user-related places or articles with $.get from server:
function inspectOwnedPlace(id){
console.log(id);
var intId = parseInt(id);
console.log(placesAjaxUrl + intId);
$.get(placesAjaxUrl + intId, function (data) {
var placeForm = $(".ownedPlaceForm");
$.each(data, function (key, value) {
placeForm.find("textarea[name='" + key + "']").val(value);
placeForm.find("input[name='" + key + "']").val(value);
});
$('#inspectOwnedPlaceModal').modal();
});
}
The Issue
For some reason, in 50-70% of cases I get NumberFormatException on server side because of NaN incoming with server request. But in SOME cases it definitely works as expected!
Important thing is: the text on generated buttons ALWAYS appears as expected, so I can make conclusion that server always gives relevant,
non-spoiled data to the front and something in JS code leads to frequent NaN exceptions when I try to request user-relevant data by id as the next step.
Here is a screenshot of the case when I face this issue (browser tab with dev console) for better understanding:
So, here I get server's NumberFormatException because of NaN instead of integer.
What I tried:
treat ids as they are by default not usig parseInt() function;
treat ids as strings and parse them as text.
I just wonder what can lead to non-predictable, non-systemic NaNs, which are more likely "spoiled integers", in the context of my current code. Thanks in advance!
This may be a very mundane question, but this is the first jQuery plugin that I have written and I'm a bit fuzzy on understanding the scope rules in JavaScript.
I'm trying to write an simple jQuery plugin that wraps around the Stack Overflow API. I'm starting off by trying to work with the Flair API.
I wanted to make the plugin as configurable as possible so that you can easily pass it the domain and user id, and generate multiple Flairs.
var superUser = $.jStackOverflow.flair({domain:"superuser.com", id: 30162, parentId:'#su-flair'});
var stackOverflow = $.jStackOverflow.flair({domain:"stackoverflow.com", id: 55954, parentId:'#so-flair'});
The problem is, when it makes the second call, it's somehow using the correct domain and id parameters, but the parentId field that it's using in the callback function to create the HTML is using the first parameter.
You can see the plugin here and the HTML here
UPDATED
DEMO: http://jsbin.com/epeti3/5
/* 16/02/2012 02.04.38 */
(function($) {
$.fn.jStackOverflow = function(options) {
var opts = $.extend({},
$.fn.jStackOverflow.defaults, options);
return this.each(function() {
$this = $(this);
var opt = $.meta ? $.extend({},
opts, $this.data()) : opts;
var result;
var id = this.id;
var flair = $.fn.jStackOverflow.flair(opt, id);
$this.html(flair);
});
};
$.fn.jStackOverflow.setApis = function(options) {
var apis = options.protocol + options.domain + options.gTLD + "/users/flair/" + options.id + "." + options.format;
if (options.makeCallbacks) {
apis += "?callback=?";
}
return apis;
};
$.fn.jStackOverflow.flair = function(options, id) {
var api = $.fn.jStackOverflow.setApis(options);
if (options.makeCallbacks) {
result = $.getJSON(api,
function(data) {
$.fn.jStackOverflow.flairCallback(data, options, id);
});
}
return result;
};
$.fn.jStackOverflow.flairCallback = function(data, options, id) {
for (var key in data) {
if (data.hasOwnProperty(key)) {
$('<div class="' + key + '"></div>').html(key + ' : ' +data[key]).appendTo('#' + id);
}
}
};
$.fn.jStackOverflow.defaults = {
protocol: 'http://',
domain: 'stackoverflow',
gTLD: '.com',
format: 'json',
makeCallbacks: true
};
})(jQuery);
use:
<div id="so-flair"></div>
$(function() {
$('#so-flair').jStackOverflow({domain:"stackoverflow", id: 91130 });
});
The problem is that you only have a single instance of your plugin. This means that the two calls to $.jStackOverflow.flair() interfere with each other as both manipulate interal data of a single object.
Check for a demo what happens if there is some delay between the two calls (click the two buttons at the bottom)
http://jsbin.com/esovu (to edit http://jsbin.com/esovu/edit
Suddenly it starts working. So you need to investigate how to write a plugin which supports multiple instances on a single page.
You can pick any "good" jQuery plugin which multiple instances support to check how to do it.
e.g. jQuery Carousel.
Check how the lines interact to allow creating multiple Carousel instances on one page (code taken from jQuery Carousel source)
$.fn.jcarousel = function(o) { //this would match your `jStackOverflow`
return this.each(function() { //for each matched element return a new carousel
new $jc(this, o);
});
};
...
var defaults = {
...
};
...
$.jcarousel = function(e, o) { //the acutal constructor
...
}
...
$jc.fn.extend({
...
});
I am trying to make a bookmarklet that when clicked will check the URL of the current tab/window to see if it contains 'char1' and/or 'char2' (a given character). If both chars are present it redirects to another URL, for the other two it will append the current URL respectively.
I believe there must be a more elegant way of stating this than the following (which has so far worked perfectly for me) but I don't have great knowledge of Javascript. My (unwieldy & repetitive) working code (apologies):
if (window.location.href.indexOf('char1') != -1 &&
window.location.href.indexOf('char2') != -1)
{
window.location="https://website.com/";
}
else if (window.location.href.indexOf('char1') != -1)
{
window.location.assign(window.location.href += 'append1');
}
else if (window.location.href.indexOf('char2') != -1)
{
window.location.assign(window.location.href += 'append2');
}
Does exactly what I need it to but, well... not very graceful to say the least.
Is there a simpler way to do this, perhaps with vars or a pseudo-object? Or better code?
A (sort-of) refactoring of dthorpe's suggestion:
var hasC1 = window.location.href.indexOf('char1')!=-1
var hasC2 = window.location.href.indexOf('char2')!=-1
var newLoc = hasC1
? hasC2 ? "https://website.com/" : window.location.href+'append1'
: hasC2 ? window.location.href+'append1' : '';
if (newLoc)
window.location = newLoc;
Calling assign is the same as assigning a value to window.location, you were doing both with the addition assignment += operator in the method anyway:
window.location.assign(window.location.href+='append2')
This would actually assign "append2" to the end of window.location.href before calling the assign method, making it redundant.
You could also reduce DOM lookups by setting window.location to a var.
The only reduction I can see is to pull out the redundant indexof calls into vars and then test the vars. It's not going to make any appreciable difference in performance though.
var hasChar1 = window.location.href.indexOf('char1') != -1;
var hasChar2 = window.location.href.indexOf('char2') != -1;
if (hasChar1)
{
if (hasChar2)
{
window.location="https://website.com/";
}
else
{
window.location.assign(window.location.href+='append1');
}
}
else if (hasChar2)
{
window.location.assign(window.location.href+='append2');
}
Kind of extendable code. Am i crazy?
var loc = window.location.href;
var arr = [{
url: "https://website.com/",
chars: ["char1", "char2"]
}, {
url: loc + "append1",
chars: ["char1"]
}, {
url: loc + "append2",
chars: ["char2"]
}];
function containsChars(str, chars)
{
var contains = true;
for(index in chars) {
if(str.indexOf(chars[index]) == -1) {
contains = false;
break;
}
}
return contains;
}
for(index in arr) {
var item = arr[index];
if(containsChars(loc, item.chars)) {
window.location.href = item.url;
break;
}
}
var location =window.location.href
if (location.indexOf('char1')!=-1 && location.indexOf('char2')!=-1)
{window.location="https://website.com/";}
else if (location.href.indexOf('char1')!=-1) {window.location.assign(location+='append1');}
else if (location.indexOf('char2')!=-1) {window.location.assign(location+='append2');}
I'm trying to modify the code from this script. Basically I'm trying to get the script to send the browser to another page rather than display the results in a div.
This is the code in question:
<script type="text/javascript">
function openOneSlot() {
SpinningWheel.addSlot({1: 'First', 2: 'Second'});
SpinningWheel.setCancelAction(cancel);
SpinningWheel.setDoneAction(done);
SpinningWheel.open();
}
function done() {
var results = SpinningWheel.getSelectedValues();
document.getElementById('result').innerHTML = 'values: ' + results.values.join(' ') + '<br />keys: ' + results.keys.join(', ');
}
function cancel() {
document.getElementById('result').innerHTML = 'cancelled!';
}
window.addEventListener('load', function(){ setTimeout(function(){ window.scrollTo(0,0); }, 100); }, true);
</script>
I've changed the 'done' function to as follows:
function done() {
var results = SpinningWheel.getSelectedValues();
if (SpinningWheel.getSelectedValues() == "1,First")
{
window.location="first.html";
}
else if (SpinningWheel.getSelectedValues() == "2,Second")
{
window.location="second.html";
}
else
{
alert("Damn, Still not working..");
}
But now I'm lost as I'm very new to javascript.. Can anyone help the noob to get this working?
:)
Try this:
function done() {
var results = SpinningWheel.getSelectedValues();
if (results.values[0] == "First")
{
window.location.href="first.html";
}
else if (results.values[0] == "Second")
{
window.location.href="second.html";
}
else
{
alert("Damn, Still not working..");
}
The returned values appear to be an array of all the slots. Since yours has only one slot, I'm only looking at the first array position of the "results.values" array.
Try location.href instead of window.location
Note that this particular thing (changing page) is done differently across different browsers so you should google "javascript redirect " if you run into trouble on a particular browser
Look at what is returned by SpinningWheel.getSelectedValues(). It is an object with two properties keys and values. Therefore, it will not equal "1,First". Check into what is in those properties, and base your conditionals on those.
To pass your variables through, use the following syntax:
window.location = "first.html?var1Name=" + var1 + "&var2Name=" + var2;
To get the value of those variables on your first.html and second.html pages, you can use the window's query string to get hold of their values:
http://www.eggheadcafe.com/articles/20020107.asp
You would want to look at the window.location.search property.
Hey I'm trying to return a message when there are no results for the users current query! i know i need to tap into the keyup event, but it looks like the plugin is using it
This question is really out of date, anyways I'm working with the new jQuery UI 1.8.16, autocomplete is now pretty different:http://jqueryui.com/demos/autocomplete/#default
Anyways if you're trying to the do the same thing as the question asks, there is no more parse function, as far as I know there is no function that is called with the search results.
The way I managed to pull this off is by overriding the autocomplete's filter function - Note: this will affect all your autocompletes
$.ui.autocomplete.filter = function(array, term) {
var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
var aryMatches = $.grep( array, function(value) {
return matcher.test(value.label || value.value || value);
});
if (aryMatches.length == 0){
aryMatches.push({
label: '<span class="info" style="font-style: italic;">no match found</span>',
value: null
});
}
return aryMatches;
};
The function is slightly modified from the source, the grep call is the same, but if there are no results I add an object with a value of null, then I override the select calls to check for a null value.
This gives you an effect where if you keep typing and no matches are found you get the 'no matches found' item in the dropdown, which is pretty cool.
To override the select calls see jQuery UI Autocomplete disable Select & Close events
$(this).data('autocomplete').menu.options.selected = function(oEvent, ui){
if ($(ui.item).data('item.autocomplete').value != null){
//your code here - remember to call close on your autocomplete after
}
};
Since I use this on all my autocompletes on a page, make sure you check if value is null first! Before you try to reference keys that aren't there.
You could try supplying a parse option (function to handle data parsing) and do what you need when no results are returned to parse.
This example assumes you're getting back an array of JSON objects that contain FullName and Address attributes.
$('#search').autocomplete( {
dataType: "json",
parse: function(data) {
var array = new Array();
if (!data || data.length == 0) {
// handle no data case specially
}
else {
for (var i = 0; i < data.length; ++i) {
var datum = data[i];
array[array.length] = {
data: datum,
value: data.FullName + ' ' + data.Address,
result: data.DisplayName
};
}
}
return array;
}
});
I'm using the following code for the same purpose (the message is shown in the autocomplete list):
success: function(data, status, xhr){
if(!data.length){
var result = [
{
label: 'There are no matches for your query: ' + response.term,
value: response.term
}
];
response(result);
}
else{
// normal response
}
}
You can also utilize the "response" event to examine this. Simple but powerful. http://api.jqueryui.com/autocomplete/#event-response
response: function (event, ui) {
if (ui.content.length == 0) {
//Display an alert or something similar since there are no results
}
},