penguin/AstroJS

Javascript framework for my blog

commit fa8599d20d2037c7661fcf11da609e5f885e1301

author斟酌 鵬兄 <tgckpg@gmail.com>
date2016-02-23T03:25:31Z
subjectHeading snippet
commit fa8599d20d2037c7661fcf11da609e5f885e1301
Author: 斟酌 鵬兄 <tgckpg@gmail.com>
Date:   2016-02-23T03:25:31Z

    Heading snippet
---
 .../SmartInput/CandidateAction/Heading.js          |  10 +-
 .../src/Astro/Blog/AstroEdit/SmartInput/_this.js   | 372 +++++++++++++++------
 .../Blog/AstroEdit/Visualizer/Snippet/Heading.js   | 177 ++++++++++
 .../src/Astro/Blog/AstroEdit/Visualizer/_this.js   |   1 +
 botanjs/src/Astro/Blog/AstroEdit/_this.js          |   2 +
 botanjs/src/Astro/Blog/Layout/MainFrame.css        |   8 -
 botanjs/src/Astro/Blog/Layout/MainFrame.js         |   1 +
 botanjs/src/Astro/Blog/SharedStyle.css             |   7 +
 botanjs/src/Astro/Blog/SharedStyle.js              |   5 +
 ...o.Blog.AstroEdit.SmartInput.ICandidateAction.js |   2 +
 10 files changed, 481 insertions(+), 104 deletions(-)

diff --git a/botanjs/src/Astro/Blog/AstroEdit/SmartInput/CandidateAction/Heading.js b/botanjs/src/Astro/Blog/AstroEdit/SmartInput/CandidateAction/Heading.js
index aa5f836..854bb1b 100644
--- a/botanjs/src/Astro/Blog/AstroEdit/SmartInput/CandidateAction/Heading.js
+++ b/botanjs/src/Astro/Blog/AstroEdit/SmartInput/CandidateAction/Heading.js
@@ -12,17 +12,25 @@
 	var Dand                              = __import( "Dandelion" );
 
 	/** @type {Astro.Blog.AstroEdit.SmartInput.ICandidateAction} */
-	var Heading = function ( visualizer )
+	var Heading = function ( visualizer, key )
 	{
 		this.visualizer = visualizer;
+		this.key = key;
 	};
 
 	Heading.prototype.GetCandidates = function( handler )
 	{
+		return null;
 	};
 
 	Heading.prototype.Process = function( key )
 	{
+		this.visualizer.insertSnippet( "heading", { "value": key } );
+	};
+
+	Heading.prototype.Retreat = function( sender, e )
+	{
+		return sender.value == "" && e.keyCode == 8; // Backspace
 	};
 
 	ns[ NS_EXPORT ]( EX_CLASS, "Heading", Heading );
diff --git a/botanjs/src/Astro/Blog/AstroEdit/SmartInput/_this.js b/botanjs/src/Astro/Blog/AstroEdit/SmartInput/_this.js
index 19e26a3..6c9fcc3 100644
--- a/botanjs/src/Astro/Blog/AstroEdit/SmartInput/_this.js
+++ b/botanjs/src/Astro/Blog/AstroEdit/SmartInput/_this.js
@@ -27,6 +27,29 @@
 	var moduleNs = "Astro.Blog.AstroEdit.SmartInput.CandidateAction.";
 	var service_uri = Config.get( "ServiceUri" );
 
+	var Destructor = function( target )
+	{
+		this.target = target;
+		this.target.Disposed = false;
+		this.destructSequence = [];
+	};
+
+	Destructor.prototype.Register = function( destruct )
+	{
+		this.destructSequence.push( destruct );
+	};
+
+	Destructor.prototype.Destruct = function()
+	{
+		for( var i in this.destructSequence )
+		{
+			this.destructSequence[i]();
+		}
+
+		this.target.Disposed = true;
+		this.destructSequence = null;
+	};
+
 	var KeyHandler = function( sender, handler )
 	{
 		return function( e )
@@ -39,99 +62,150 @@
 		};
 	};
 
-	var LoadModule = function( mod, handler )
+	// {{{ Candidates Class
+	var Candidates = function( title, desc, defs )
 	{
-		var ldr = new Loader( service_uri );
-		ldr.load( moduleNs + mod, handler );
+		this.title = title;
+		this.desc = desc;
+		this.__cands = [];
+		this.__defs = defs || {};
+		this.Empty = !defs;
 	};
 
