Index: lib/TWiki/Plugins/TwistyPlugin/MANIFEST
===================================================================
--- lib/TWiki/Plugins/TwistyPlugin/MANIFEST	(revision 16680)
+++ lib/TWiki/Plugins/TwistyPlugin/MANIFEST	(working copy)
@@ -1,2 +1,8 @@
 data/TWiki/TwistyPlugin.txt 0644
 lib/TWiki/Plugins/TwistyPlugin.pm 0444
+
+templates/twistyplugin.tmpl
+templates/twistyplugin.jquery.tmpl
+templates/twistyplugin.dojo.tmpl
+
+pub/TWiki/TwistyPlugin/twistie.dojo.js
Index: lib/TWiki/Plugins/TwistyPlugin.pm
===================================================================
--- lib/TWiki/Plugins/TwistyPlugin.pm	(revision 16680)
+++ lib/TWiki/Plugins/TwistyPlugin.pm	(working copy)
@@ -106,24 +106,11 @@
     return if $doneHeader;
     $doneHeader = 1;
 
-    my $header .= <<'EOF';
-<style type="text/css" media="all">
-@import url("%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.css");
-</style>
-<script type='text/javascript' src='%PUBURL%/%TWIKIWEB%/BehaviourContrib/behaviour.compressed.js'></script>
-<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikilib.js"></script>
-<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiPref.js"></script>
-<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiCSS.js"></script>
-<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.compressed.js"></script>
-<script type="text/javascript">
-// <![CDATA[
-var styleText = '<style type="text/css" media="all">.twikiMakeVisible{display:inline;}.twikiMakeVisibleInline{display:inline;}.twikiMakeVisibleBlock{display:block;}.twikiMakeHidden{display:none;}</style>';
-document.write(styleText);
-// ]]>
-</script>
-EOF
+    #get twistyplugin.tmpl by default
+    TWiki::Func::loadTemplate( lc($pluginName) );
+    my $header = TWiki::Func::expandTemplate('twisty:header');
 
