# HG changeset patch # User jonathandicarlo@jonathan-dicarlos-macbook-pro.local # Date 1210709065 25200 # Node ID 4ea09b7ce82088aaa588cecc0d0f763367ec5d81 # Parent cf0f9e6d383d0f0f306bfb61d4d5690e63613eac Added verbs and nouns and up/down arrows and space-to-autocomplete and suggestion list and escape-to-clear. diff -r cf0f9e6d383d -r 4ea09b7ce820 hip.css --- a/hip.css Mon May 12 13:53:10 2008 -0700 +++ b/hip.css Tue May 13 13:04:25 2008 -0700 @@ -11,3 +11,7 @@ font: 14pt Helvetica; padding: 4px; } + +.hilited { + background: #00aa00; +} \ No newline at end of file diff -r cf0f9e6d383d -r 4ea09b7ce820 hip.js --- a/hip.js Mon May 12 13:53:10 2008 -0700 +++ b/hip.js Tue May 13 13:04:25 2008 -0700 @@ -1,39 +1,317 @@ -Array.prototype.tagify = function(tagName) { - var result = ""; + +function NounType( name, expectedWords ) { + this._init( name, expectedWords ); + } +NounType.prototype = { + _init: function( name, expectedWords ) { + this._name = name; + this._expectedWords = expectedWords; // an array + }, + + match: function( fragment ) { + var suggs = this.suggest( fragment ); + // klugy! + if ( suggs.length > 0 ) { + return true; + } + return false; + }, + + suggest: function( fragment ) { + // returns (ordered) array of suggestions + var suggestions = []; + for ( var x in this._expectedWords ) { + word = this._expectedWords[x]; + if ( word.indexOf( fragment ) > -1 ) { + suggestions.push( word ); + // TODO sort these in order of goodness + // todo if fragment is multiple words, search for each of them + // separately within the expected word. + } + } + return suggestions; + } +}; + +// for example.... +var city = new NounType( "city", [ "new york", "los angeles", "mexico city", "sao paulo", "rio de janeiro", "buenos aires", "london", "paris", "moscow", "cairo", "lagos", "tehran", "karachi", "mumbai", "delhi", "kolkata", "jakarta", "manila", "bejing", "singapore", "shanghai", "hong kong", "seoul", "tokyo", "osaka" ] ); + +var language = new NounType( "language", [ "english", "chinese", "hindi", "japanese", "klingon", "esperanto", "sanskrit", "pig latin", "tagalog", "portugese" ] ); + +var tab = new NounType( "tab", [ "gmail", "mozilla developer connection", "xulplanet", "evilbrainjono.net", "google calendar", "humanized enso forum" ] ); + +var person = new NounType( "person", ["atul@mozilla.com", "aza@mozilla.com", "thunder@mozilla.com", "chris@mozilla.com", "myk@mozilla.com" ] ); + +var anyWord = { + // a singleton object which can be used in place of a NounType. + match: function( fragment ) { + return true; + }, + suggest: function( fragment ) { + return [ fragment ]; + } +}; + +function ParsedSentence( rawText, verb, DO, DOType, modifiers ) { + this._init( rawText, verb, DO, DOType, modifiers ); +} +ParsedSentence.prototype = { + _init: function( rawText, verb, DO, DOType, modifiers ) { + + // needs state to represent what's locked in so far. + }, + + lockInLatestWord: function( selectedCompletion ) { + // + + }, + + getCompletions: function() { + }, + + getDescription: function() { + // returns a string describing what the sentence will do if executed + }, + +}; + + +function Verb( name, DOLabel, DOType, modifiers ) { + this._init( name, DOLabel, DOType, modifiers ); +} +Verb.prototype = { + _init: function( name, DOLabel, DOType, modifiers ) { + this._name = name; + this._DOLabel = DOLabel; + this._DOType = DOType; // must be a NounType. + this._modifiers = modifiers; + // modifiers should be a dictionary + // keys are prepositions + // values are NounTypes. + // example: { "from" : City, "to" : City, "on" : Day } + }, - for (var i = 0; i < this.length; i++) { - result += ("<" + tagName + ">" + this[i] + - "" ); + getCompletions: function( words ) { + /* words is an array of words that were space-separated. + already determined that words[0] matches this verb; + Everything after that is either: + 1. my direct object + 2. a preposition + 3. a noun following a preposition. + */ + var predicate = ""; + var completions = []; + var suggestions = []; + var x; + var y; + + // TODO pull out things that might be modifiers, try using + // the remainder as the direct object. + + for ( x = 1; x < words.length; x++ ) { + // a horrible way of reassembling input minus verb + predicate = predicate + words[x]; + if ( x < words.length - 1 ) { + predicate = predicate + " "; + } } - return result; -} + + // first approx: just try out each word as direct object + if ( this._DOType == null ) { + // No direct object accepted!! + if ( predicate.length == 0 ) { + return [this._name]; + } else { + return []; + } + } else if ( this._DOType.match( predicate ) ){ + // direct object accepts whole input at once? + suggestions = this._DOType.suggest( predicate ); + for( y in suggestions ) { + completions.push( this._name + " " + suggestions[y] ); + } + return completions; + } else { + // try each word as the direct object + for ( x=1; x this._suggestionList.length ) { + this._hilitedSuggestion = 0; + } + }, + + indicationUp: function() { + this._hilitedSuggestion --; + if ( this._hilitedSuggestion < 0 ) { + this._hilitedSuggestion = this._suggestionList.length; + } + }, - for (name in methods) { - this[name] = methods[name]; + getHilitedSuggestion: function() { + return this._hilitedSuggestion - 1; // because 0 means no hilite + // and the suggestion list starts at 1... fencepost! + }, + + autocomplete: function( query ) { + var hilited = this.getHilitedSuggestion(); + if ( hilited > -1 ) { + var newText = this._suggestionList[ hilited ] + " "; + } else { + newText = query; } -} + return newText; + }, + + clear: function() { + this._suggestionList = []; + this._hilitedSuggestion = 0; + lockedInSentence = null; + } +}; var gQs = new QuerySource(); +function makeSuggestionHtml( tagName, list, hilitedNumber ) { + var result = ""; + var openingTag = ""; + var closingTag = ""; + + for (var i = 0; i < list.length; i++) { + if ( i == hilitedNumber ) { + openingTag = "<" + tagName + " class=\"hilited\">"; + } else { + openingTag = "<" + tagName + ">"; + } + result += (openingTag + list[i] + closingTag ); + } + return result; +} + +function updateDisplay( ) { + var suggestions = gQs.getSuggestions(); + var hilitedSuggestion = gQs.getHilitedSuggestion(); + var ac = $("#autocomplete-popup"); + ac.html( makeSuggestionHtml( "div", suggestions, hilitedSuggestion ) ); + ac.show(); +} + function searchBoxQuery( event ) { - gQs.getSuggestions( - event.target.value, - function(suggestions) { - var ac = $("#autocomplete-popup"); - ac.html( suggestions.tagify("div") ); - ac.show(); - } - ); + // TODO: if the event is an 'esc' key, clear the input field. + // If the event is an 'up arrow' or 'down arrow' key, change the + // indication. + + // key is event.which + // esc is 27 + // up arrow is 38 + // down arrow is 40 + // enter is 13 + // space is 32 + switch( event.which ) { + case 27: //esc + event.target.value = ""; + gQs.clear(); + break; + case 38: // up arrow + gQs.indicationUp(); + break; + case 40: // down arrow + gQs.indicationDown(); + break; + case 13: // enter + gQs.execute(); + break; + case 32: // spacebar + event.target.value = gQs.autocomplete( event.target.value ); + break; + default: + gQs.updateSuggestionList( event.target.value ); + break; + // todo: delete key "unlocks" if you delete past a space? + } + + updateDisplay(); + } $(document).ready( function() {