-	/** @param {Astro.Blog.AstroEdit.Visualizer} */
-	var SmartInput = function ( visualizer, CandidatesOvd )
+	Candidates.prototype.Get = function()
 	{
-		var SBarPresent = false;
-		var insert = function() { return Dand.textNode( "" ); };
-		var __cands = [];
+		if( this.__cands.length ) return this.__cands;
 
-		var CandidateCycle = -1;
-		var KeywordTyped = 0;
-		var shiftTabbed = false;
+		for( var i in this.__defs )
+		{
+			/** @param {Astro.Blog.AstroEdit.SmartInput.Definition} */
+			var c = this.__defs[i];
+			this.__cands.push( Dand.wrapc( "cn", [ i, Dand.wrapc( "desc", c.desc ) ], new DataKey( "key", i ) ) );
+		}
 
-		var Cands = CandidatesOvd || {
-			"Article Reference": { module: "ArticleReference", desc: "Article reference link" }
-			, "facts": { module: "Facts", desc: "Facts, a fact bubble popup when mouseover" }
-			, "footnote": { module: "Footnote", desc: "Footnote, a footnote displayed at the end of article" }
-			, "h1": { module: "Heading", options: 1, desc: "Heading, size 1" }
-			, "h2": { module: "Heading", options: 2, desc: "Heading, size 2" }
-			, "h3": { module: "Heading", options: 3, desc: "Heading, size 3" }
-			, "h4": { module: "Heading", options: 4, desc: "Heading, size 4" }
-			, "h5": { module: "Heading", options: 5, desc: "Heading, size 5" }
-		};
+		return this.__cands;
+	};
 
-		var GetCandidates = function()
+	Candidates.prototype.Reset = function()
+	{
+		var Cands = this.Get();
+		for( var i in Cands )
+		{
+			var c = IDOMElement( Cands[i] );
+			c.setAttribute( new DataKey( "selected", 0 ) );
+		}
+	};
+
+	Candidates.prototype.Filtered = function()
+	{
+		var Cands = this.Get();
+		var selected = [];
+		for( var i in Cands )
 		{
-			if( __cands.length ) return __cands;
+			var c = IDOMElement( Cands[i] );
+			c.setAttribute( new DataKey( "selected", 0 ) );
 
-			for( var i in Cands )
+			if( c.style.display != "none" )
 			{
-				/** @param {Astro.Blog.AstroEdit.SmartInput.Definition} */
-				var c = Cands[i];
-				__cands.push( Dand.wrapc( "cn", [ i, Dand.wrapc( "desc", c.desc ) ], new DataKey( "key", i ) ) );
+				selected.push( c );
 			}
+		}
 
-			return __cands;
-		};
+		return selected;
+	};
 
