(function(){ var ns = __namespace( "Astro.Blog.AstroEdit" ); /** @type {typeof Dandelion} */ var Dand = __import( "Dandelion" ); /** @type {function(...?): Dandelion.IDOMElement} */ var IDOMElement = __import( "Dandelion.IDOMElement" ); /** @type {Dandelion.IDOMObject} */ var IDOMObject = __import( "Dandelion.IDOMObject" ); /** @type {System.Cycle} */ var Cycle = __import( "System.Cycle" ); /** @type {System.Debug} */ var debug = __import( "System.Debug" ); /** @type {System.Net.ClassLoader} */ var Loader = __import( "System.Net.ClassLoader" ); /** @type {System.utils} */ var utils = __import( "System.utils" ); /** @type {typeof System.utils.DataKey} */ var DataKey = __import( "System.utils.DataKey" ); /** @type {typeof System.utils.IKey} */ var IKey = __import( "System.utils.IKey" ); /** @type {typeof Components.MessageBox} */ var MessageBox = __import( "Components.MessageBox" ); /** @type {Astro.Blog.Config} */ var Config = __import( "Astro.Blog.Config" ); var moduleNs = "Astro.Blog.AstroEdit.SmartInput.CandidateAction."; var service_uri = Config.get( "ServiceUri" ); var code; 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 ) { e = e || window.event; if ( e.keyCode ) code = e.keyCode; else if ( e.which ) code = e.which; handler( sender, e ); }; }; // {{{ Candidates Class var Candidates = function( title, desc, defs ) { this.title = title; this.desc = desc; this.__cands = []; /** @type Array */ this.__defs = defs || {}; this.Empty = !defs; }; Candidates.prototype.Get = function() { if( this.__cands.length ) return this.__cands; for( var i in this.__defs ) { var c = this.__defs[i]; this.__cands.push( Dand.wrapc( "cn", [ i, Dand.wrapc( "desc", c.desc ) ], new DataKey( "key", i ) ) ); } return this.__cands; }; 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 ) { var c = IDOMElement( Cands[i] ); c.setAttribute( new DataKey( "selected", 0 ) ); if( c.style.display != "none" ) { selected.push( c ); } } return selected; }; Candidates.prototype.Selected = function() { var Cands = this.Get(); for( var i in Cands ) { var c = IDOMElement( Cands[i] ); if( c.getDAttribute( "selected" ) == "1" ) { return c; } } return null; }; // }}} /** @param {Astro.Blog.AstroEdit.Visualizer} 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 BindingBox = null; 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 ) { if( !reload && __title ) return __title; var l = ModLevels.length - 1; var t = ModLevels[ l ][0].title; for( var i = 0; i < l; i ++ ) { var Mod = ModLevels[i]; t = t + " > " + Mod[0].title; } if( __title ) { __title.textContent = t; } else { __title = Dand.wrape( t ); } return __title; }; var __stage = null; var stage = function( incoming ) { if( __stage ) { if( incoming ) { IDOMElement( __stage[1] ).clear(); var ThisCands = ModLevels.Cands().Get(); if( ThisCands.length ) { IDOMElement( __stage[1] ).loot( Dand.wrape( ThisCands ) ); } } return __stage; } var Cands = ModLevels.Cands(); var Command = Dand.wrap( "input", null, "v_snippet_input_single" , null, IKey.quickDef( "type", "text", "placeholder", Cands.desc, "value", "`" ) ); var CandList = Dand.wrap( "div", null, "compx smartbar-candidates", Cands.Get() ); Command.selectionStart = Command.selectionEnd = 1; 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 if( e.shiftKey || e.ctrlKey ) { // Except the Shift + Tab, we need to cycle this shiftTabbed = e.shiftKey && e.keyCode == 9; if( !shiftTabbed ) return; } 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; // Hitting ` twice escapes the ` character itself var v = sender.value.substr( 1 ); if( v == "" ) { insert = function() { return Dand.textNode( "`" ); }; BindingBox.close( true ); break; } BindingBox.close(); // Insert the code snippet with inline flag visualizer.insertSnippet( "code", { "inline": "on", "lang": "plain", "value": v } ); break; case 13: // Enter // Not closing the quote, either a direct text or the first matched action e.preventDefault(); // No candidates, directly pass the input text to the processor if( ModLevels.Cands().Empty && 1 < ModLevels.length ) { ModLevels.Action()( sender.value.substr( 1 ) ); BindingBox.close(); break; } var selected = ModLevels.Cands().Selected(); // Check if matched an action first if( selected ) { insert = undefined; var close = ModLevels.Action()( selected.getDAttribute( "key" ) ); if( close ) BindingBox.close(); } else { // Insert this text directly var v = Dand.textNode( sender.value.substr( 1 ) ); insert = function() { return v; }; BindingBox.close( true ); } break; case 27: // Esc BindingBox.close(); break; case 9: // Tab // Hitting tab will cycle around the candidates e.preventDefault(); var c = ModLevels.Cands().Filtered(); var l = c.length; if( !l ) break; CandidateCycle += e.shiftKey ? -1 : 1; if( CandidateCycle == l ) { CandidateCycle = 0; } else if( CandidateCycle == -1 ) { CandidateCycle = c.length - 1; } var ThisCandidate = c[ CandidateCycle ]; ThisCandidate.setAttribute( new DataKey( "selected", 1 ) ); 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: ModLevels.Cands().Filtered(); CandidateCycle = -1; } }; var TestEmpty = function( sender, e ) { if( ModLevels.Retreat()( sender, e ) ) { _self.RetreatLevel( sender ); if( !ModLevels.length ) return; } // Search exact matched Candidates switch( e.keyCode ) { case 9: // Tab, do nothing break; case 16: // Shift, check if shiftTabbed if( shiftTabbed ) { shiftTabbed = false; break; } default: if( CandidateCycle == -1 ) { KeywordTyped = sender.value.length; } var c = ModLevels.Cands().Get(); var keyword = sender.value.substr( 1 ); for( var i in c ) { var Cand = IDOMElement( c[i] ); var t = Cand.getDAttribute( "key" ); Cand.style.display = t.match( new RegExp( keyword, "i" ) ) ? "" : "none"; // Highlight the exact match if( t.match( new RegExp( "^" + keyword + "$", "i" ) ) ) { Cand.setAttribute( new DataKey( "selected", 1 ) ); } } } }; var ClosePanel = function( confirmed ) { visualizer.restoreSelection(); if( confirmed && insert != undefined ) visualizer.insertAtCaret( insert() ); // Posponing this prevents the BackQuoteBinding firing Cycle.next( function() { _self.Present = false; destructor.Destruct(); } ); }; // Advance the input level by CandidateAction this.advanceLevel = function( Candidates, Action, Retreat ) { if( ModLevels.length ) { ModLevels[0].selected = ModLevels.Cands().Selected().getDAttribute( "key" ); } ModLevels.unshift([ Candidates, Action, Retreat ]); if( 1 < ModLevels.length ) { CandidateCycle = -1; stage( Candidates ); title( true ); stage()[0].value = "`"; stage()[0].setAttribute( "placeholder", ModLevels.Cands().desc ); BindingBox.show(); } }; this.RetreatLevel = function( sender ) { if( ModLevels.length == 1 ) { BindingBox.close(); destructor.Destruct(); } ModLevels.shift(); 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; BindingBox.show(); } }; this.Show = function() { if( _self.Present ) return; visualizer.saveSelection(); _self.Present = true; var MsgBox = new MessageBox( title() , Dand.wrape( stage() ) , "Back", false , ClosePanel ); MsgBox.show(); var Command = stage()[0]; BindingBox = MsgBox; Command.focus(); var DCommand = IDOMElement( Command ); 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: "Links to other article" } , "Article Content": { module: "ArticleContent", desc: "Put contents of an article" } , "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, "o" ); 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 ]; module.GetCandidates( function( x ) { InputBox.advanceLevel( new Candidates( ModItem.module, ModItem.desc, x ) , module.Process.bind( module ) , module.Retreat.bind( module ) ); } ); }; var BackQuoteBinding = function ( sender, e ) { 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(); InputBox.Show(); } }; IDOMObject( document ).addEventListener( "KeyDown", KeyHandler( document, BackQuoteBinding ), false ); }; ns[ NS_EXPORT ]( EX_CLASS, "SmartInput", MasterInput ); })();