-    TWiki::Func::addToHEAD( 'TWISTYPLUGIN_TWISTY', $header );
+    TWiki::Func::addToHEAD( 'TWISTYPLUGIN_TWISTY', $header||'<!-- twisty:header not found in %SKIN% -->' );
 }
 
 sub _TWISTYSHOW {
Index: pub/TWiki/TwistyPlugin/twistie.dojo.js
===================================================================
--- pub/TWiki/TwistyPlugin/twistie.dojo.js	(revision 0)
+++ pub/TWiki/TwistyPlugin/twistie.dojo.js	(revision 0)
@@ -0,0 +1,239 @@
+/*
+To compress this file you can use Dojo ShrinkSafe compressor at
+http://alex.dojotoolkit.org/shrinksafe/
+*/
+
+/**
+Singleton class.
+*/
+var twiki;
+if (!twiki) twiki = {};
+twiki.TwistyPlugin = new function () {
+
+	var self = this;
+
+	/**
+	Retrieves the name of the twisty from an HTML element id. For example 'demotoggle' will return 'demo'.
+	@param inId : (String) HTML element id
+	@return String
+	@privileged
+	*/
+	this._getName = function (e) {
+		var re = new RegExp("(.*)(hide|show|toggle)", "g");
+        var inId = dojo.attr(e, 'id');
+		var m = re.exec(inId);
+		var name = (m && m[1]) ? m[1] : "";
+    	return name;
+	}
+	
+	/**
+	Retrieves the type of the twisty from an HTML element id. For example 'demotoggle' will return 'toggle'.
+	@param inId : (String) HTML element id
+	@return String
+	@privileged
+	*/
+	this._getType = function (inId) {
+		var re = new RegExp("(.*)(hide|show|toggle)", "g");
+		var m = re.exec(inId);
+    	var type = (m && m[2]) ? m[2] : "";
+    	return type;
+	}
+	
+	/**
+	Toggles the collapsed state. Calls _update().
+	@privileged
+	*/
+	this._toggleTwisty = function (ref) {
+		if (!ref) return;
+		ref.state = (ref.state == twiki.TwistyPlugin.CONTENT_HIDDEN) ? twiki.TwistyPlugin.CONTENT_SHOWN : twiki.TwistyPlugin.CONTENT_HIDDEN;
+		self._update(ref, true);
+	}
+	
+	/**
+	Updates the states of UI trinity 'show', 'hide' and 'content'.
+	Saves new state in a cookie if one of the elements has CSS class 'twistyRememberSetting'.
+	@param ref : (Object) twiki.TwistyPlugin.Storage object
+	@privileged
+	*/
+	this._update = function (ref, inMaySave) {
+		var showControl = ref.show;
+		var hideControl = ref.hide;
+		var contentElem = ref.toggle;
+		if (ref.state == twiki.TwistyPlugin.CONTENT_SHOWN) {
+			// show content
+			dojo.addClass(showControl, 'twistyHidden');	// hide 'show'
+			dojo.removeClass(hideControl, 'twistyHidden'); // show 'hide'
+			dojo.removeClass(contentElem, 'twistyHidden'); // show content
+		} else {
+			// hide content
+			dojo.removeClass(showControl, 'twistyHidden'); // show 'show'	
+			dojo.addClass(hideControl, 'twistyHidden'); // hide 'hide'
+			dojo.addClass(contentElem, 'twistyHidden'); // hide content
+		}
+		if (inMaySave && ref.saveSetting) {
+	        twiki.Pref.setPref(twiki.TwistyPlugin.COOKIE_PREFIX + ref.name, ref.state);
+		}
+		if (ref.clearSetting) {
+	        twiki.Pref.setPref(twiki.TwistyPlugin.COOKIE_PREFIX + ref.name, "");
+		}
+	}
+	
+	/**
+	Stores a twisty HTML element (either show control, hide control or content 'toggle').
+	@param e : (Object) HTMLElement
+	@privileged
+	*/
+	this._register = function (e) {
+		if (!e) return;
+		var name = self._getName(e);
+		var ref = self._storage[name];
+		if (!ref) {
+			ref = new twiki.TwistyPlugin.Storage();
+		}
+        var classValue = dojo.attr(e, 'class');             //TODO: replace with dojo.hasClass
+        ref.saveSetting = dojo.hasClass(e, 'twistyRememberSetting');
+        ref.clearSetting = dojo.hasClass(e, 'twistyForgetSetting');
+        ref.startShown = dojo.hasClass(e, 'twistyStartShow');
+        ref.startHidden = dojo.hasClass(e, 'twistyStartHide');
+        ref.firstStartShown = dojo.hasClass(e, 'twistyFirstStartShow');
+        ref.firstStartHidden = dojo.hasClass(e, 'twistyFirstStartHide');
+
+		ref.name = name;
+		var type = self._getType(e.id);
+		ref[type] = e;
+		self._storage[name] = ref;
+		switch (type) {
+			case 'show': // fall through
+			case 'hide':
+				e.onclick = function() {
+					self._toggleTwisty(ref);
+					return false;
+				}
+				break;
+		}
+		return ref;
+	}
+	
+	/**
+	Key-value set of twiki.TwistyPlugin.Storage objects. The value is accessed by twisty id identifier name.
+	@example var ref = self._storage["demo"];
+	@privileged
+	*/
+	this._storage = {};
+};
+
+/**
+Public constants.
+*/
+twiki.TwistyPlugin.CONTENT_HIDDEN = 0;
+twiki.TwistyPlugin.CONTENT_SHOWN = 1;
+twiki.TwistyPlugin.COOKIE_PREFIX = "TwistyPlugin_";
+
+/**
+The cached full TWiki cookie string so the data has to be read only once during init.
+*/
+twiki.TwistyPlugin.prefList;
+
+/**
+Initializes a twisty HTML element (either show control, hide control or content 'toggle') by registering and setting the visible state.
+Calls _register() and _update().
+@public
+@param inId : (String) id of HTMLElement
+@return The stored twiki.TwistyPlugin.Storage object.
+*/
+twiki.TwistyPlugin.init = function(e) {
+	if (!e) return;
+
+	// check if already inited
+        var name = this._getName(e);
+	var ref = this._storage[name];
+	if (ref && ref.show && ref.hide && ref.toggle) return ref;
+
+	// else register
+	ref = this._register(e);
+	
+	//twiki.CSS.replaceClass(e, "twistyMakeHidden", "twistyHidden");
+	dojo.removeClass(e, "twikiMakeVisible");
+	dojo.removeClass(e, "twikiMakeVisibleBlock");
+	dojo.removeClass(e, "twikiMakeVisibleInline");
+	dojo.removeClass(e, "twikiMakeHidden");
+
+	
+	if (ref.show && ref.hide && ref.toggle) {
+		// all Twisty elements present
+
+		if (dojo.hasClass(e, 'twistyInited1')) {
+			ref.state = twiki.TwistyPlugin.CONTENT_SHOWN
+			this._update(ref, false);
+			return ref;
+		}
+		if (dojo.hasClass(e, 'twistyInited0')) {
+			ref.state = twiki.TwistyPlugin.CONTENT_HIDDEN
+			this._update(ref, false);
+			return ref;
+		}
+
+		if (twiki.TwistyPlugin.prefList == null) {
+			// cache complete cookie string
+			twiki.TwistyPlugin.prefList = twiki.Pref.getPrefList();
+		}
+		var cookie = twiki.Pref.getPrefValueFromPrefList(twiki.TwistyPlugin.COOKIE_PREFIX + ref.name, twiki.TwistyPlugin.prefList);
+		if (ref.firstStartHidden) ref.state = twiki.TwistyPlugin.CONTENT_HIDDEN;
+		if (ref.firstStartShown) ref.state = twiki.TwistyPlugin.CONTENT_SHOWN;
+		// cookie setting may override  firstStartHidden and firstStartShown
+		if (cookie && cookie == "0") ref.state = twiki.TwistyPlugin.CONTENT_HIDDEN;
+		if (cookie && cookie == "1") ref.state = twiki.TwistyPlugin.CONTENT_SHOWN;
+		// startHidden and startShown may override cookie
+		if (ref.startHidden) ref.state = twiki.TwistyPlugin.CONTENT_HIDDEN;
+		if (ref.startShown) ref.state = twiki.TwistyPlugin.CONTENT_SHOWN;
+
+		this._update(ref, false);
+	}
+	return ref;	
+}
+
+twiki.TwistyPlugin.toggleAll = function(inState) {
+	var i;
+	for (var i in this._storage) {
+		var e = this._storage[i];
+		e.state = inState;
+		this._update(e, true);
+	}
+}
+twiki.TwistyPlugin.toggleAll_Show = function() {
+    twiki.TwistyPlugin.toggleAll(twiki.TwistyPlugin.CONTENT_SHOWN);
+}
+twiki.TwistyPlugin.toggleAll_Hide = function() {
+    twiki.TwistyPlugin.toggleAll(twiki.TwistyPlugin.CONTENT_HIDDEN);
+}
+
+/**
+Storage container for properties of a twisty HTML element: show control, hide control or toggle content.
+*/
+twiki.TwistyPlugin.Storage = function () {
+	this.name;										// String
+	this.state = twiki.TwistyPlugin.CONTENT_HIDDEN;	// Number
+	this.hide;										// HTMLElement
+	this.show;										// HTMLElement
+	this.toggle;									// HTMLElement (content element)
+	this.saveSetting = false;						// Boolean; default not saved
+	this.clearSetting = false;						// Boolean; default not cleared
+	this.startShown;								// Boolean
+	this.startHidden;								// Boolean
+	this.firstStartShown;							// Boolean
+	this.firstStartHidden;							// Boolean
+}
+
+/**
+ * dojo init 
+ */
+twiki.TwistyPlugin.onLoad = function() {
+  dojo.query(".twistyTrigger").forEach("twiki.TwistyPlugin.init(item);");
+  dojo.query(".twistyContent").forEach("twiki.TwistyPlugin.init(item);");
+  
+  dojo.query(".twistyExpandAll").onclick(twiki.TwistyPlugin.toggleAll_Show);
+  dojo.query(".twistyCollapseAll").onclick(twiki.TwistyPlugin.toggleAll_Hide);
+}
+
+dojo.addOnLoad(twiki.TwistyPlugin.onLoad);
+
Index: templates/twistyplugin.dojo.tmpl
===================================================================
--- templates/twistyplugin.dojo.tmpl	(revision 0)
+++ templates/twistyplugin.dojo.tmpl	(revision 0)
@@ -0,0 +1,22 @@
+
+%TMPL:INCLUDE{twistyplugin}%
+
+%TMPL:DEF{twisty:header}%
+<style type="text/css" media="all">
+@import url("%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.css");
+</style>
+<!-- style type="text/css">
+        @import "dojoroot/dijit/themes/tundra/tundra.css";
+        @import "dojoroot/dojo/resources/dojo.css"
+</style
+-->
+<script type="text/javascript" src="%PUBURLPATH%/%TWIKIWEB%/DojoToolkitContrib/dojo/dojo.js" djConfig="parseOnLoad: true"></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiPref.js"></script>
+<script type="text/javascript" src="%PUBURLPATH%/%TWIKIWEB%/TwistyPlugin/twistie.dojo.js"></script>
+<script type="text/javascript">
+// <![CDATA[
+var styleText = '<style type="text/css" media="all">.twikiMakeVisible{display:inline;}.twikiMakeVisibleInline{display:inline;}.twikiMakeVisibleBlock{display:block;}.twikiMakeHidden{display:none;}</style>';
+document.write(styleText);
+// ]]>
+</script>
+%TMPL:END%
Index: templates/twistyplugin.jquery.tmpl
===================================================================
--- templates/twistyplugin.jquery.tmpl	(revision 0)
+++ templates/twistyplugin.jquery.tmpl	(revision 0)
@@ -0,0 +1,17 @@
+
+%TMPL:INCLUDE{twistyplugin}%
+
+%TMPL:DEF{twisty:header}%
+<style type="text/css" media="all">
+@import url("%PUBURL%/%TWIKIWEB%/JQueryTwistyPlugin/twist.css");
+</style>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/JQueryTwistyPlugin/twist.js"></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiPref.js"></script>
+<script type="text/javascript">
+// <![CDATA[
+var styleText = '<style type="text/css" media="all">.twikiMakeVisible{display:inline;}.twikiMakeVisibleInline{display:inline;}.twikiMakeVisibleBlock{display:block;}.twikiMakeHidden{display:none;}</style>';
+document.write(styleText);
+// ]]>
+</script>
+%TMPL:END%
+
Index: templates/twistyplugin.tmpl
===================================================================
--- templates/twistyplugin.tmpl	(revision 0)
+++ templates/twistyplugin.tmpl	(revision 0)
@@ -0,0 +1,18 @@
+%{ this is the default fallback header for TwistyPlugin, thus using TwistyContrib (currently part of default TWiki ) }%
+
+%TMPL:DEF{twisty:header}%
+<style type="text/css" media="all">
+@import url("%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.css");
+</style>
+<script type='text/javascript' src='%PUBURL%/%TWIKIWEB%/BehaviourContrib/behaviour.compressed.js'></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikilib.js"></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiPref.js"></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TWikiJavascripts/twikiCSS.js"></script>
+<script type="text/javascript" src="%PUBURL%/%TWIKIWEB%/TwistyContrib/twist.compressed.js"></script>
+<script type="text/javascript">
+// <![CDATA[
+var styleText = '<style type="text/css" media="all">.twikiMakeVisible{display:inline;}.twikiMakeVisibleInline{display:inline;}.twikiMakeVisibleBlock{display:block;}.twikiMakeHidden{display:none;}</style>';
+document.write(styleText);
+// ]]>
+</script>
+%TMPL:END%
