BVB Source Codes

Squire Show KeyHandlers.js Source code

Return Download Squire: download KeyHandlers.js Source code - Download Squire Source code - Type:.js
  1. /*jshint strict:false, undef:false, unused:false */
  2.  
  3. var keys = {
  4.     8: 'backspace',
  5.     9: 'tab',
  6.     13: 'enter',
  7.     32: 'space',
  8.     33: 'pageup',
  9.     34: 'pagedown',
  10.     37: 'left',
  11.     39: 'right',
  12.     46: 'delete',
  13.     219: '[',
  14.     221: ']'
  15. };
  16.  
  17. // Ref: http://unixpapa.com/js/key.html
  18. var onKey = function ( event ) {
  19.     var code = event.keyCode,
  20.         key = keys[ code ],
  21.         modifiers = '',
  22.         range = this.getSelection();
  23.  
  24.     if ( event.defaultPrevented ) {
  25.         return;
  26.     }
  27.  
  28.     if ( !key ) {
  29.         key = String.fromCharCode( code ).toLowerCase();
  30.         // Only reliable for letters and numbers
  31.         if ( !/^[A-Za-z0-9]$/.test( key ) ) {
  32.             key = '';
  33.         }
  34.     }
  35.  
  36.     // On keypress, delete and '.' both have event.keyCode 46
  37.     // Must check event.which to differentiate.
  38.     if ( isPresto && event.which === 46 ) {
  39.         key = '.';
  40.     }
  41.  
  42.     // Function keys
  43.     if ( 111 < code && code < 124 ) {
  44.         key = 'f' + ( code - 111 );
  45.     }
  46.  
  47.     // We need to apply the backspace/delete handlers regardless of
  48.     // control key modifiers.
  49.     if ( key !== 'backspace' && key !== 'delete' ) {
  50.         if ( event.altKey  ) { modifiers += 'alt-'; }
  51.         if ( event.ctrlKey ) { modifiers += 'ctrl-'; }
  52.         if ( event.metaKey ) { modifiers += 'meta-'; }
  53.     }
  54.     // However, on Windows, shift-delete is apparently "cut" (WTF right?), so
  55.     // we want to let the browser handle shift-delete.
  56.     if ( event.shiftKey ) { modifiers += 'shift-'; }
  57.  
  58.     key = modifiers + key;
  59.  
  60.     if ( this._keyHandlers[ key ] ) {
  61.         this._keyHandlers[ key ]( this, event, range );
  62.     } else if ( key.length === 1 && !range.collapsed ) {
  63.         // Record undo checkpoint.
  64.         this.saveUndoState( range );
  65.         // Delete the selection
  66.         deleteContentsOfRange( range, this._root );
  67.         this._ensureBottomLine();
  68.         this.setSelection( range );
  69.         this._updatePath( range, true );
  70.     }
  71. };
  72.  
  73. var mapKeyTo = function ( method ) {
  74.     return function ( self, event ) {
  75.         event.preventDefault();
  76.         self[ method ]();
  77.     };
  78. };
  79.  
  80. var mapKeyToFormat = function ( tag, remove ) {
  81.     remove = remove || null;
  82.     return function ( self, event ) {
  83.         event.preventDefault();
  84.         var range = self.getSelection();
  85.         if ( self.hasFormat( tag, null, range ) ) {
  86.             self.changeFormat( null, { tag: tag }, range );
  87.         } else {
  88.             self.changeFormat( { tag: tag }, remove, range );
  89.         }
  90.     };
  91. };
  92.  
  93. // If you delete the content inside a span with a font styling, Webkit will
  94. // replace it with a <font> tag (!). If you delete all the text inside a
  95. // link in Opera, it won't delete the link. Let's make things consistent. If
  96. // you delete all text inside an inline tag, remove the inline tag.
  97. var afterDelete = function ( self, range ) {
  98.     try {
  99.         if ( !range ) { range = self.getSelection(); }
  100.         var node = range.startContainer,
  101.             parent;
  102.         // Climb the tree from the focus point while we are inside an empty
  103.         // inline element
  104.         if ( node.nodeType === TEXT_NODE ) {
  105.             node = node.parentNode;
  106.         }
  107.         parent = node;
  108.         while ( isInline( parent ) &&
  109.                 ( !parent.textContent || parent.textContent === ZWS ) ) {
  110.             node = parent;
  111.             parent = node.parentNode;
  112.         }
  113.         // If focused in empty inline element
  114.         if ( node !== parent ) {
  115.             // Move focus to just before empty inline(s)
  116.             range.setStart( parent,
  117.                 indexOf.call( parent.childNodes, node ) );
  118.             range.collapse( true );
  119.             // Remove empty inline(s)
  120.             parent.removeChild( node );
  121.             // Fix cursor in block
  122.             if ( !isBlock( parent ) ) {
  123.                 parent = getPreviousBlock( parent, self._root );
  124.             }
  125.             fixCursor( parent, self._root );
  126.             // Move cursor into text node
  127.             moveRangeBoundariesDownTree( range );
  128.         }
  129.         // If you delete the last character in the sole <div> in Chrome,
  130.         // it removes the div and replaces it with just a <br> inside the
  131.         // root. Detach the <br>; the _ensureBottomLine call will insert a new
  132.         // block.
  133.         if ( node === self._root &&
  134.                 ( node = node.firstChild ) && node.nodeName === 'BR' ) {
  135.             detach( node );
  136.         }
  137.         self._ensureBottomLine();
  138.         self.setSelection( range );
  139.         self._updatePath( range, true );
  140.     } catch ( error ) {
  141.         self.didError( error );
  142.     }
  143. };
  144.  
  145. var keyHandlers = {
  146.     enter: function ( self, event, range ) {
  147.         var root = self._root;
  148.         var block, parent, nodeAfterSplit;
  149.  
  150.         // We handle this ourselves
  151.         event.preventDefault();
  152.  
  153.         // Save undo checkpoint and add any links in the preceding section.
  154.         // Remove any zws so we don't think there's content in an empty
  155.         // block.
  156.         self._recordUndoState( range );
  157.         addLinks( range.startContainer, root, self );
  158.         self._removeZWS();
  159.         self._getRangeAndRemoveBookmark( range );
  160.  
  161.         // Selected text is overwritten, therefore delete the contents
  162.         // to collapse selection.
  163.         if ( !range.collapsed ) {
  164.             deleteContentsOfRange( range, root );
  165.         }
  166.  
  167.         block = getStartBlockOfRange( range, root );
  168.  
  169.         // If this is a malformed bit of document or in a table;
  170.         // just play it safe and insert a <br>.
  171.         if ( !block || /^T[HD]$/.test( block.nodeName ) ) {
  172.             insertNodeInRange( range, self.createElement( 'BR' ) );
  173.             range.collapse( false );
  174.             self.setSelection( range );
  175.             self._updatePath( range, true );
  176.             return;
  177.         }
  178.  
  179.         // If in a list, we'll split the LI instead.
  180.         if ( parent = getNearest( block, root, 'LI' ) ) {
  181.             block = parent;
  182.         }
  183.  
  184.         if ( !block.textContent ) {
  185.             // Break list
  186.             if ( getNearest( block, root, 'UL' ) ||
  187.                     getNearest( block, root, 'OL' ) ) {
  188.                 return self.modifyBlocks( decreaseListLevel, range );
  189.             }
  190.             // Break blockquote
  191.             else if ( getNearest( block, root, 'BLOCKQUOTE' ) ) {
  192.                 return self.modifyBlocks( removeBlockQuote, range );
  193.             }
  194.         }
  195.  
  196.         // Otherwise, split at cursor point.
  197.         nodeAfterSplit = splitBlock( self, block,
  198.             range.startContainer, range.startOffset );
  199.  
  200.         // Clean up any empty inlines if we hit enter at the beginning of the
  201.         // block
  202.         removeZWS( block );
  203.         removeEmptyInlines( block );
  204.         fixCursor( block, root );
  205.  
  206.         // Focus cursor
  207.         // If there's a <b>/<i> etc. at the beginning of the split
  208.         // make sure we focus inside it.
  209.         while ( nodeAfterSplit.nodeType === ELEMENT_NODE ) {
  210.             var child = nodeAfterSplit.firstChild,
  211.                 next;
  212.  
  213.             // Don't continue links over a block break; unlikely to be the
  214.             // desired outcome.
  215.             if ( nodeAfterSplit.nodeName === 'A' &&
  216.                     ( !nodeAfterSplit.textContent ||
  217.                         nodeAfterSplit.textContent === ZWS ) ) {
  218.                 child = self._doc.createTextNode( '' );
  219.                 replaceWith( nodeAfterSplit, child );
  220.                 nodeAfterSplit = child;
  221.                 break;
  222.             }
  223.  
  224.             while ( child && child.nodeType === TEXT_NODE && !child.data ) {
  225.                 next = child.nextSibling;
  226.                 if ( !next || next.nodeName === 'BR' ) {
  227.                     break;
  228.                 }
  229.                 detach( child );
  230.                 child = next;
  231.             }
  232.  
  233.             // 'BR's essentially don't count; they're a browser hack.
  234.             // If you try to select the contents of a 'BR', FF will not let
  235.             // you type anything!
  236.             if ( !child || child.nodeName === 'BR' ||
  237.                     ( child.nodeType === TEXT_NODE && !isPresto ) ) {
  238.                 break;
  239.             }
  240.             nodeAfterSplit = child;
  241.         }
  242.         range = self._createRange( nodeAfterSplit, 0 );
  243.         self.setSelection( range );
  244.         self._updatePath( range, true );
  245.     },
  246.     backspace: function ( self, event, range ) {
  247.         var root = self._root;
  248.         self._removeZWS();
  249.         // Record undo checkpoint.
  250.         self.saveUndoState( range );
  251.         // If not collapsed, delete contents
  252.         if ( !range.collapsed ) {
  253.             event.preventDefault();
  254.             deleteContentsOfRange( range, root );
  255.             afterDelete( self, range );
  256.         }
  257.         // If at beginning of block, merge with previous
  258.         else if ( rangeDoesStartAtBlockBoundary( range, root ) ) {
  259.             event.preventDefault();
  260.             var current = getStartBlockOfRange( range, root );
  261.             var previous;
  262.             if ( !current ) {
  263.                 return;
  264.             }
  265.             // In case inline data has somehow got between blocks.
  266.             fixContainer( current.parentNode, root );
  267.             // Now get previous block
  268.             previous = getPreviousBlock( current, root );
  269.             // Must not be at the very beginning of the text area.
  270.             if ( previous ) {
  271.                 // If not editable, just delete whole block.
  272.                 if ( !previous.isContentEditable ) {
  273.                     detach( previous );
  274.                     return;
  275.                 }
  276.                 // Otherwise merge.
  277.                 mergeWithBlock( previous, current, range );
  278.                 // If deleted line between containers, merge newly adjacent
  279.                 // containers.
  280.                 current = previous.parentNode;
  281.                 while ( current !== root && !current.nextSibling ) {
  282.                     current = current.parentNode;
  283.                 }
  284.                 if ( current !== root && ( current = current.nextSibling ) ) {
  285.                     mergeContainers( current, root );
  286.                 }
  287.                 self.setSelection( range );
  288.             }
  289.             // If at very beginning of text area, allow backspace
  290.             // to break lists/blockquote.
  291.             else if ( current ) {
  292.                 // Break list
  293.                 if ( getNearest( current, root, 'UL' ) ||
  294.                         getNearest( current, root, 'OL' ) ) {
  295.                     return self.modifyBlocks( decreaseListLevel, range );
  296.                 }
  297.                 // Break blockquote
  298.                 else if ( getNearest( current, root, 'BLOCKQUOTE' ) ) {
  299.                     return self.modifyBlocks( decreaseBlockQuoteLevel, range );
  300.                 }
  301.                 self.setSelection( range );
  302.                 self._updatePath( range, true );
  303.             }
  304.         }
  305.         // Otherwise, leave to browser but check afterwards whether it has
  306.         // left behind an empty inline tag.
  307.         else {
  308.             self.setSelection( range );
  309.             setTimeout( function () { afterDelete( self ); }, 0 );
  310.         }
  311.     },
  312.     'delete': function ( self, event, range ) {
  313.         var root = self._root;
  314.         var current, next, originalRange,
  315.             cursorContainer, cursorOffset, nodeAfterCursor;
  316.         self._removeZWS();
  317.         // Record undo checkpoint.
  318.         self.saveUndoState( range );
  319.         // If not collapsed, delete contents
  320.         if ( !range.collapsed ) {
  321.             event.preventDefault();
  322.             deleteContentsOfRange( range, root );
  323.             afterDelete( self, range );
  324.         }
  325.         // If at end of block, merge next into this block
  326.         else if ( rangeDoesEndAtBlockBoundary( range, root ) ) {
  327.             event.preventDefault();
  328.             current = getStartBlockOfRange( range, root );
  329.             if ( !current ) {
  330.                 return;
  331.             }
  332.             // In case inline data has somehow got between blocks.
  333.             fixContainer( current.parentNode, root );
  334.             // Now get next block
  335.             next = getNextBlock( current, root );
  336.             // Must not be at the very end of the text area.
  337.             if ( next ) {
  338.                 // If not editable, just delete whole block.
  339.                 if ( !next.isContentEditable ) {
  340.                     detach( next );
  341.                     return;
  342.                 }
  343.                 // Otherwise merge.
  344.                 mergeWithBlock( current, next, range );
  345.                 // If deleted line between containers, merge newly adjacent
  346.                 // containers.
  347.                 next = current.parentNode;
  348.                 while ( next !== root && !next.nextSibling ) {
  349.                     next = next.parentNode;
  350.                 }
  351.                 if ( next !== root && ( next = next.nextSibling ) ) {
  352.                     mergeContainers( next, root );
  353.                 }
  354.                 self.setSelection( range );
  355.                 self._updatePath( range, true );
  356.             }
  357.         }
  358.         // Otherwise, leave to browser but check afterwards whether it has
  359.         // left behind an empty inline tag.
  360.         else {
  361.             // But first check if the cursor is just before an IMG tag. If so,
  362.             // delete it ourselves, because the browser won't if it is not
  363.             // inline.
  364.             originalRange = range.cloneRange();
  365.             moveRangeBoundariesUpTree( range, self._root );
  366.             cursorContainer = range.endContainer;
  367.             cursorOffset = range.endOffset;
  368.             if ( cursorContainer.nodeType === ELEMENT_NODE ) {
  369.                 nodeAfterCursor = cursorContainer.childNodes[ cursorOffset ];
  370.                 if ( nodeAfterCursor && nodeAfterCursor.nodeName === 'IMG' ) {
  371.                     event.preventDefault();
  372.                     detach( nodeAfterCursor );
  373.                     moveRangeBoundariesDownTree( range );
  374.                     afterDelete( self, range );
  375.                     return;
  376.                 }
  377.             }
  378.             self.setSelection( originalRange );
  379.             setTimeout( function () { afterDelete( self ); }, 0 );
  380.         }
  381.     },
  382.     tab: function ( self, event, range ) {
  383.         var root = self._root;
  384.         var node, parent;
  385.         self._removeZWS();
  386.         // If no selection and at start of block
  387.         if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) {
  388.             node = getStartBlockOfRange( range, root );
  389.             // Iterate through the block's parents
  390.             while ( parent = node.parentNode ) {
  391.                 // If we find a UL or OL (so are in a list, node must be an LI)
  392.                 if ( parent.nodeName === 'UL' || parent.nodeName === 'OL' ) {
  393.                     // Then increase the list level
  394.                     event.preventDefault();
  395.                     self.modifyBlocks( increaseListLevel, range );
  396.                     break;
  397.                 }
  398.                 node = parent;
  399.             }
  400.         }
  401.     },
  402.     'shift-tab': function ( self, event, range ) {
  403.         var root = self._root;
  404.         var node;
  405.         self._removeZWS();
  406.         // If no selection and at start of block
  407.         if ( range.collapsed && rangeDoesStartAtBlockBoundary( range, root ) ) {
  408.             // Break list
  409.             node = range.startContainer;
  410.             if ( getNearest( node, root, 'UL' ) ||
  411.                     getNearest( node, root, 'OL' ) ) {
  412.                 event.preventDefault();
  413.                 self.modifyBlocks( decreaseListLevel, range );
  414.             }
  415.         }
  416.     },
  417.     space: function ( self, _, range ) {
  418.         var node, parent;
  419.         self._recordUndoState( range );
  420.         addLinks( range.startContainer, self._root, self );
  421.         self._getRangeAndRemoveBookmark( range );
  422.  
  423.         // If the cursor is at the end of a link (<a>foo|</a>) then move it
  424.         // outside of the link (<a>foo</a>|) so that the space is not part of
  425.         // the link text.
  426.         node = range.endContainer;
  427.         parent = node.parentNode;
  428.         if ( range.collapsed && parent.nodeName === 'A' &&
  429.                 !node.nextSibling && range.endOffset === getLength( node ) ) {
  430.             range.setStartAfter( parent );
  431.         }
  432.         // Delete the selection if not collapsed
  433.         else if ( !range.collapsed ) {
  434.             deleteContentsOfRange( range, self._root );
  435.             self._ensureBottomLine();
  436.             self.setSelection( range );
  437.             self._updatePath( range, true );
  438.         }
  439.  
  440.         self.setSelection( range );
  441.     },
  442.     left: function ( self ) {
  443.         self._removeZWS();
  444.     },
  445.     right: function ( self ) {
  446.         self._removeZWS();
  447.     }
  448. };
  449.  
  450. // Firefox pre v29 incorrectly handles Cmd-left/Cmd-right on Mac:
  451. // it goes back/forward in history! Override to do the right
  452. // thing.
  453. // https://bugzilla.mozilla.org/show_bug.cgi?id=289384
  454. if ( isMac && isGecko ) {
  455.     keyHandlers[ 'meta-left' ] = function ( self, event ) {
  456.         event.preventDefault();
  457.         var sel = getWindowSelection( self );
  458.         if ( sel && sel.modify ) {
  459.             sel.modify( 'move', 'backward', 'lineboundary' );
  460.         }
  461.     };
  462.     keyHandlers[ 'meta-right' ] = function ( self, event ) {
  463.         event.preventDefault();
  464.         var sel = getWindowSelection( self );
  465.         if ( sel && sel.modify ) {
  466.             sel.modify( 'move', 'forward', 'lineboundary' );
  467.         }
  468.     };
  469. }
  470.  
  471. // System standard for page up/down on Mac is to just scroll, not move the
  472. // cursor. On Linux/Windows, it should move the cursor, but some browsers don't
  473. // implement this natively. Override to support it.
  474. if ( !isMac ) {
  475.     keyHandlers.pageup = function ( self ) {
  476.         self.moveCursorToStart();
  477.     };
  478.     keyHandlers.pagedown = function ( self ) {
  479.         self.moveCursorToEnd();
  480.     };
  481. }
  482.  
  483. keyHandlers[ ctrlKey + 'b' ] = mapKeyToFormat( 'B' );
  484. keyHandlers[ ctrlKey + 'i' ] = mapKeyToFormat( 'I' );
  485. keyHandlers[ ctrlKey + 'u' ] = mapKeyToFormat( 'U' );
  486. keyHandlers[ ctrlKey + 'shift-7' ] = mapKeyToFormat( 'S' );
  487. keyHandlers[ ctrlKey + 'shift-5' ] = mapKeyToFormat( 'SUB', { tag: 'SUP' } );
  488. keyHandlers[ ctrlKey + 'shift-6' ] = mapKeyToFormat( 'SUP', { tag: 'SUB' } );
  489. keyHandlers[ ctrlKey + 'shift-8' ] = mapKeyTo( 'makeUnorderedList' );
  490. keyHandlers[ ctrlKey + 'shift-9' ] = mapKeyTo( 'makeOrderedList' );
  491. keyHandlers[ ctrlKey + '[' ] = mapKeyTo( 'decreaseQuoteLevel' );
  492. keyHandlers[ ctrlKey + ']' ] = mapKeyTo( 'increaseQuoteLevel' );
  493. keyHandlers[ ctrlKey + 'y' ] = mapKeyTo( 'redo' );
  494. keyHandlers[ ctrlKey + 'z' ] = mapKeyTo( 'undo' );
  495. keyHandlers[ ctrlKey + 'shift-z' ] = mapKeyTo( 'redo' );
  496.  
