// ==UserScript==
// @name           Toggle /programmes styling
// @namespace      http://djce.org.uk/greasemonkey
// @description    Adds controls to toggle /programmes service/brand styling
// @include        http://*.ips.radio.bbc.co.uk/*
// @include        http://www.bbc.co.uk/*
// ==/UserScript==

try {
	/////////////////////////////////////////////////////////////
	// Generic toggler links
	/////////////////////////////////////////////////////////////

	// Add a link with a given click handler, and return a function which
	// sets the label text
	var insert_toggler_link;
	{
		var list = null;
		var get_list = function()
		{
			if (list) return list;
			list = document.createElement("ul");
			list.setAttribute("class", "branding-toggler");
			list.setAttribute("style", "font-size: 70%; display: block; border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; top: 0; right: 0; position: absolute; background-color: #fff;");
			var b = document.body;
			if (b) {
				b.appendChild(list);
			}
			return list;
		};

		insert_toggler_link = function(f, at_top) {
			var a = document.createElement("a");
			a.setAttribute("href", "javascript:");
			a.setAttribute("style", "color: #999; background: white;");
			a.addEventListener("click", f, false);

			a.appendChild(document.createTextNode("..."));

			var li = document.createElement("li");
			li.setAttribute("style", "padding: 0.2em;");
			li.appendChild(a);

			var l = get_list();
			if (at_top) l.insertBefore(li, l.firstChild);
			else l.appendChild(li);

			return function(t) {
				a.firstChild.nodeValue = t;
			};
		};
	}

	// A wrapper to ensure that togglers are called in alternating
	// on-off-on-off fashion (i.e. never called with new state == current
	// state)
	var debouncer = function(f, initial_state) {
		var cur_state = initial_state;
		return function(new_state) {
			if (!!new_state == !!cur_state) return;
			f(new_state);
			cur_state = new_state;
		};
	};

	/////////////////////////////////////////////////////////////
	// Add togglers to the list
	/////////////////////////////////////////////////////////////

	// One of the css/sssi togglers
	var actions = [];
	var add_toggler = function(f, label)
	{
		f = debouncer(f, true);

		var set_text;
		var on = true;
		var set_state = function(new_state) {
			f(on = new_state);
			set_text(label+" is "+(on ? "on" : "off"));
		};
		actions.push(set_state);

		var click_handler = function(evt) {
			set_state(!on);
		};
		set_text = insert_toggler_link(click_handler, false);
		set_text(label+" is on");
	};

	// The 'all' toggler
	var add_all_toggler = function()
	{
		if (actions.length == 0) return;

		var on = true;
		var click_handler = function(evt) {
			on = !on;
			for (var i=0; i<actions.length; ++i) actions[i](on);
		};
		var set_text = insert_toggler_link(click_handler, true);
		set_text("toggle all");
	};

	/////////////////////////////////////////////////////////////

	// Toggle <link rel="stylesheet"> elements
	var add_css_togglers = function() {
		var add_toggle_for_link = function(e, label)
		{
			var f = function(new_state) {
				e.setAttribute("rel", (new_state ? "" : "x-")+"stylesheet");
			};
			add_toggler(f, label);
		};

		var x = document.evaluate(
			// look in body as well as head.  /programmes pages
			// should always put them in the head, of course, but
			// if there's a sssi decoupling error then they can
			// end up in the body instead.
			'/html//link[@rel="stylesheet"][@type="text/css"]',
		       document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
		);

		for (var i=0; i<x.snapshotLength; ++i)
		{
			var e = x.snapshotItem(i);
			var href = e.getAttribute("href");
			if (!href) continue;
			var m = href.match(/^\/programmes\/r\/\d+\/stylesheets\/(([a-z0-9]\w*)\/.*)\.css$/);
			if (!m) continue;

			var label = m[1];
			if (m = label.match(/^pid\/\w\w\/(.*)$/))
				label = m[1];

			add_toggle_for_link(e, label);
		}
	};

	/////////////////////////////////////////////////////////////

	// Toggle the "head.sssi" include - a series of siblings insead <head>
	var add_head_sssi_toggler = function() {
		// Try to find whatever was added by head.sssi
		// This would be easier if we added explicit markers around
		// the include, e.g. <link id="start-of-foo">
		var x = document.evaluate(
			'/html/head/meta[@name="dcterms.modified"]',
		       document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
		);

		// Really we want to end at a comment node: <!-- Barlesque r55 -->
		// However I can't currently see how to find comment nodes.
		// So we'll stop at the next element, <link rel="index" href="/a-z/" title="A to Z" />

		// As above, look in body too in case of sssi errors
		var y = document.evaluate(
			'/html//link[@rel="index"][@title="A to Z"]',
		       document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null
		);
		if (x.snapshotLength == 1 && y.snapshotLength == 1)
		{
			var start_ele = x.snapshotItem(0);

			var end_ele = y.snapshotItem(0);
			// If there's an sssi error ([an error occurred while processing this directive])
			// then some of what should have been in <head> is now in
			// <body>.  That sounds messy to sort out; just bail in this
			// case.
			if (end_ele.parentNode.nodeName == "body")
			{
				// alert("skipping head.sssi because of sssi error");
			}
			else if (start_ele.parentNode != end_ele.parentNode)
			{
				// alert("skipping head.sssi because of parent mismatch");
			} else {
				var p = start_ele.parentNode;

				var eles = [];
				for (var e = start_ele.nextSibling; e != end_ele; e=e.nextSibling)
				{
					eles.push(e);
				}

				var f = function(new_state) {
					if (new_state) {
						for (var i=0; i<eles.length; ++i)
						{
							p.insertBefore(eles[i], end_ele);
						}
					} else {
						for (var i=0; i<eles.length; ++i)
						{
							p.removeChild(eles[i]);
						}
					}
				};
				add_toggler(f, "head");
			}
		}
	};

	/////////////////////////////////////////////////////////////

	// Toggle the other .sssi elements (which are all neatly inside divs
	// with known ids).
	var add_div_togglers = function() {
		var add_div_toggler = function(id)
		{
			var div = document.getElementById(id);
			if (!div) return;

			var eles = [];
			for (var e = div.firstChild; e; e=e.nextSibling)
			{
				eles.push(e);
			}

			var f = function(new_state) {
				if (new_state) {
					for (var i=0; i<eles.length; ++i)
					{
						div.appendChild(eles[i]);
					}
				} else {
					for (var i=0; i<eles.length; ++i)
					{
						div.removeChild(eles[i]);
					}
				}
			};
			add_toggler(f, id);
		};

		
		add_div_toggler("masthead");
		add_div_toggler("blq-local-nav");
		add_div_toggler("footer");
	};

	/////////////////////////////////////////////////////////////

	var is_programmes_page = function() {
		if (!(window.location.href.match(/\/programmes(\/|$)/))) return false;
		return true;
	};

	if (is_programmes_page())
	{
		add_css_togglers();
		add_head_sssi_toggler();
		add_div_togglers();
		add_all_toggler();
	}

} catch(err) {
	alert("Error: "+err);
}

