|
@@ -0,0 +1,238 @@
|
|
|
+// shortcut suggest functionality
|
|
|
+// auto attaches to all [with-shortcuts] elements
|
|
|
+(function() {
|
|
|
+ let input = null, menu = null, options = [], index = -1;
|
|
|
+ let backtrackAfterApplying = false;
|
|
|
+ function getCaretPos(win) {
|
|
|
+ win = win || window;
|
|
|
+ let doc = win.document;
|
|
|
+ let sel, range, rects, rect;
|
|
|
+ let x = 0, y = 0;
|
|
|
+ if (win.getSelection) { // won't work if getSelection() isn't available!
|
|
|
+ sel = win.getSelection();
|
|
|
+ if(sel.toString() !== '') return null; // won't work if there isa
|
|
|
+ if (sel.rangeCount) {
|
|
|
+ range = sel.getRangeAt(0).cloneRange();
|
|
|
+ if (range.getClientRects) {
|
|
|
+ range.collapse(true);
|
|
|
+ let span = doc.createElement("span");
|
|
|
+ if (span.getClientRects) {
|
|
|
+ // Ensure span has dimensions and position by
|
|
|
+ // adding a zero-width space character
|
|
|
+ span.appendChild( doc.createTextNode("\u200bXX") );
|
|
|
+ range.insertNode(span);
|
|
|
+ rect = span.getClientRects()[0];
|
|
|
+ x = rect.left;
|
|
|
+ y = rect.top;
|
|
|
+ let spanParent = span.parentNode;
|
|
|
+ spanParent.removeChild(span);
|
|
|
+
|
|
|
+ // Glue any broken text nodes back together
|
|
|
+ spanParent.normalize();
|
|
|
+
|
|
|
+ return [x, y];
|
|
|
+ }
|
|
|
+ return null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return { x: x, y: y };
|
|
|
+ }
|
|
|
+ function show(_input) {
|
|
|
+ let pos = getCaretPos();
|
|
|
+ if(pos) {
|
|
|
+ index = -1;
|
|
|
+ let rawOptions = $(_input).closest('[data-shortcuts]').attr('data-shortcuts');
|
|
|
+ options = [];
|
|
|
+ rawOptions = rawOptions.split('^^^');
|
|
|
+ for (let i = 0; i < rawOptions.length; i++) {
|
|
|
+ let parts = rawOptions[i].split('|||');
|
|
|
+ options.push({
|
|
|
+ name: parts[0],
|
|
|
+ value: parts[1]
|
|
|
+ });
|
|
|
+ }
|
|
|
+ menu.empty();
|
|
|
+ for(let i = 0; i < options.length; i++) {
|
|
|
+ menu.append(
|
|
|
+ $('<div/>')
|
|
|
+ .addClass('sc')
|
|
|
+ .text(options[i].name)
|
|
|
+ .attr('title', options[i].value)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ menu
|
|
|
+ .css({
|
|
|
+ left: pos[0] + 'px',
|
|
|
+ top: pos[1] + 'px',
|
|
|
+ })
|
|
|
+ .show();
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function discard() {
|
|
|
+ if($('.stag-shortcuts:visible').length) {
|
|
|
+ $('.stag-shortcuts').hide();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function highlightOption() {
|
|
|
+ menu.find('.sc').removeClass('active');
|
|
|
+ if(options && options.length && index >= 0 && index < options.length) {
|
|
|
+ menu.find('.sc:eq(' + index + ')').addClass('active');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function apply() {
|
|
|
+ if(input && options && options.length && index >= 0 && index < options.length) {
|
|
|
+ $(input).focus();
|
|
|
+ if(backtrackAfterApplying) {
|
|
|
+ document.execCommand("delete", true, null);
|
|
|
+ }
|
|
|
+ document.execCommand("insertText", true, options[index].value);
|
|
|
+ discard();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ function isVisible() {
|
|
|
+ return !!$('.stag-shortcuts:visible').length;
|
|
|
+ }
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ var selectedText = '';
|
|
|
+
|
|
|
+ $('.stag-shortcuts').remove();
|
|
|
+ options = [];
|
|
|
+ menu = $('<div/>')
|
|
|
+ .addClass('stag-shortcuts')
|
|
|
+ .appendTo('body');
|
|
|
+
|
|
|
+ $(document)
|
|
|
+ .off('mousedown.outside-shortcuts')
|
|
|
+ .on('mousedown.outside-shortcuts', function(_e) {
|
|
|
+ return discard();
|
|
|
+ });
|
|
|
+ $(document)
|
|
|
+ .off('keypress.shortcuts', '[with-shortcuts]')
|
|
|
+ .on('keypress.shortcuts', '[with-shortcuts]', function(_e) {
|
|
|
+ // console.log('KP: ', _e.which);
|
|
|
+ input = this;
|
|
|
+ switch(_e.which) {
|
|
|
+ case 64:
|
|
|
+ backtrackAfterApplying = true;
|
|
|
+ show(this);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if(isVisible()) return false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .off('keydown.shortcuts', '[with-shortcuts]')
|
|
|
+ .on('keydown.shortcuts', '[with-shortcuts]', function(_e) {
|
|
|
+ // console.log('KD: ', _e.which);
|
|
|
+ input = this;
|
|
|
+ let consumed = false;
|
|
|
+ switch(_e.which) {
|
|
|
+ case 32:
|
|
|
+ if(_e.ctrlKey) {
|
|
|
+ backtrackAfterApplying = false;
|
|
|
+ show(this);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if(!isVisible()) return;
|
|
|
+ _e.preventDefault();
|
|
|
+ apply();
|
|
|
+ consumed = true;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 27:
|
|
|
+ if(!isVisible()) return;
|
|
|
+ consumed = !discard();
|
|
|
+ break;
|
|
|
+ case 38:
|
|
|
+ if(!isVisible()) return;
|
|
|
+ if(index > 0) index--;
|
|
|
+ highlightOption();
|
|
|
+ consumed = true;
|
|
|
+ break;
|
|
|
+ case 40:
|
|
|
+ if(!isVisible()) return;
|
|
|
+ if(index < options.length - 1) index++;
|
|
|
+ highlightOption();
|
|
|
+ consumed = true;
|
|
|
+ break;
|
|
|
+ case 13:
|
|
|
+ if(!isVisible()) return;
|
|
|
+ apply();
|
|
|
+ consumed = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ consumed = isVisible();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(consumed) return false;
|
|
|
+ })
|
|
|
+ .off('selectionchange.shortcuts')
|
|
|
+ .on('selectionchange.shortcuts', function(_e) {
|
|
|
+ console.log(_e);
|
|
|
+ });
|
|
|
+ $(document)
|
|
|
+ .off('click.apply-shortcuts', '.stag-shortcuts>.sc')
|
|
|
+ .on('click.apply-shortcuts', '.stag-shortcuts>.sc', function(_e) {
|
|
|
+ apply();
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ menu.off('mousedown.inside-shortcuts')
|
|
|
+ .on('mousedown.inside-shortcuts', function(_e) {
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+ $(document)
|
|
|
+ .off('mousedown.add-shortcuts', '.add-shortcut')
|
|
|
+ .on('mousedown.add-shortcuts', '.add-shortcut', function(_e) {
|
|
|
+ let hasFocus = $(document.activeElement).closest('.note-content').length;
|
|
|
+ if(hasFocus) {
|
|
|
+ selectedText = window.getSelection().toString();
|
|
|
+ if(selectedText !== '') return;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ })
|
|
|
+ .off('click.add-shortcuts', '.add-shortcut')
|
|
|
+ .on('click.add-shortcuts', '.add-shortcut', function(_e) {
|
|
|
+ if(selectedText === '') return;
|
|
|
+ $('#selected-sc-text').val(selectedText);
|
|
|
+ $('#create-shortcut-form')
|
|
|
+ .css({
|
|
|
+ left: $(this).offset().left + 'px',
|
|
|
+ top: ($(this).offset().top + $(this).outerHeight()) + 'px'
|
|
|
+ })
|
|
|
+ .show();
|
|
|
+ showMoeFormMask();
|
|
|
+ return false;
|
|
|
+ })
|
|
|
+ .off('mousedown.inside-form', '#create-shortcut-form *')
|
|
|
+ .on('mousedown.inside-form', '#create-shortcut-form *', function(_e) {
|
|
|
+ // return false;
|
|
|
+ })
|
|
|
+ .off('submit.add-shortcut', '#create-shortcut-form')
|
|
|
+ .on('submit.add-shortcut', '#create-shortcut-form', function(_e) {
|
|
|
+ var label = $(this).find('[name="shortcut"]').val(),
|
|
|
+ content = $(this).find('[name="text"]').val();
|
|
|
+ $.post('/api/proTextShortcut/create', $(this).serialize(), function(_data) {
|
|
|
+ if(_data && _data.success && input) {
|
|
|
+ var options = [$(input).closest('[data-shortcuts]').attr('data-shortcuts')]
|
|
|
+ options.push(label + '|||' + content);
|
|
|
+ options = options.join('^^^');
|
|
|
+ $(input).closest('[data-shortcuts]').attr('data-shortcuts', options);
|
|
|
+ toastr.success('Shortcut saved');
|
|
|
+ hideMoeFormMask();
|
|
|
+ $('#create-shortcut-form').hide();
|
|
|
+ $(input).focus();
|
|
|
+ }
|
|
|
+ }, 'json');
|
|
|
+ return false;
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+ addMCInitializer('shortcut-suggest', init);
|
|
|
+})();
|
|
|
+
|
|
|
+
|