NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
/* * Project: Long Press * Description: Pops a list of alternate characters when a key is long-pressed * Author: Quentin Thiaucourt, http://toki-woki.net * Licence: MIT License http://opensource.org/licenses/mit-license.php */ /* Fork by Kasper Peulen to make the lay-out very similar to the OSX press and hold app. */ var strVar = ""; strVar += "<style>"; strVar += ""; strVar += ".long-press-popup {"; strVar += " -moz-box-shadow: 0px 1px 3px 1px lightgray;"; strVar += "-webkit-box-shadow: 0px 1px 3px 1px lightgray;"; strVar += "box-shadow: 0px 2px 3px 0px lightgrey;"; strVar += " font-family: Helvetica;"; strVar += " position: relative;"; strVar += " top:20px;"; strVar += " left:-10px;"; strVar += " padding: 2px;"; strVar += " margin: 0px;"; strVar += " background: white;"; strVar += " font-size: 12pt;"; strVar += " border: 1px #C2D7FE solid;"; strVar += " list-style-type: none;"; strVar += " display: inline-block;"; strVar += " border-radius: 5px;"; strVar += "}"; strVar += ".long-press-popup li {"; strVar += "position:relative;"; strVar += " border: 1px white solid;"; strVar += " border-radius: 4px;"; strVar += " background: white;"; strVar += " color: #0A7ED0;"; strVar += " display: inline-block;"; strVar += " padding: 0 7px;"; strVar += "}"; strVar += ".long-press-popup li:hover{"; strVar += " border: 1px #B9D6F0 solid;"; strVar += " background: #E6F1FE;"; strVar += " color: #0A7ED0;"; strVar += "}"; strVar += ".long-press-popup .selected {"; strVar += " border: 1px #B9D6F0 solid;"; strVar += " background: #E6F1FE;"; strVar += " color: #0A7ED0;"; strVar += "}"; strVar += ""; strVar += ""; strVar += ".number {"; strVar += " text-align:center;"; strVar += " font-size:9pt;"; strVar += " display: block;"; strVar += " color: #CCCCCC;"; strVar += " padding-top:8px ;"; strVar += "}"; strVar += "<\/style>"; $(document).ready(function() { var tail = $('<div class="tail" style="position: absolute; z-index:1000;"></div> '); $('body').append(tail); $('head').append(strVar); }); var typedChar; (function($) { var types = ['DOMMouseScroll', 'mousewheel']; if ($.event.fixHooks) { for (var i = types.length; i;) { $.event.fixHooks[types[--i]] = $.event.mouseHooks; } } $.event.special.mousewheel = { setup: function() { if (this.addEventListener) { for (var i = types.length; i;) { this.addEventListener(types[--i], handler, false); } } else { this.onmousewheel = handler; } }, teardown: function() { if (this.removeEventListener) { for (var i = types.length; i;) { this.removeEventListener(types[--i], handler, false); } } else { this.onmousewheel = null; } } }; $.fn.extend({ mousewheel: function(fn) { return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel"); }, unmousewheel: function(fn) { return this.unbind("mousewheel", fn); } }); function handler(event) { var orgEvent = event || window.event, args = [].slice.call(arguments, 1), delta = 0, returnValue = true, deltaX = 0, deltaY = 0; event = $.event.fix(orgEvent); event.type = "mousewheel"; // Old school scrollwheel delta if (orgEvent.wheelDelta) { delta = orgEvent.wheelDelta / 120; } if (orgEvent.detail) { delta = -orgEvent.detail / 3; } // New school multidimensional scroll (touchpads) deltas deltaY = delta; // Gecko if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) { deltaY = 0; deltaX = -1 * delta; } // Webkit if (orgEvent.wheelDeltaY !== undefined) { deltaY = orgEvent.wheelDeltaY / 120; } if (orgEvent.wheelDeltaX !== undefined) { deltaX = -1 * orgEvent.wheelDeltaX / 120; } // Add event and delta to the front of the arguments args.unshift(event, delta, deltaX, deltaY); return ($.event.dispatch || $.event.handle).apply(this, args); } })(jQuery); // Avoid `console` errors in browsers that lack a console. (function() { var method; var noop = function noop() {}; var methods = [ 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn' ]; var length = methods.length; var console = (window.console = window.console || {}); while (length--) { method = methods[length]; // Only stub undefined methods. if (!console[method]) { console[method] = noop; } } }()); // Place any jQuery/helper plugins in here. function getCaretPosition(ctrl) { var caretPos = 0; if (document.selection) { // IE Support ctrl.focus(); var sel = document.selection.createRange(); sel.moveStart('character', -ctrl.value.length); caretPos = sel.text.length; } else if (ctrl.selectionStart || ctrl.selectionStart == '0') { // Firefox support caretPos = ctrl.selectionStart; } return caretPos; } function setCaretPosition(ctrl, pos) { if (ctrl.setSelectionRange) { ctrl.focus(); ctrl.setSelectionRange(pos, pos); } else if (ctrl.createTextRange) { var range = ctrl.createTextRange(); range.collapse(true); range.moveEnd('character', pos); range.moveStart('character', pos); range.select(); } } ; (function($, window, undefined) { var pluginName = 'longPress', document = window.document, defaults = { /* propertyName: "value" */ }; var moreChars = { // extended latin (and african latin) // upper 'A': 'â{đ}{đ}', 'B': 'a{đ }âŹ', 'C': 'â{đ}â', 'D': '{đ}Îâ©', 'E': 'â â°', 'F': 'â±', 'G': 'Î{đą}{đ}ââ âââ„âŠââżâŸâŠâŠ', 'H': 'â', 'I': 'âââ©â«âŹââźâŻâ°â±âČâłâšâš', 'J': '{đ„}{đ}', 'K': '{đŠ}{đ}', 'L': 'â{đ}ÂŹââââŽâ”â§âšâšâââ', 'M': 'âł{đ}', 'N': 'ââ”{đ©}{đ}', 'O': 'Ω{đȘ}âââââââââ', 'P': 'ΠΊΚâ{đ«}', 'Q': 'ââ', 'R': 'ââ', 'S': 'ÎŁâ {đź}', 'T': 'â€â„âąâŁâ§âšâ©{đŻ}', 'U': '{đ°}âȘâ©', 'V': '{đ±}ÆČ', 'W': '{đČ}', 'X': 'Î{đł}', 'Y': '{đŽ}', 'Z': 'â€{đ”}', // lower 'a': 'αââ§â {đ}', 'b': 'ÎČ{đ}', 'c': 'Ï{đ }âŻâź', 'd': 'ÎŽ{đĄ}âÂșâŹ', 'e': 'ϔΔâââ ', 'f': 'ÏÏ', 'g': 'Îł{đ€}', 'h': 'η{đ„}â âĄâ„', 'i': 'ââÎčâ«ââââ©', 'j': '{đ§}', 'k': 'Îș', 'l': 'λâ', 'm': 'ÎŒ{đȘ}', 'n': 'âżÎœ{đ«}ÂŹ', 'o': 'Ï{đŹ}âšÂ°', 'p': 'ÏÏÏÏ{đ}', 'q': '{đź}â', 'r': 'Ï{đŻ}', 's': 'Ï{đ°}ââââ”', 't': 'ÏΞ{đ±}âŽ', 'u': 'Ï {đČ}', 'v': 'Ê', 'w': 'Ïâ ', 'x': 'Ο', 'y': '', 'z': 'ζ', '#': 'â»âââ âĄâ§â§â§ â§', '.': 'âŠâŻâ°â±âź ', '\'': 'ÌÌÌÌÌÌÌÌ', '+': '±ââââș', '-': 'â»âŸââ', //â '"': 'ÌÌÌÌÌÌÌÌÌ', '_': 'âââ', //âŹâ '{': 'âŽâŠâšâȘâââŠâŠ', '}': 'â”â§â©â«âââŠâŠ', '[': 'âŽâŠâšâȘâââŠâŠ', ']': 'â”â§â©â«âââŠâŠ', '|': 'âŁâ€â„âŠ', '\\': 'â', '/': 'â÷Š', '<': 'â€âčââââ€ââČ', '>': 'â„âșââââŠââčâł', '=': 'â ââ ââĄâșââââââ', '1': 'Âčâ', '2': 'ÂČâ', '3': 'Âłâ', '4': 'âŽâ', '5': 'â”â ', '6': 'â¶â', '7': 'â·â', '8': 'âžâ', '9': 'âčâ', '0': 'â°â', '*': '·ââĂââšââŒ', '^': 'âŽ' }; var ignoredKeys = [8, 13, 37, 38, 39, 40]; var selectedCharIndex; var lastWhich; var timer; var activeElement; var keyup; var count = 0; var oldCharlength; var tail; var popup = $('<ul class=long-press-popup />'); var onlyonce = false; $('html').click(function() { hidePopup(); //$('textarea').focus(); }); $(window).mousewheel(onWheel); $(window).keyup(onKeyUp); function onKeyDown(e) { keyup = false; count += 1; if ($('.long-press-popup').length <= 0) { if (e.which == 37 || e.which == 38 || e.which == 39 || e.which == 40 || e.which == 16) { return; } var oldcount = count; setTimeout(function() { if (!keyup && (oldcount === count)) { onTimer(); } }, 250); } // Arrow key with popup visible if ($('.long-press-popup').length > 0) { if (e.which == 37) { activePreviousLetter(); e.preventDefault(); } else if (e.which == 39) { activateNextLetter(); e.preventDefault(); } else if (e.which == 13) { hidePopup(); e.preventDefault(); } else if (e.which == 49) { selectCharIndex(0); e.preventDefault(); hidePopup(); } else if (e.which == 50) { selectCharIndex(1); e.preventDefault(); hidePopup(); } else if (e.which == 51) { selectCharIndex(2); e.preventDefault(); hidePopup(); } else if (e.which == 52) { selectCharIndex(3); e.preventDefault(); hidePopup(); } else if (e.which == 53) { selectCharIndex(4); e.preventDefault(); hidePopup(); } else if (e.which == 54) { selectCharIndex(5); e.preventDefault(); hidePopup(); } else if (e.which == 55) { selectCharIndex(6); e.preventDefault(); hidePopup(); } else if (e.which == 56) { selectCharIndex(7); e.preventDefault(); hidePopup(); } else if (e.which == 57) { selectCharIndex(8); e.preventDefault(); hidePopup(); } else if (e.which == lastWhich) { e.preventDefault(); activateNextLetter(); } else if (e.which != lastWhich) { hidePopup(); } } activeElement = e.target; lastWhich = e.which; } function onKeyUp(e) { keyup = true; } function onTimer() { typedChar = $(activeElement).val().split('')[getCaretPosition(activeElement) - 1]; if (moreChars[typedChar]) { showPopup((moreChars[typedChar])); } else { } } function showPopup(chars) { popup.empty(); oldCharlength = 1; $('.tail').on('click', function(e) { e.preventDefault(); }); var letter; var count = 0; for (var i = 0; i < chars.length; i++) { if (chars[i] !== "{") { letter = $('<li class=long-press-letter />').html(chars[i] + "<span class=\"number\">" + (count + 1) + "</span>"); letter.mouseenter(activateLetter); letter.mousedown(hidePopup()); popup.append(letter); count += 1; } else { letter = $('<li class=long-press-letter />').html(chars[i + 1] + chars[i + 2] + "<span class=\"number\">" + (count + 1) + "</span>"); letter.mouseenter(activateDouble); letter.mousedown(hidePopup()); popup.append(letter); count += 1; i += 3; } } $('.tail').append(popup); var height = $(".long-press-popup").innerHeight(); $(".long-press-popup li").each(function(index) { $(this).css('padding-top', height - $(this).height() - 6); }); $('textarea').each(function(index) { if ($(this).is(":focus")) { $('.tail').css($(this).textareaHelper('caretPos')); var offset1 = $('.tail').offset(); var offset2 = $(this).offset(); $(".tail").css('top', offset1.top + offset2.top); $(".tail").css('left', offset1.left + offset2.left); console.log($(".tail").css('top'), $(this).css('top')) } }); /* */ selectedCharIndex = -1; } function activateLetter(e) { selectCharIndex($(e.target).index()); } function activateDouble(e) { selectCharIndex($(e.target).index()); } function activateRelativeLetter(i) { selectCharIndex(($('.long-press-letter').length + selectedCharIndex + i) % $('.long-press-letter').length); } function activateNextLetter() { activateRelativeLetter(1); } function activePreviousLetter() { activateRelativeLetter(-1); } function hidePopup() { popup.detach(); keyup = false; } function onWheel(e, delta, deltaX, deltaY) { if ($('.long-press-popup').length == 0) return; e.preventDefault(); delta < 0 ? activateNextLetter() : activePreviousLetter(); } function selectCharIndex(i) { $('.long-press-letter.selected').removeClass('selected'); $('.long-press-letter').eq(i).addClass('selected'); selectedCharIndex = i; updateChar(i); } function updateChar(i) { var caretpostion; var endString = $('.long-press-letter.selected').html().indexOf("<"); var newChar = $('.long-press-letter.selected').html().substring(0, endString); var pos = getCaretPosition(activeElement); var arVal = $(activeElement).val().split(''); if (newChar.length == 2 && (oldCharlength == 1 || arVal[pos - 1] == typedChar)) { arVal[pos - 1] = newChar[0]; arVal.splice(pos, 0, newChar[1]); caretposition = pos + newChar.length - 1; } else if (arVal[pos - 1] == typedChar) { arVal[pos - 1] = newChar; caretposition = pos + newChar.length - 1; } else if (newChar.length == 2 && oldCharlength == 2) { arVal[pos - 2] = newChar[0]; arVal[pos - 1] = newChar[1]; caretposition = pos + newChar.length - 2; } else if (oldCharlength == 2) { arVal[pos - 2] = newChar; arVal[pos - 1] = ""; caretposition = pos + newChar.length - 2; } else { arVal[pos - 1] = newChar; caretposition = pos + newChar.length - 1; } $(activeElement).val(arVal.join('')); setCaretPosition(activeElement, caretposition); oldCharlength = newChar.length; } function LongPress(element, options) { this.element = element; this.options = $.extend({}, defaults, options); this._defaults = defaults; this._name = pluginName; this.init(); } LongPress.prototype = { init: function() { $(this.element).keydown(onKeyDown); } }; $.fn[pluginName] = function(options) { return this.each(function() { if (!$.data(this, 'plugin_' + pluginName)) { $.data(this, 'plugin_' + pluginName, new LongPress(this, options)); } }); }; }(jQuery, window)); $(function() { $('textarea').each(function(index) { $(this).longPress(); }); }); (function($) { 'use strict'; var caretClass = 'textarea-helper-caret', dataKey = 'textarea-helper' // Styles that could influence size of the mirrored element. , mirrorStyles = [ // Box Styles. 'box-sizing', 'height', 'width', 'padding-bottom', 'padding-left', 'padding-right', 'padding-top' // Font stuff. , 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight' // Spacing etc. , 'word-spacing', 'letter-spacing', 'line-height', 'text-decoration', 'text-indent', 'text-transform' // The direction. , 'direction' ]; var TextareaHelper = function(elem) { if (elem.nodeName.toLowerCase() !== 'textarea') return; this.$text = $(elem); this.$mirror = $('<div/>').css({ 'position': 'absolute', 'overflow': 'auto', 'white-space': 'pre-wrap', 'word-wrap': 'break-word', 'top': 0, 'left': -9999 }).insertAfter(this.$text); }; (function() { this.update = function() { // Copy styles. var styles = {}; for (var i = 0, style; style = mirrorStyles[i]; i++) { styles[style] = this.$text.css(style); } this.$mirror.css(styles).empty(); // Update content and insert caret. var caretPos = this.getOriginalCaretPos() //caretPos.left += 20; , str = this.$text.val(), pre = document.createTextNode(str.substring(0, caretPos)), post = document.createTextNode(str.substring(caretPos)), $car = $('<span/>').addClass(caretClass).css('position', 'absolute').html(' '); this.$mirror.append(pre, $car, post) .scrollTop(this.$text.scrollTop()); }; this.destroy = function() { this.$mirror.remove(); this.$text.removeData(dataKey); return null; }; this.caretPos = function() { this.update(); var $caret = this.$mirror.find('.' + caretClass), pos = $caret.position(); if (this.$text.css('direction') === 'rtl') { pos.right = this.$mirror.innerWidth() - pos.left; pos.left = 'auto'; } return pos; }; this.height = function() { this.update(); this.$mirror.css('height', ''); console.log(this.$mirror.height()); return this.$mirror.height(); }; // XBrowser caret position // Adapted from http://stackoverflow.com/questions/263743/how-to-get-caret-position-in-textarea this.getOriginalCaretPos = function() { var text = this.$text[0]; if (text.selectionStart) { return text.selectionStart; } else if (document.selection) { text.focus(); var r = document.selection.createRange(); if (r == null) { return 0; } var re = text.createTextRange(), rc = re.duplicate(); re.moveToBookmark(r.getBookmark()); rc.setEndPoint('EndToStart', re); return rc.text.length; } return 0; }; }).call(TextareaHelper.prototype); $.fn.textareaHelper = function(method) { this.each(function() { var $this = $(this), instance = $this.data(dataKey); if (!instance) { instance = new TextareaHelper(this); $this.data(dataKey, instance); } }); if (method) { var instance = this.first().data(dataKey); return instance[method](); } else { return this; } }; })(jQuery);