aboutsummaryrefslogtreecommitdiff
path: root/js/components/hintable.reel/hintable.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/components/hintable.reel/hintable.js')
-rw-r--r--js/components/hintable.reel/hintable.js360
1 files changed, 360 insertions, 0 deletions
diff --git a/js/components/hintable.reel/hintable.js b/js/components/hintable.reel/hintable.js
new file mode 100644
index 00000000..79813c92
--- /dev/null
+++ b/js/components/hintable.reel/hintable.js
@@ -0,0 +1,360 @@
1/* ComputedStyleSubPanel.js */
2var Montage = require("montage").Montage,
3 Component = require("montage/ui/component").Component,
4 Editable = require("js/components/editable.reel").Editable;
5
6
7/*
8
9EDITABLE - Methods
10- startEdit
11- stopEdit
12- value
13-
14- _suggest
15- _suggestNext
16- _suggestPrev
17- _clearSuggest
18- _accept
19- _revert
20- _setCaret
21
22*/
23
24
25exports.Hintable = Montage.create(Editable, {
26 inheritsFrom : { value : Editable },
27 _matchIndex : { value : 0 },
28 matches : { value : [] },
29
30 _hint : { value : null },
31 hint : {
32 get : function() {
33 return this._hint;
34 },
35 set : function(hint) {
36 hint = hint || '';
37
38 ///// Set the hint element's text
39 this._getFirstTextNode(this.hintElement).textContent = hint;
40 ///// if hintElement was removed from the DOM, the object still
41 ///// exists, so it needs to be re-appended
42 if(this.hintElement.parentNode === null) {
43 this._element.appendChild(this.hintElement);
44 }
45
46 this._hint = hint;
47 }
48 },
49
50 _hintElement : { value : null },
51 hintElement : {
52 get : function() {
53 if(!this._hintElement) {
54 /// Remove the phantom "<BR>" element that is generated when
55 /// content editable element is empty
56 this._children(this._element, function(item) {
57 return item.nodeName === 'BR';
58 }).forEach(function(item) {
59 this._element.removeChild(item);
60 }, this);
61
62 this._hintElement = document.createElement('span');
63 this._hintElement.classList.add(this.hintClass);
64
65 this._element.appendChild(this._hintElement);
66 }
67
68 return this._hintElement;
69 },
70 set : function(el) {
71 this._hintElement = el;
72 }
73 },
74
75 _getHintDifference : {
76 value : function() {
77 if(!this.matches[this._matchIndex]) {
78 debugger;
79 }
80 return this.matches[this._matchIndex].substr(this.value.length);
81 }
82 },
83
84 hintNext : {
85 value : function(e) {
86 if(e) { e.preventDefault(); }
87 console.log('next1');
88
89 if(this._matchIndex < this.matches.length - 1) {
90 console.log('next');
91 ++this._matchIndex;
92 this.hint = this._getHintDifference();
93 }
94 }
95 },
96 hintPrev : {
97 value : function(e) {
98 if(e) { e.preventDefault(); }
99 console.log('prev1');
100 if(this._matchIndex !== 0) {
101 console.log('prev');
102 --this._matchIndex;
103 this.hint = this._getHintDifference();
104 }
105 }
106 },
107
108 accept : {
109 value: function(e, preserveCaretPosition) {
110 if(e) {
111 e.preventDefault();
112 }
113 var fullText = this._hint;
114 this.hint = null;
115 this.value += fullText;
116
117 if(!preserveCaretPosition) {
118 this.setCursor('end');
119 }
120
121 this._sendEvent('accept');
122 }
123 },
124 revert : {
125 value : function(e, forceRevert) {
126 this.hint = null;
127
128 if(this.isEditable || forceRevert) {
129 /// revert to old value
130 this.value = (this._preEditValue);
131 this._sendEvent('revert');
132 console.log('reverting');
133
134 }
135 }
136 },
137 value : {
138 get: function() {
139 return this._getFirstTextNode().textContent;
140 },
141 set: function(str) {
142 var node = this._getFirstTextNode();
143 node.textContent = str;
144 }
145 },
146
147 handleKeydown : {
148 value : function handleKeydown(e) {
149 var k = e.keyCode,
150 isCaretAtEnd, selection, text;
151
152 this._super(arguments);
153
154 if(k === 39) {
155 selection = window.getSelection();
156 text = selection.baseNode.textContent;
157 isCaretAtEnd = (selection.anchorOffset === text.length);
158 }
159
160 if(this.hint && isCaretAtEnd) {
161 ///// Advance the cursor
162 this.hint = this.hint.substr(0, 1);
163 this.accept(e);
164 this.handleInput();
165 }
166
167 this._execKeyAction(e);
168 }
169 },
170 ///// Text input has changed values
171 handleInput : {
172 value : function handleInput(e) {
173 this._super(arguments);
174
175 var val = this.value,
176 matches, hint;
177 console.log('val = "' + val + '"');
178 //// Handle auto-suggest if configured
179 if(this.hints instanceof Array) {
180
181 if(val.length > 0) { // content is not empty
182
183 this._matchIndex = 0;
184 this.matches = this.hints.filter(function(h) {
185 return h.indexOf(val) === 0;
186 }).sort();
187
188 ///// If there are no matches, or the new value doesn't match all the
189 ///// previous matches, then get new list of matches
190 if(!this.matches.length || !this._matchesAll(val)) {
191 }
192
193 if(this.matches.length) { // match(es) found
194 if(this.matches[this._matchIndex] !== val) {
195 // Suggest the matched hint, subtracting the typed-in string
196 // Only if the hint is not was the user has typed already
197 this.hint = this._getHintDifference();
198 } else {
199 this.hint = null;
200 }
201 } else { // no matches found
202 this.hint = null;
203 }
204 } else { // no suggestion for empty string
205 this.hint = null;
206 }
207
208 }
209 }
210 },
211 handleBackspace : {
212 value : function(e) {
213 this.matches.length = 0;
214 }
215 },
216 _matchesAll : {
217 value : function(value) {
218 return this.matches.every(function(match) {
219 return match.indexOf(value) === 0;
220 }, this);
221 }
222 },
223 _execKeyAction : {
224 value : function(e) {
225 var key = e.keyCode,
226 keys = this.keyActions;
227
228 if(this.hint) {
229 if( keys.hint.revert.indexOf(key) !== -1 ) { this.revert(e); }
230 if( keys.hint.accept.indexOf(key) !== -1 ) { this.accept(e); }
231 if( keys.hint.stop.indexOf(key) !== -1 ) { this.stop(e); }
232 if( keys.hint.next.indexOf(key) !== -1 ) { this.hintNext(e); }
233 if( keys.hint.prev.indexOf(key) !== -1 ) { this.hintPrev(e); }
234 if( keys.hint.backsp.indexOf(key) !== -1 ) { this.handleBackspace(e); }