/* ComputedStyleSubPanel.js */ var Montage = require("montage").Montage, Component = require("montage/ui/component").Component, Editable = require("js/components/editable.reel").Editable; /* EDITABLE - Methods - startEdit - stopEdit - value - - _suggest - _suggestNext - _suggestPrev - _clearSuggest - _accept - _revert - _setCaret */ exports.Hintable = Montage.create(Editable, { inheritsFrom : { value : Editable }, _matchIndex : { value : 0 }, matches : { value : [] }, _hint : { value : null }, hint : { get : function() { return this._hint; }, set : function(hint) { hint = hint || ''; ///// Set the hint element's text this._getFirstTextNode(this.hintElement).textContent = hint; ///// if hintElement was removed from the DOM, the object still ///// exists, so it needs to be re-appended if(this.hintElement.parentNode === null) { this._element.appendChild(this.hintElement); } this._hint = hint; } }, _hintElement : { value : null }, hintElement : { get : function() { if(!this._hintElement) { /// Remove the phantom "
" element that is generated when /// content editable element is empty this._children(this._element, function(item) { return item.nodeName === 'BR'; }).forEach(function(item) { this._element.removeChild(item); }, this); this._hintElement = document.createElement('span'); this._hintElement.classList.add(this.hintClass); this._element.appendChild(this._hintElement); } return this._hintElement; }, set : function(el) { this._hintElement = el; } }, _getHintDifference : { value : function() { if(!this.matches[this._matchIndex]) { debugger; } return this.matches[this._matchIndex].substr(this.value.length); } }, hintNext : { value : function(e) { if(e) { e.preventDefault(); } console.log('next1'); if(this._matchIndex < this.matches.length - 1) { console.log('next'); ++this._matchIndex; this.hint = this._getHintDifference(); } } }, hintPrev : { value : function(e) { if(e) { e.preventDefault(); } console.log('prev1'); if(this._matchIndex !== 0) { console.log('prev'); --this._matchIndex; this.hint = this._getHintDifference(); } } }, accept : { value: function(e, preserveCaretPosition) { if(e) { e.preventDefault(); } var fullText = this._hint; this.hint = null; this.value += fullText; if(!preserveCaretPosition) { this.setCursor('end'); } this._sendEvent('accept'); } }, revert : { value : function(e, forceRevert) { this.hint = null; if(this.isEditable || forceRevert) { /// revert to old value this.value = (this._preEditValue); this._sendEvent('revert'); console.log('reverting'); } } }, value : { get: function() { return this._getFirstTextNode().textContent; }, set: function(str) { var node = this._getFirstTextNode(); node.textContent = str; } }, handleKeydown : { value : function handleKeydown(e) { var k = e.keyCode, isCaretAtEnd, selection, text; this._super(arguments); if(k === 39) { selection = window.getSelection(); text = selection.baseNode.textContent; isCaretAtEnd = (selection.anchorOffset === text.length); } if(this.hint && isCaretAtEnd) { ///// Advance the cursor this.hint = this.hint.substr(0, 1); this.accept(e); this.handleInput(); } this._execKeyAction(e); } }, ///// Text input has changed values handleInput : { value : function handleInput(e) { this._super(arguments); var val = this.value, matches, hint; console.log('val = "' + val + '"'); //// Handle auto-suggest if configured if(this.hints instanceof Array) { if(val.length > 0) { // content is not empty this._matchIndex = 0; this.matches = this.hints.filter(function(h) { return h.indexOf(val) === 0; }).sort(); ///// If there are no matches, or the new value doesn't match all the ///// previous matches, then get new list of matches if(!this.matches.length || !this._matchesAll(val)) { } if(this.matches.length) { // match(es) found if(this.matches[this._matchIndex] !== val) { // Suggest the matched hint, subtracting the typed-in string // Only if the hint is not was the user has typed already this.hint = this._getHintDifference(); } else { this.hint = null; } } else { // no matches found this.hint = null; } } else { // no suggestion for empty string this.hint = null; } } } }, handleBackspace : { value : function(e) { this.matches.length = 0; } }, _matchesAll : { value : function(value) { return this.matches.every(function(match) { return match.indexOf(value) === 0; }, this); } }, _execKeyAction : { value : function(e) { var key = e.keyCode, keys = this.keyActions; if(this.hint) { if( keys.hint.revert.indexOf(key) !== -1 ) { this.revert(e); } if( keys.hint.accept.indexOf(key) !== -1 ) { this.accept(e); } if( keys.hint.stop.indexOf(key) !== -1 ) { this.stop(e); } if( keys.hint.next.indexOf(key) !== -1 ) { this.hintNext(e); } if( keys.hint.prev.indexOf(key) !== -1 ) { this.hintPrev(e); } if( keys.hint.backsp.indexOf(key) !== -1 ) { this.handleBackspace(e); } } else { if(keys.noHint.revert.indexOf(key) !== -1) { this.revert(e); } if(keys.noHint.stop.indexOf(key) !== -1) { this.stop(e); } //if( keys.hint.next.indexOf(key) !== -1 ) { this.handleDown(e); } //if( keys.hint.prev.indexOf(key) !== -1 ) { this.handleUp(e); } //if( keys.hint.backsp.indexOf(key) !== -1 ) { this.backspace(e); } } } }, /* --------------- Utils --------------- */ _children : { value : function(el, filter) { var f = filter || function(item) { return item.nodeType === 1; }; return this._toArray(el.childNodes).filter(f); } }, _toArray : { value : function(arrayLikeObj) { return Array.prototype.slice.call(arrayLikeObj); } }, _getFirstTextNode : { value : function(el) { ///// optional el argument specified container element var e = el || this._element, nodes = e.childNodes, node; if(nodes.length) { for(var i=0; i" element that is generated when // /// content editable element is empty // this._children(this._element, function(item) { // return item.nodeName === 'BR'; // }).forEach(function(item) { // this._element.removeChild(item); // }, this); // // this.hintElement = document.createElement('span'); // this.hintElement.classList.add(this.suggestClass); // this.hintElement.appendChild(document.createTextNode(hint)); // this._element.appendChild(this.hintElement); // } // // this._hint = hint; // } // },