-		var ResetCandidates = function()
+	Candidates.prototype.Selected = function()
+	{
+		var Cands = this.Get();
+		for( var i in Cands )
 		{
-			var Cands = GetCandidates();
-			for( var i in Cands )
+			var c = IDOMElement( Cands[i] );
+			if( c.getDAttribute( "selected" ) == "1" )
 			{
-				var c = IDOMElement( Cands[i] );
-				c.setAttribute( new DataKey( "selected", 0 ) );
+				return c;
 			}
-		};
+		}
+
+		return null;
+	};
+	// }}}
+
+	/** @param {Astro.Blog.AstroEdit.Visualizer} */
+	var SmartInput = function( visualizer )
+	{
+		this.Present = false;
+		var destructor = new Destructor( this );
+
+		var _self = this;
+		var insert = function() { return Dand.textNode( "" ); };
+
+		var ModLevels = [];
 
-		var FilteredCandidates = function()
+		ModLevels.Cands = function() { return ModLevels[0][0]; };
+		ModLevels.Action = function() { return ModLevels[0][1]; };
+		ModLevels.Retreat = function() { return ModLevels[0][2]; };
+
+		var __title = null;
+		var title = function( reload )
 		{
-			var Cands = GetCandidates();
-			var selected = [];
-			for( var i in Cands )
+			if( !reload && __title ) return __title;
+
+			var l = ModLevels.length - 1;
+			var t = ModLevels[ l ][0].title;
+
+			for( var i = 0; i < l; i ++ )
 			{
-				var c = IDOMElement( Cands[i] );
-				c.setAttribute( new DataKey( "selected", 0 ) );
+				var Mod = ModLevels[i];
+				t = t + " > " + Mod[0].title;
+			}
 
-				if( c.style.display != "none" )
-				{
-					selected.push( c );
-				}
+			if( __title )
+			{
+				__title.textContent = t;
+			}
+			else
+			{
+				__title = Dand.wrape( t );
 			}
 
-			return selected;
+			return __title;
 		};
-
-		var SelectedCandidate = function()
+		var __stage = null;
+		var stage = function( incoming )
 		{
-			var Cands = GetCandidates();
-			for( var i in Cands )
+			if( __stage )
 			{
-				var c = IDOMElement( Cands[i] );
-				if( c.getDAttribute( "selected" ) == "1" )
+				if( incoming )
 				{
-					return c;
+					IDOMElement( Dand.wrap() ).lootChildren( __stage[1] );
+
+					var ThisCands = ModLevels.Cands().Get();
+					if( ThisCands.length )
+					{
+						IDOMElement( __stage[1] ).lootChildren( Dand.wrape( ThisCands ) );
+					}
 				}
+
+				return __stage;
 			}
+			var Cands = ModLevels.Cands();
 
-			return null;
-		};
+			var Command = Dand.wrap(
+				"input", null, "v_snippet_input_single"
+				, null, IKey.quickDef( "type", "text", "placeholder", Cands.desc, "value", "`" )
+			);
 
-		var ModuleLoaded = function( e )
-		{
-			/** @type {Astro.Blog.AstroEdit.SmartInput.ICandidateAction} */
-			var module = new ( __import( e ) )( visualizer );
+			var CandList = Dand.wrap( "div", null, "compx smartbar-candidates", Cands.Get() );
+
+			Command.selectionStart = Command.selectionEnd = 1;
 
-			// XXX
+			return __stage = [ Command, CandList ];
 		};
 
+		var CandidateCycle = -1;
+		var KeywordTyped = 0;
+		var shiftTabbed = false;
+
 		var HandleInput = function( sender, e )
 		{
 			// Don't handle if holding shift or ctrl key
@@ -146,6 +220,9 @@
 			switch( e.keyCode )
 			{
 				case 192: // `
+					// If we are not are the first level, do nothing
+					if( 1 < ModLevels.length ) break;
+
 					// Closing the quote, that means this is a block-quoted text
 					e.preventDefault();
 					insert = undefined;
@@ -164,14 +241,19 @@
 					// Not closing the quote, either a direct text or the first matched action
 					e.preventDefault();
 
-					var selected = SelectedCandidate();
+					// No candidates, directly pass the input text to the processor
+					if( ModLevels.Cands().Empty && 1 < ModLevels.length )
+					{
+						ModLevels.Action()( sender.value );
+						break;
+					}
+
+					var selected = ModLevels.Cands().Selected();
 					// Check if matched an action first
 					if( selected )
 					{
 						insert = undefined;
-						sender.BindingBox.close();
-
-						LoadModule( Cands[ selected.getDAttribute( "key" ) ].module, ModuleLoaded );
+						ModLevels.Action()( selected.getDAttribute( "key" ) );
 					}
 					else
 					{
@@ -190,7 +272,7 @@
 					// Hitting tab will cycle around the candidates
 					e.preventDefault();
 
-					var c = FilteredCandidates();
+					var c = ModLevels.Cands().Filtered();
 					var l = c.length;
 					if( !l ) break;
 
@@ -211,20 +293,30 @@
 					var CyclingKeyword = ThisCandidate.getDAttribute( "key" );
 
 					sender.value = "`" + CyclingKeyword;
+
+					// Check if only 1 matched
+					if( c.length == 1 && KeywordTyped == sender.value.length )
+					{
+						insert = undefined;
+						ModLevels.Action()( ThisCandidate.getDAttribute( "key" ) );
+						break;
+					}
+
 					sender.setSelectionRange( KeywordTyped, sender.value.length );
 					break;
 
 				default:
-					FilteredCandidates();
+					ModLevels.Cands().Filtered();
 					CandidateCycle = -1;
 			}
 		};
 
 		var TestEmpty = function( sender, e )
 		{
-			if( sender.value == "" )
+			if( ModLevels.Retreat()( sender, e ) )
 			{
-				sender.BindingBox.close();
+				_self.RetreatLevel( sender );
+				if( !ModLevels.length ) return;
 			}
 
 			// Search exact matched Candidates
@@ -244,7 +336,7 @@
 						KeywordTyped = sender.value.length;
 					}
 
-					var c = GetCandidates();
+					var c = ModLevels.Cands().Get();
 					var keyword = sender.value.substr( 1 );
 					for( var i in c )
 					{
@@ -263,60 +355,150 @@
 
 		var ClosePanel = function( confirmed )
 		{
-			ResetCandidates();
-
-			Cycle.next( function() { SBarPresent = false; } );
+			Cycle.next( function() { _self.Present = false; } );
 			visualizer.restoreSelection();
 
-			if( !confirmed ) return;
-
-			if( insert != undefined )
+			if( confirmed && insert != undefined )
 				visualizer.insertAtCaret( insert() );
+
+			destructor.Destruct();
 		};
 
-		var ShowSmartBar = function()
+		// Advance the input level by CandidateAction
+		this.advanceLevel = function( Candidates, Action, Retreat )
 		{
-			if( SBarPresent ) return;
-			visualizer.saveSelection();
+			if( ModLevels.length )
+			{
+				ModLevels[0].selected = ModLevels.Cands().Selected().getDAttribute( "key" );
+			}
 
-			SBarPresent = true;
+			ModLevels.unshift([ Candidates, Action, Retreat ]);
 
-			var title = "Quick Access ( Prototype )";
+			if( 1 < ModLevels.length )
+			{
+				stage( Candidates );
+				title( true );
+				stage()[0].value = "";
+				stage()[0].setAttribute( "placeholder", ModLevels.Cands().desc );
+			}
+		};
 
-			var Command = Dand.wrap(
-				"input", null, "v_snippet_input_single"
-				, null, IKey.quickDef( "type", "text", "placeholder", "Command", "value", "`" )
-			);
-			var Candidates = Dand.wrap( "div", null, "compx smartbar-candidates", GetCandidates() );
+		this.RetreatLevel = function( sender )
+		{
+			if( ModLevels.length == 1 )
+			{
+				sender.BindingBox.close();
+				destructor.Destruct();
+			}
 
-			Command.selectionStart = Command.selectionEnd = 1;
+			ModLevels.shift();
 
-			Command.BindingBox = new MessageBox(
-				title
-				, Dand.wrape([ Command, Candidates ])
+			if( ModLevels.length )
+			{
+				var Cands = ModLevels.Cands();
+				var input = stage( Cands )[0];
+				title( true );
+				input.value
+					= "`" + Cands.Selected().getDAttribute( "key" );
+
+				input.setAttribute( "placeholder", Cands.desc );
+
+				input.selectionStart = 1;
+				input.selectionEnd = input.value.length;
+			}
+		};
+
+		this.Show = function()
+		{
+			if( _self.Present ) return;
+			visualizer.saveSelection();
+
+			_self.Present = true;
+
+			var MsgBox = new MessageBox(
+				title()
+				, Dand.wrape( stage() )
 				, "Back", false
 				, ClosePanel
 			);
 
-			Command.BindingBox.show();
+			MsgBox.show();
+
+			var Command = stage()[0];
+			Command.BindingBox = MsgBox;
+
 			Command.focus();
 
 			var DCommand = IDOMElement( Command );
-			DCommand.addEventListener( "KeyDown", KeyHandler( Command, HandleInput ) );
-			DCommand.addEventListener( "KeyUp", KeyHandler( Command, TestEmpty ) );
+			var KDown = KeyHandler( Command, HandleInput );
+			var KUp = KeyHandler( Command, TestEmpty );
+
+			DCommand.addEventListener( "KeyDown", KDown );
+			DCommand.addEventListener( "KeyUp", KUp );
+
+			destructor.Register( function() {
+				DCommand.removeEventListener( "KeyDown", KDown );
+				DCommand.removeEventListener( "KeyUp", KUp );
+			} );
+		};
+	};
+
+	var MasterInput = function( visualizer )
+	{
+		var Cands = {
+			"Article Reference": { module: "ArticleReference", desc: "Article reference link" }
+			, "facts": { module: "Facts", desc: "Facts, a fact bubble popup when mouseover" }
+			, "footnote": { module: "Footnote", desc: "Footnote, a footnote displayed at the end of article" }
+			, "h1": { module: "Heading", options: 1, desc: "Heading, size 1" }
+			, "h2": { module: "Heading", options: 2, desc: "Heading, size 2" }
+			, "h3": { module: "Heading", options: 3, desc: "Heading, size 3" }
+			, "h4": { module: "Heading", options: 4, desc: "Heading, size 4" }
+			, "h5": { module: "Heading", options: 5, desc: "Heading, size 5" }
+		};
+
+		var LoadModule = function( mod )
+		{
+			var ldr = new Loader( service_uri );
+			var ModItem = Cands[ mod ];
+			ldr.load( moduleNs + ModItem.module, function( e ) { ModuleLoaded( mod, e ); } );
+		};
+
+		var InputBox = null;
+		var ModuleLoaded = function( sender, e )
+		{
+			/** @type {Astro.Blog.AstroEdit.SmartInput.ICandidateAction} */
+			var module = new ( __import( e ) )( visualizer, sender );
+
+			var ModItem = Cands[ sender ];
+			InputBox.advanceLevel(
+				new Candidates( ModItem.module, ModItem.desc, module.GetCandidates() )
+				, module.Process.bind( module )
+				, module.Retreat.bind( module )
+			);
 		};
 
 		var BackQuoteBinding = function ( sender, e )
 		{
-			if( !SBarPresent && code == 192 )
+			if( !InputBox || InputBox.Disposed )
+			{
+				InputBox = new SmartInput( visualizer );
+				InputBox.advanceLevel(
+					// First level Candidates
+					new Candidates( "Quick Access", "Keyword", Cands )
+					, LoadModule
+					, function( sender, e ) { return sender.value == ""; }
+				);
+			}
+
+			if( !InputBox.Present && code == 192 )
 			{
 				e.preventDefault();
-				ShowSmartBar();
+				InputBox.Show();
 			}
 		};
 
 		IDOMObject( document ).addEventListener( "KeyDown", KeyHandler( document, BackQuoteBinding ), false );
 	};
 
-	ns[ NS_EXPORT ]( EX_CLASS, "SmartInput", SmartInput );
+	ns[ NS_EXPORT ]( EX_CLASS, "SmartInput", MasterInput );
 })();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Heading.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Heading.js
new file mode 100644
index 0000000..412ea79
--- /dev/null
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/Snippet/Heading.js
@@ -0,0 +1,177 @@
+(function ()
+{
+	var ns = __namespace( "Astro.Blog.AstroEdit.Visualizer.Snippet" );
+
+	/** @type {System.utils.IKey} */
+	var IKey                              = __import( "System.utils.IKey" );
+	/** @type {System.utils.DataKey} */
+	var DataKey                           = __import( "System.utils.DataKey" );
+	/** @type {Dandelion.IDOMElement} */
+	var IDOMElement                       = __import( "Dandelion.IDOMElement" );
+	/** @type {Dandelion} */
+	var Dand                              = __import( "Dandelion" );
+	/** @type {Components.MessageBox} */
+	var MessageBox                        = __import( "Components.MessageBox" );
+
+	var escapeStr = ns[ NS_INVOKE ]( "escapeStr" );
+	var compileProp = ns[ NS_INVOKE ]( "compileProp" );
+	
+	var heading = function ( insertSnippet, snippetWrap, createContext, override )
+	{
+		var temp, i, j
+		, Sizes = IKey.quickDef(
+			"h1", "h1"
+			, "h2", "h2"
+			, "h3", "h3"
+			, "h4", "h4"
+			, "h5", "h5"
+		)
+
+		// Private methods
+		, compileListItems = function ()
+		{
+			var arr = [];
+			for ( i in Sizes )
+			{
+				arr[ arr.length ] = Dand.wrapne(
+					"option"
+					, Sizes[i].keyName
+					, new IKey( "value", Sizes[i].keyValue )
+				);
+			}
+			return arr;
+		}
+		
+		// Snippet Class structure: handler & visualizer
+		, handler = function ()
+		{
+			// Input fields
+			var v_snippetInput = Dand.wrap('input', null, "v_snippet_input_single", null, new IKey("type", "text") )
+				, v_headingsize = Dand.wrap( "select", null, "v_select flsf", compileListItems() );
+			
+			if ( this._stage )
+			{
+				if ( this._size )
+				{
+					for ( i = 0; i < Sizes.length; i ++ )
+					{
+						if ( Sizes[i].keyValue == this._size )
+						{
+							v_headingsize.selectedIndex = i;
+						}
+					}
+				}
+
+				v_snippetInput.value = this._content || "";
+			}
+			else
+			{
+				// Remember the last choice
+				if ( typeof( this.pSnippeHeadingChoice ) == "number" )
+					v_headingsize.selectedIndex = this.pSnippeHeadingChoice;
+			}
+			
+			// Popup MessageBox
+			new MessageBox(
+				( this._stage ? "Edit" : "Insert" ) + " heading text"
+				, Dand.wrapc(
+					"v_trimmer"
+					, [
+						v_snippetInput
+						, Dand.wrapc( "v_instruction", v_headingsize )
+					]
+				)
+				, "OK", "Cancel"
+				// Switcher
+				, visualizer.bind({ heading:v_snippetInput, size: v_headingsize, stage: this._stage })
+			).show();
+		}
+
+		, visualizer = function ( submitted, override )
+		{
+			var size, heading
+			, stage = this.stage;
+			
+			if ( override )
+			{
+				size = override.size;
+				heading = override.value;
+			}
+			else
+			{
+				size = this.size[this.pSnippeHeadingChoice = this.size.selectedIndex].value;
+				heading = this.heading.value;
+			}
+
+			var size;
+			for( var i in Sizes )
+			{
+				if( Sizes[i].keyValue == size )
+				{
+					size = Sizes[i].keyName;
+					break;
+				}
+			}
+
+			if ( submitted && heading )
+			{
+				if ( !stage )
+				{
+					// Visualize component
+					temp = Dand.wrapne( "span", Dand.wrapne( size, heading ), [
+						new DataKey( "value", heading )
+						, new DataKey( "size", size )
+					]);
+
+					insertSnippet( j = snippetWrap( "Heading", temp ), Boolean( override ) );
+				}
+				else
+				{
+					IDOMElement( stage ).setAttribute([
+						new DataKey( "value", heading )
+						, new DataKey( "size", size )
+					]);
+
+					stage.removeChild( stage.firstChild );
+					stage.appendChild(  Dand.wrapne( size, heading ) );
+
+					temp = stage;
+				}
+
+				i = { _size: size, _content: heading, _stage: temp };
+
+				// Set context menu
+				createContext( i, j, handler );
+			}
+		}
+		;
+		
+		if ( override )
+		{
+			visualizer( true, override );
+			override = false;
+		}
+		else
+		{
+			return handler;
+		}
+		return true;
+	};
+	
+	var compile = function ( stage )
+	{
+		var element = IDOMElement( stage )
+		, props = [ "size" ];
+		
+		return "[heading"
+			+ compileProp( element, props )
+			+  "]"
+			+ escapeStr( element.getDAttribute( "value" ) )
+			+ "[/heading]"
+			;
+	};
+
+	__static_method( heading, "compile", compile );
+
+	ns[ NS_EXPORT ]( EX_CLASS, "Heading", heading );
+})();
diff --git a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js
index 9b7c23e..8941ac1 100644
--- a/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js
+++ b/botanjs/src/Astro/Blog/AstroEdit/Visualizer/_this.js
@@ -37,6 +37,7 @@
 		, "AcquireLib" , "background: black;"
 		, "Html"       , "background: coral;"
 		, "SiteFile"   , "background: royalblue;"
+		, "Heading"    , ""
 	);
 
 	var snippetNs = "Astro.Blog.AstroEdit.Visualizer.Snippet.";