downloadKeyHandlers.js Source code - Download Squire Source code
Related Source Codes/Software:
thor - Thor is a toolkit for building powerful command-li... 2017-01-08
glide - Package Management for Golang h... 2017-01-08
TextFieldEffects - Custom UITextFields effects inspired by Codrops, b... 2017-01-08
flowchart.js - Draws simple SVG flow chart diagrams from textual ... 2017-01-08
RoundedImageView - A fast ImageView that supports rounded corners, ov... 2017-01-07
webpack-demos - a collection of simple demos of Webpack 2017-01-08
amazon-dsstne - Deep Scalable Sparse Tensor Network Engine (DSSTNE... 2017-01-08
rq - Simple job queues for Python ht... 2017-01-08
emmet-vim - emmet for vim: http://emmet.io/ ... 2017-01-08
prose - A Content Editor for GitHub. ht... 2017-01-08
csvkit - A suite of utilities for converting to and working... 2017-02-18
Messenger - This is a native iOS Messenger app, making realtim... 2017-02-18
meteor-up - Production Quality Meteor Deployment 2017-02-18
book-of-modern-frontend-tooling - The Front-end Tooling Book 2017-02-17
sorcery - Magical authentication for Rails 3 & 4 2017-02-17
iScript - Xiami.com script--about shrimp, Baidu's Web site, ... 2017-02-17
AndroidViewHover - An elegant way to show your menu or messages. 2017-02-17
viper - Go configuration with fang 2017-02-17
bypy - The Python client for Baidu Yun (Personal Cloud St... 2017-02-17
ZLSwipeableView - A simple view for building card like interface ins... 2017-02-17

 Back to top