User:Martijn Hoekstra/watchthingy.js

//

(function (mw, $) {

function coyoneda(f, t) {

return function() {

return f(t);

};

}

//recursive implementation, needs to be trampolined for actual use. Also, value should be deferred.

function startLazyIterator(initiator, producer, hasNext) {

return initiator().then(function(t) {

return {

value: t,

hasNext: hasNext(t),

next: function() {

if (hasNext(t)) {

return startLazyIterator(coyoneda(producer, t), producer,

hasNext);

} else {

var reject = $.Deferred();

reject.reject();

return reject.promise();

}

}

};

});

}

//recursive implementation, needs to be trampolined for actual use. Also, value should be deferred.

function startEagerIterator(initiator, producer, hasNext) {

return initiator().then(function(t) {

var res;

if (hasNext(t)) {

res = startEagerIterator(coyoneda(producer, t), producer, hasNext);

} else {

var reject = $.Deferred();

reject.reject();

res = reject.promise();

}

return {

value: t,

hasNext: hasNext(t),

next: function() {

return res;

}

};

});

}

function map_iter(it, f) {

var newdef = $.Deferred();

var result = {

value: f(it.value),

hasNext: it.hasNext,

next: function() {

if (it.hasNext) {

it.next().done(function(t) {

return newdef.resolve(t);

});

} else {

newdef.reject();

}

return newdef.promise();

}

};

return result;

}

function flatmap_array(arr, f) {

return [].concat.apply(arr.map(f));

}

function flatmap_deferred(prom, f) {

var def = $.Deferred();

prom.then(f).done(function(pp) {

return pp.done;

});

return def.promise();

}

function tokensource(names) {

var url = "/w/api.php?action=query&meta=tokens&format=json&continue=&type=" +

names.join("|");

var req = $.getJSON(url);

return startLazyIterator(function() {

return req;

}, function(x) {

return tokensource(names);

}, function(x) {

return true;

});

}

function mergeprops(left, right) {

var result = {};

var leftname;

var rightname;

for (leftname in left) {

result[leftname] = left[leftname];

}

for (rightname in right) {

result[rightname] = right[rightname];

}

return result;

}

function post_query_continue(baseurl, data, init_iter) {

var baseprops = {

type: "POST",

headers: {

'Api-User-Agent': 'Martijn/1.0'

}

};

function getreq(params) {

var postdata = mergeprops(data, {

"continue": "",

format: "json"

});

postdata = mergeprops(postdata, params);

var querysettings = mergeprops(baseprops, {

data: postdata

});

var req = $.ajax(baseurl, querysettings);

console.debug("requesting", req);

return req;

}

return init_iter(coyoneda(getreq, baseprops), function(res) {

var continue_part = res["continue"];

var req = getreq(continue_part);

return req;

}, function(res) {

return ("continue" in res);

});

}

function get_tokens(names) {

var url = "/w/api.php?action=query&meta=tokens&format=json&type=" + names.join("|");

var req = $.getJSON(url);

return req.then(function(res) {

return res.query.tokens;

});

}

function watchWithTemplates(title) {

var baseurl = "/w/api.php";

var baseparams = {

action: "watch",

generator: "templates",

titles: title

};

var tokens_p = get_tokens(["watch"]);

var params_p = tokens_p.then(function(tok) {

return mergeprops(baseparams, {token: tok.watchtoken});

});

return flatmap_deferred(params_p, function(params) {

return post_query_continue(baseurl, params, startEagerIterator);

});

}

function foreach_iterator(it, f){

f(it.value);

if(it.hasNext){

it.next().done(function(n) {foreach_iterator(n, f);});

}

}

function foldl_iterator(it, init, accumulator){

if (it.hasNext){

var next = accumulator(init, it.value);

return flatmap_deferred(it.next(), function (nit) {

foldl_iterator(nit, next, accumulator);

});

} else {

var def = $.Deferred();

def.resolve(init);

return def.promise();

}

}

function id(x){ return x; }

function left(a, b){ return a;}

function right(a, b){ return b;}

function watch_transclusions_of_current() {

var title = "";

if (mw.config.values.wgNamespaceNumber === 0){

title = mw.config.values.wgTitle;

} else {

title = mw.config.values.wgCanonicalNamespace + ":" + mw.config.values.wgTitle;

}

return watchWithTemplates(title);

}

var portletname = "p-views";

var portlettextdone ="{{★}}";

var portlettextnotdone = "{{☆}}";

//var portlettextdone = "{{}}";

//var portlettextnotdone = "{{W}}";

var portletid = "p-watchtemplates";

var portlettooltip = "watch all pages transcluded on this page";

//seems this should be a mw.hooks event, but the documentation is unclear

//on which events are available, and which one is "right"

$( function(){

if ((mw.config.values.wgNamespaceNumber % 2) === 0) {

var portletlink = mw.util.addPortletLink(portletname, "#", portlettextnotdone, portletid, portlettooltip);

$( portletlink ).click( function ( e ) {

e.preventDefault();

var it_p = watch_transclusions_of_current();

it_p.done(function(it){

foldl_iterator(it, null, left).done(function(n){

$( portletLink ).text(portlettextdone);

});

});

});

}

});

}(window.mw, window.$));

//