User:Martijn Hoekstra/watchthingy.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <nowiki>
(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.$));
// </nowiki>