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);