/*\
title: js/macros/slider.js

!Introduction
The slider macro is used to selectively reveal a chunk of text. By default, it renders as a button that may be clicked or touched to reveal the enclosed text.

The enclosed text can be a string of WikiText or be taken from a target tiddler.

The current state of the slider can be stored as the string "open" or "closed" in a specified tiddler. If the value of that tiddler changes then the slider is automatically updated. If no state tiddler is specified then the state of the slider isn't retained, but the slider still works as expected.
!!Parameters
|`state` //(defaults to 1st parameter)// |The title of the tiddler to contain the current state of the slider |
|`default` |The initial state of the slider, either `open` or `closed` |
|`class` |A CSS class to be applied to the slider root element |
|`content` |The WikiText to be enclosed in the slider. Overrides the `target` parameter, if present |
|`target` //(defaults to 2nd parameter)// |The title of the tiddler that contains the enclosed text. Ignored if the `content` parameter is specified |
|`label` //(defaults to 3rd parameter)// |The plain text to be displayed as the label for the slider button |
|`tooltip` //(defaults to 4th parameter)// |The plain text tooltip to be displayed when the mouse hovers over the slider button |
!!Markup
The markup generated by the slider macro is:
{{{
	<span class="tw-slider {user defined class}">
		<a class="btn-info">{slider label}</a>
		<div class="tw-slider-body" style="display:{state}">{slider content}</div>
	</span>
}}}
!!Examples
A minimal slider:
{{{
<<slider target:MyTiddler>>
}}}
!!Notes
The slider is a good study example of a simple interactive macro.
\*/
(function(){

/*jslint node: true */
"use strict";

var Renderer = require("../Renderer.js").Renderer,
    Dependencies = require("../Dependencies.js").Dependencies,
	Tiddler = require("../Tiddler.js").Tiddler,
	utils = require("../Utils.js");

function getOpenState(macroNode) {
	if(macroNode.params.hasOwnProperty("state")) {
		var stateTiddler = macroNode.store.getTiddler(macroNode.params.state);
		if(stateTiddler) {
			return stateTiddler.text.trim() === "open";
		}
	}
	if(macroNode.params.hasOwnProperty("default")) {
		return macroNode.params["default"] === "open";
	}
	return false;
}

function saveOpenState(macroNode) {
	if(macroNode.params.hasOwnProperty("state")) {
		var stateTiddler = macroNode.store.getTiddler(macroNode.params.state) ||
							new Tiddler({title: macroNode.params.state, text: ""});
		macroNode.store.addTiddler(new Tiddler(stateTiddler,{text: macroNode.isOpen ? "open" : "closed"}));
		return true;
	}
	return false;
}

function getSliderContent(macroNode) {
	if(macroNode.params.hasOwnProperty("content")) {
		return macroNode.store.parseText("text/x-tiddlywiki",macroNode.params.content).nodes;
	} else if(macroNode.params.hasOwnProperty("target")) {
		return [Renderer.MacroNode(
								"tiddler",
								{target: macroNode.params.target},
								null,
								macroNode.store)];
	} else {
		return [Renderer.ErrorNode("No content specified for slider")];
	}
}

exports.macro = {
	name: "slider",
	params: {
		state: {byPos: 0, type: "tiddler"},
		"default": {byName: true, type: "text"},
		"class": {byName: true, type: "text"},
		target: {byPos: 1, type: "tiddler"},
		label: {byPos: 2, type: "text"},
		tooltip: {byPos: 3, type: "text"},
		content: {byName: true, type: "text"}
	},
	events: {
		click: function(event) {
			if(event.target === this.domNode.firstChild.firstChild) {
				this.isOpen = !this.isOpen;
				if(!saveOpenState(this)) {
					exports.macro.refreshInDom.call(this,{});
				}
				event.preventDefault();
				return false;
			} else {
				return true;	
			}
		}
	},
	execute: function() {
		this.isOpen = getOpenState(this);
		var sliderContent = [];
		if(this.isOpen) {
			sliderContent = getSliderContent(this);
		}
		var attributes = {
			"class": ["tw-slider"]
		};
		if(this.hasParameter("class")) {
			attributes["class"].push(this.params["class"]);
		}
		if(this.hasParameter("state")) {
			attributes["data-tw-slider-type"] = this.params.state;
		}
		if(this.hasParameter("tooltip")) {
			attributes.alt = this.params.tooltip;
			attributes.title = this.params.tooltip;
		}
		var content = Renderer.ElementNode("span",
			attributes,
			[
				Renderer.ElementNode("a",
					{
						"class": ["btn","btn-info"]
					},[
						Renderer.TextNode(this.params.label ? this.params.label : this.params.target)
					]
				),
				Renderer.ElementNode("div",
					{
						"class": ["tw-slider-body"],
						"style": {"display": this.isOpen ? "block" : "none"}
					},
					sliderContent
				)
			]
		);
		content.execute(this.parents,this.tiddlerTitle);
		return [content];
	},
	refreshInDom: function(changes) {
		var needContentRefresh = true; // Avoid refreshing the content nodes if we don't need to
		// If the state tiddler has changed then reset the open state
		if(this.hasParameter("state") && changes.hasOwnProperty(this.params.state)) {
			this.isOpen = getOpenState(this);
		}
		// Render the content if the slider is open and we don't have any content yet
		if(this.isOpen && this.content[0].children[1].children.length === 0) {
			// Remove the existing dom node for the body
			this.content[0].domNode.removeChild(this.content[0].children[1].domNode);
			// Get the slider content and execute it
			this.content[0].children[1].children = getSliderContent(this);
			this.content[0].children[1].execute(this.parents,this.tiddlerTitle);
			this.content[0].children[1].renderInDom(this.content[0].domNode,null);
			needContentRefresh = false; // Don't refresh the children if we've just created them
		}
		// Set the visibility of the slider content
		this.content[0].children[1].domNode.style.display = this.isOpen ? "block" : "none";
		// Refresh any children
		if(needContentRefresh) {
			for(var t=0; t<this.content.length; t++) {
				this.content[t].refreshInDom(changes);
			}
		}
	}
};

})();