diff --git a/botanjs/src/Astro/Blog/AstroEdit/_this.js b/botanjs/src/Astro/Blog/AstroEdit/_this.js
index 91e4ff1..4442063 100644
--- a/botanjs/src/Astro/Blog/AstroEdit/_this.js
+++ b/botanjs/src/Astro/Blog/AstroEdit/_this.js
@@ -27,6 +27,8 @@
 	// calls the smart bar
 	var SmartInput                                 = __import( "Astro.Blog.AstroEdit.SmartInput" );
 
+	// __import( "Astro.Blog.SharedStyle" ); CSS_RESERVATION
+
 	var wh, ww, cw, html, article;
 
 	var init = function ()
diff --git a/botanjs/src/Astro/Blog/Layout/MainFrame.css b/botanjs/src/Astro/Blog/Layout/MainFrame.css
index cc9a361..8669d98 100644
--- a/botanjs/src/Astro/Blog/Layout/MainFrame.css
+++ b/botanjs/src/Astro/Blog/Layout/MainFrame.css
@@ -29,14 +29,6 @@ sup { vertical-align: super; }
 
 a:hover { text-decoration: underline; }
 
-h1, h2, h3, h4, .h1, .h2, .h3, .h4 { font-family: custom-serif; }
-h1, .h1 { font-size: 2.5em; }
-h2, .h2 { font-size: 2em; }
-h3, .h3 { font-size: 1.5em; }
-h4, .h4 { font-size: 1em; }
-h5, .h5 { font-size: 0.8em; }
-h6, .h6 { font-size: 0.5em; }
-
 .clearfix {
     clear: both;
     padding: 0 !important;
diff --git a/botanjs/src/Astro/Blog/Layout/MainFrame.js b/botanjs/src/Astro/Blog/Layout/MainFrame.js
index 10aba98..70837b4 100644
--- a/botanjs/src/Astro/Blog/Layout/MainFrame.js
+++ b/botanjs/src/Astro/Blog/Layout/MainFrame.js
@@ -28,6 +28,7 @@
 
 	// __import( "Dandelion.CSSReset" ); CSS_RESERVATION
 	// __import( "Dandelion.CSSAnimations" ); CSS_RESERVATION
+	// __import( "Astro.Blog.SharedStyle" ); CSS_RESERVATION
 	// __import( "Astro.Blog.Element.Layer" ); CSS_RESERVATION
 
 	/** @function {System.Net.getData} */
diff --git a/botanjs/src/Astro/Blog/SharedStyle.css b/botanjs/src/Astro/Blog/SharedStyle.css
new file mode 100644
index 0000000..78d6e56
--- /dev/null
+++ b/botanjs/src/Astro/Blog/SharedStyle.css
@@ -0,0 +1,7 @@
+h1, h2, h3, h4, .h1, .h2, .h3, .h4 { font-family: custom-serif; }
+h1, .h1 { font-size: 2.5em; }
+h2, .h2 { font-size: 2em; }
+h3, .h3 { font-size: 1.5em; }
+h4, .h4 { font-size: 1em; }
+h5, .h5 { font-size: 0.8em; }
+h6, .h6 { font-size: 0.5em; }
diff --git a/botanjs/src/Astro/Blog/SharedStyle.js b/botanjs/src/Astro/Blog/SharedStyle.js
new file mode 100644
index 0000000..62e2e73
--- /dev/null
+++ b/botanjs/src/Astro/Blog/SharedStyle.js
@@ -0,0 +1,5 @@
+/*
+(function(){
+	var ns = __namespace( "Astro.Blog.SharedStyle" );
+})();
+*/
diff --git a/botanjs/src/externs/Astro.Blog.AstroEdit.SmartInput.ICandidateAction.js b/botanjs/src/externs/Astro.Blog.AstroEdit.SmartInput.ICandidateAction.js
index 1ab9e35..59c2d04 100644
--- a/botanjs/src/externs/Astro.Blog.AstroEdit.SmartInput.ICandidateAction.js
+++ b/botanjs/src/externs/Astro.Blog.AstroEdit.SmartInput.ICandidateAction.js
@@ -4,3 +4,5 @@ Astro.Blog.AstroEdit.SmartInput.ICandidateAction = function(){};
 Astro.Blog.AstroEdit.SmartInput.ICandidateAction.GetCandidates;
 /** @type {function} */
 Astro.Blog.AstroEdit.SmartInput.ICandidateAction.Process;
+/** @type {function} */
+Astro.Blog.AstroEdit.SmartInput.ICandidateAction.Retreat;