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 = ""; 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.$)); //