diff --git a/js/web/input.js b/js/web/input.js new file mode 100644 index 0000000..3085db2 --- /dev/null +++ b/js/web/input.js @@ -0,0 +1,172 @@ +/* ------------------------------------------------------------------------* + * Copyright 2013-2014 Arne F. Claassen + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *-------------------------------------------------------------------------*/ + +var Josh = Josh || {}; +(function (root, $, _) { + $.fn.josh_caretTo = function (index) { + return this.queue(function (next) { + if (this.createTextRange) { + var range = this.createTextRange(); + range.move("character", index); + range.select(); + } else if (this.selectionStart !== null) { + this.setSelectionRange(index, index); + } + next(); + }); + }; + $.fn.josh_caretPosition = function () { + var el = this.get(0); + if (el.createTextRange) { + var range = el.createTextRange(); + range.moveStart('character', -el.value.length); + return range.text.length; + } else if (el.selectionStart !== null) { + return el.selectionStart; + } + return 0; + }; + + var history = Josh.History(); + var killring = new Josh.KillRing(); + + Josh.Input = function (config) { + config = config || {}; + + // instance fields + var _console = config.console || (Josh.Debug && root.console ? root.console : { + log: function () { + } + }); + + var _id = "#" + config.id; + var _blinktime = config.blinktime || 500; + var _active = false; + var _cursor_visible = false; + var _isInput = false; + var _history = config.history || history; + var _killring = config.killring || killring; + var _text; + var self = { + templates: { + span: _.template('') + }, + history: _history, + killring: _killring + }; + + $(document).ready(function () { + var $input = $(_id); + var el = $input.get(0); + var readline = new Josh.ReadLine({ + history: _history, + killring: _killring, + console: _console + }); + self.readline = readline; + readline.attach(el); + var activate = null; + _isInput = $input.is('input'); + if (_isInput) { + + _console.log(_id + ' is an input'); + + function renderInput(line) { + var text = line ? line.text : ''; + _text = text; + $input.val(text); + $input.josh_caretTo(line.cursor); + } + readline.onChange(renderInput); + $input.click(function() { + var line = readline.getLine(); + line.cursor = $input.josh_caretPosition(); + readline.setLine(line); + }); + + activate = function() { + // Note: have to re-render with a setTimeout, because on focus, but after the onfocus event is processed, + // the input will select all, invalidating our render + setTimeout(function() { + renderInput(readline.getLine()); + }, 0); + }; + } else { + _console.log(_id + ' is a non-input element'); + $input.html(self.templates.span()); + if(typeof $input.attr('tabindex') === 'undefined') { + $input.attr('tabindex',0); + } + var $left = $input.find('.left'); + var $right = $input.find('.right'); + var $cursor = $input.find('.cursor'); + + function renderSpan(line) { + var text = line.text || ''; + _text = text; + var cursorIdx = line.cursor || 0; + var left = _.escape(text.substr(0, cursorIdx)).replace(/ /g, ' '); + var cursor = text.substr(cursorIdx, 1); + var right = _.escape(text.substr(cursorIdx + 1)).replace(/ /g, ' '); + $left.html(left); + if (!cursor) { + $cursor.html(' ').css('textDecoration', 'underline'); + } else { + $cursor.text(cursor).css('textDecoration', 'underline'); + } + $right.html(right); + } + + function blinkCursor() { + if (!_active) { + return; + } + root.setTimeout(function () { + if (!_active) { + return; + } + _cursor_visible = !_cursor_visible; + if (_cursor_visible) { + $cursor.css('textDecoration', 'underline'); + } else { + $cursor.css('textDecoration', ''); + } + blinkCursor(); + }, _blinktime); + } + + activate = function () { + blinkCursor(); + } + readline.onChange(renderSpan); + } + readline.unbind({keyCode: Josh.Keys.Special.Tab}); + readline.unbind({'char': 'R', ctrlKey: true}); + readline.onActivate(function () { + _active = true; + activate(); + }); + readline.onDeactivate(function () { + _active = false; + if (_text) { + _history.accept(_text); + } + }); + + }); + return self; + } +})(this, $, _);