BVB Source Codes

Squire Show Range.js Source code

Return Download Squire: download Range.js Source code - Download Squire Source code - Type:.js
  1. /*jshint strict:false, undef:false, unused:false, latedef:false */
  2.  
  3. var getNodeBefore = function ( node, offset ) {
  4.     var children = node.childNodes;
  5.     while ( offset && node.nodeType === ELEMENT_NODE ) {
  6.         node = children[ offset - 1 ];
  7.         children = node.childNodes;
  8.         offset = children.length;
  9.     }
  10.     return node;
  11. };
  12.  
  13. var getNodeAfter = function ( node, offset ) {
  14.     if ( node.nodeType === ELEMENT_NODE ) {
  15.         var children = node.childNodes;
  16.         if ( offset < children.length ) {
  17.             node = children[ offset ];
  18.         } else {
  19.             while ( node && !node.nextSibling ) {
  20.                 node = node.parentNode;
  21.             }
  22.             if ( node ) { node = node.nextSibling; }
  23.         }
  24.     }
  25.     return node;
  26. };
  27.  
  28. // ---
  29.  
  30. var insertNodeInRange = function ( range, node ) {
  31.     // Insert at start.
  32.     var startContainer = range.startContainer,
  33.         startOffset = range.startOffset,
  34.         endContainer = range.endContainer,
  35.         endOffset = range.endOffset,
  36.         parent, children, childCount, afterSplit;
  37.  
  38.     // If part way through a text node, split it.
  39.     if ( startContainer.nodeType === TEXT_NODE ) {
  40.         parent = startContainer.parentNode;
  41.         children = parent.childNodes;
  42.         if ( startOffset === startContainer.length ) {
  43.             startOffset = indexOf.call( children, startContainer ) + 1;
  44.             if ( range.collapsed ) {
  45.                 endContainer = parent;
  46.                 endOffset = startOffset;
  47.             }
  48.         } else {
  49.             if ( startOffset ) {
  50.                 afterSplit = startContainer.splitText( startOffset );
  51.                 if ( endContainer === startContainer ) {
  52.                     endOffset -= startOffset;
  53.                     endContainer = afterSplit;
  54.                 }
  55.                 else if ( endContainer === parent ) {
  56.                     endOffset += 1;
  57.                 }
  58.                 startContainer = afterSplit;
  59.             }
  60.             startOffset = indexOf.call( children, startContainer );
  61.         }
  62.         startContainer = parent;
  63.     } else {
  64.         children = startContainer.childNodes;
  65.     }
  66.  
  67.     childCount = children.length;
  68.  
  69.     if ( startOffset === childCount ) {
  70.         startContainer.appendChild( node );
  71.     } else {
  72.         startContainer.insertBefore( node, children[ startOffset ] );
  73.     }
  74.  
  75.     if ( startContainer === endContainer ) {
  76.         endOffset += children.length - childCount;
  77.     }
  78.  
  79.     range.setStart( startContainer, startOffset );
  80.     range.setEnd( endContainer, endOffset );
  81. };
  82.  
  83. var extractContentsOfRange = function ( range, common, root ) {
  84.     var startContainer = range.startContainer,
  85.         startOffset = range.startOffset,
  86.         endContainer = range.endContainer,
  87.         endOffset = range.endOffset;
  88.  
  89.     if ( !common ) {
  90.         common = range.commonAncestorContainer;
  91.     }
  92.  
  93.     if ( common.nodeType === TEXT_NODE ) {
  94.         common = common.parentNode;
  95.     }
  96.  
  97.     var endNode = split( endContainer, endOffset, common, root ),
  98.         startNode = split( startContainer, startOffset, common, root ),
  99.         frag = common.ownerDocument.createDocumentFragment(),
  100.         next, before, after;
  101.  
  102.     // End node will be null if at end of child nodes list.
  103.     while ( startNode !== endNode ) {
  104.         next = startNode.nextSibling;
  105.         frag.appendChild( startNode );
  106.         startNode = next;
  107.     }
  108.  
  109.     startContainer = common;
  110.     startOffset = endNode ?
  111.         indexOf.call( common.childNodes, endNode ) :
  112.         common.childNodes.length;
  113.  
  114.     // Merge text nodes if adjacent. IE10 in particular will not focus
  115.     // between two text nodes
  116.     after = common.childNodes[ startOffset ];
  117.     before = after && after.previousSibling;
  118.     if ( before &&
  119.             before.nodeType === TEXT_NODE &&
  120.             after.nodeType === TEXT_NODE ) {
  121.         startContainer = before;
  122.         startOffset = before.length;
  123.         before.appendData( after.data );
  124.         detach( after );
  125.     }
  126.  
  127.     range.setStart( startContainer, startOffset );
  128.     range.collapse( true );
  129.  
  130.     fixCursor( common, root );
  131.  
  132.     return frag;
  133. };
  134.  
  135. var deleteContentsOfRange = function ( range, root ) {
  136.     // Move boundaries up as much as possible to reduce need to split.
  137.     // But we need to check whether we've moved the boundary outside of a
  138.     // block. If so, the entire block will be removed, so we shouldn't merge
  139.     // later.
  140.     moveRangeBoundariesUpTree( range );
  141.  
  142.     var startBlock = range.startContainer,
  143.         endBlock = range.endContainer,
  144.         needsMerge = ( isInline( startBlock ) || isBlock( startBlock ) ) &&
  145.             ( isInline( endBlock ) || isBlock( endBlock ) );
  146.  
  147.     // Remove selected range
  148.     var frag = extractContentsOfRange( range, null, root );
  149.  
  150.     // Move boundaries back down tree so that they are inside the blocks.
  151.     // If we don't do this, the range may be collapsed to a point between
  152.     // two blocks, so get(Start|End)BlockOfRange will return null.
  153.     moveRangeBoundariesDownTree( range );
  154.  
  155.     // If we split into two different blocks, merge the blocks.
  156.     startBlock = getStartBlockOfRange( range, root );
  157.     if ( needsMerge ) {
  158.         endBlock = getEndBlockOfRange( range, root );
  159.         if ( startBlock && endBlock && startBlock !== endBlock ) {
  160.             mergeWithBlock( startBlock, endBlock, range );
  161.         }
  162.     }
  163.  
  164.     // Ensure block has necessary children
  165.     if ( startBlock ) {
  166.         fixCursor( startBlock, root );
  167.     }
  168.  
  169.     // Ensure root has a block-level element in it.
  170.     var child = root.firstChild;
  171.     if ( !child || child.nodeName === 'BR' ) {
  172.         fixCursor( root, root );
  173.         range.selectNodeContents( root.firstChild );
  174.     } else {
  175.         range.collapse( range.endContainer === root ? true : false );
  176.     }
  177.     return frag;
  178. };
  179.  
  180. // ---
  181.  
  182. var insertTreeFragmentIntoRange = function ( range, frag, root ) {
  183.     // Check if it's all inline content
  184.     var allInline = true,
  185.         children = frag.childNodes,
  186.         l = children.length;
  187.     while ( l-- ) {
  188.         if ( !isInline( children[l] ) ) {
  189.             allInline = false;
  190.             break;
  191.         }
  192.     }
  193.  
  194.     // Delete any selected content
  195.     if ( !range.collapsed ) {
  196.         deleteContentsOfRange( range, root );
  197.     }
  198.  
  199.     // Move range down into text nodes
  200.     moveRangeBoundariesDownTree( range );
  201.  
  202.     if ( allInline ) {
  203.         // If inline, just insert at the current position.
  204.         insertNodeInRange( range, frag );
  205.         if ( range.startContainer !== range.endContainer ) {
  206.             mergeInlines( range.endContainer, range );
  207.         }
  208.         mergeInlines( range.startContainer, range );
  209.         range.collapse( false );
  210.     } else {
  211.         // Otherwise...
  212.         // 1. Split up to blockquote (if a parent) or root
  213.         var splitPoint = range.startContainer,
  214.             nodeAfterSplit = split(
  215.                 splitPoint,
  216.                 range.startOffset,
  217.                 getNearest( splitPoint.parentNode, root, 'BLOCKQUOTE' ) || root,
  218.                 root
  219.             ),
  220.             nodeBeforeSplit = nodeAfterSplit.previousSibling,
  221.             startContainer = nodeBeforeSplit,
  222.             startOffset = startContainer.childNodes.length,
  223.             endContainer = nodeAfterSplit,
  224.             endOffset = 0,
  225.             parent = nodeAfterSplit.parentNode,
  226.             child, node, prev, next, startAnchor;
  227.  
  228.         // 2. Move down into edge either side of split and insert any inline
  229.         // nodes at the beginning/end of the fragment
  230.         while ( ( child = startContainer.lastChild ) &&
  231.                 child.nodeType === ELEMENT_NODE ) {
  232.             if ( child.nodeName === 'BR' ) {
  233.                 startOffset -= 1;
  234.                 break;
  235.             }
  236.             startContainer = child;
  237.             startOffset = startContainer.childNodes.length;
  238.         }
  239.         while ( ( child = endContainer.firstChild ) &&
  240.                 child.nodeType === ELEMENT_NODE &&
  241.                 child.nodeName !== 'BR' ) {
  242.             endContainer = child;
  243.         }
  244.         startAnchor = startContainer.childNodes[ startOffset ] || null;
  245.         while ( ( child = frag.firstChild ) && isInline( child ) ) {
  246.             startContainer.insertBefore( child, startAnchor );
  247.         }
  248.         while ( ( child = frag.lastChild ) && isInline( child ) ) {
  249.             endContainer.insertBefore( child, endContainer.firstChild );
  250.             endOffset += 1;
  251.         }
  252.  
  253.         // 3. Fix cursor then insert block(s) in the fragment
  254.         node = frag;
  255.         while ( node = getNextBlock( node, root ) ) {
  256.             fixCursor( node, root );
  257.         }
  258.         parent.insertBefore( frag, nodeAfterSplit );
  259.  
  260.         // 4. Remove empty nodes created either side of split, then
  261.         // merge containers at the edges.
  262.         next = nodeBeforeSplit.nextSibling;
  263.         node = getPreviousBlock( next, root );
  264.         if ( node && !/\S/.test( node.textContent ) ) {
  265.             do {
  266.                 parent = node.parentNode;
  267.                 parent.removeChild( node );
  268.                 node = parent;
  269.             } while ( node && !node.lastChild && node !== root );
  270.         }
  271.         if ( !nodeBeforeSplit.parentNode ) {
  272.             nodeBeforeSplit = next.previousSibling;
  273.         }
  274.         if ( !startContainer.parentNode ) {
  275.             startContainer = nodeBeforeSplit || next.parentNode;
  276.             startOffset = nodeBeforeSplit ?
  277.                 nodeBeforeSplit.childNodes.length : 0;
  278.         }
  279.         // Merge inserted containers with edges of split
  280.         if ( isContainer( next ) ) {
  281.             mergeContainers( next, root );
  282.         }
  283.  
  284.         prev = nodeAfterSplit.previousSibling;
  285.         node = isBlock( nodeAfterSplit ) ?
  286.             nodeAfterSplit : getNextBlock( nodeAfterSplit, root );
  287.         if ( node && !/\S/.test( node.textContent ) ) {
  288.             do {
  289.                 parent = node.parentNode;
  290.                 parent.removeChild( node );
  291.                 node = parent;
  292.             } while ( node && !node.lastChild && node !== root );
  293.         }
  294.         if ( !nodeAfterSplit.parentNode ) {
  295.             nodeAfterSplit = prev.nextSibling;
  296.         }
  297.         if ( !endOffset ) {
  298.             endContainer = prev;
  299.             endOffset = prev.childNodes.length;
  300.         }
  301.         // Merge inserted containers with edges of split
  302.         if ( nodeAfterSplit && isContainer( nodeAfterSplit ) ) {
  303.             mergeContainers( nodeAfterSplit, root );
  304.         }
  305.  
  306.         range.setStart( startContainer, startOffset );
  307.         range.setEnd( endContainer, endOffset );
  308.         moveRangeBoundariesDownTree( range );
  309.     }
  310. };
  311.  
  312. // ---
  313.  
  314. var isNodeContainedInRange = function ( range, node, partial ) {
  315.     var nodeRange = node.ownerDocument.createRange();
  316.  
  317.     nodeRange.selectNode( node );
  318.  
  319.     if ( partial ) {
  320.         // Node must not finish before range starts or start after range
  321.         // finishes.
  322.         var nodeEndBeforeStart = ( range.compareBoundaryPoints(
  323.                 END_TO_START, nodeRange ) > -1 ),
  324.             nodeStartAfterEnd = ( range.compareBoundaryPoints(
  325.                 START_TO_END, nodeRange ) < 1 );
  326.         return ( !nodeEndBeforeStart && !nodeStartAfterEnd );
  327.     }
  328.     else {
  329.         // Node must start after range starts and finish before range
  330.         // finishes
  331.         var nodeStartAfterStart = ( range.compareBoundaryPoints(
  332.                 START_TO_START, nodeRange ) < 1 ),
  333.             nodeEndBeforeEnd = ( range.compareBoundaryPoints(
  334.                 END_TO_END, nodeRange ) > -1 );
  335.         return ( nodeStartAfterStart && nodeEndBeforeEnd );
  336.     }
  337. };
  338.  
  339. var moveRangeBoundariesDownTree = function ( range ) {
  340.     var startContainer = range.startContainer,
  341.         startOffset = range.startOffset,
  342.         endContainer = range.endContainer,
  343.         endOffset = range.endOffset,
  344.         maySkipBR = true,
  345.         child;
  346.  
  347.     while ( startContainer.nodeType !== TEXT_NODE ) {
  348.         child = startContainer.childNodes[ startOffset ];
  349.         if ( !child || isLeaf( child ) ) {
  350.             break;
  351.         }
  352.         startContainer = child;
  353.         startOffset = 0;
  354.     }
  355.     if ( endOffset ) {
  356.         while ( endContainer.nodeType !== TEXT_NODE ) {
  357.             child = endContainer.childNodes[ endOffset - 1 ];
  358.             if ( !child || isLeaf( child ) ) {
  359.                 if ( maySkipBR && child && child.nodeName === 'BR' ) {
  360.                     endOffset -= 1;
  361.                     maySkipBR = false;
  362.                     continue;
  363.                 }
  364.                 break;
  365.             }
  366.             endContainer = child;
  367.             endOffset = getLength( endContainer );
  368.         }
  369.     } else {
  370.         while ( endContainer.nodeType !== TEXT_NODE ) {
  371.             child = endContainer.firstChild;
  372.             if ( !child || isLeaf( child ) ) {
  373.                 break;
  374.             }
  375.             endContainer = child;
  376.         }
  377.     }
  378.  
  379.     // If collapsed, this algorithm finds the nearest text node positions
  380.     // *outside* the range rather than inside, but also it flips which is
  381.     // assigned to which.
  382.     if ( range.collapsed ) {
  383.         range.setStart( endContainer, endOffset );
  384.         range.setEnd( startContainer, startOffset );
  385.     } else {
  386.         range.setStart( startContainer, startOffset );
  387.         range.setEnd( endContainer, endOffset );
  388.     }
  389. };
  390.  
  391. var moveRangeBoundariesUpTree = function ( range, common ) {
  392.     var startContainer = range.startContainer,
  393.         startOffset = range.startOffset,
  394.         endContainer = range.endContainer,
  395.         endOffset = range.endOffset,
  396.         maySkipBR = true,
  397.         parent;
  398.  
  399.     if ( !common ) {
  400.         common = range.commonAncestorContainer;
  401.     }
  402.  
  403.     while ( startContainer !== common && !startOffset ) {
  404.         parent = startContainer.parentNode;
  405.         startOffset = indexOf.call( parent.childNodes, startContainer );
  406.         startContainer = parent;
  407.     }
  408.  
  409.     while ( true ) {
  410.         if ( maySkipBR &&
  411.                 endContainer.nodeType !== TEXT_NODE &&
  412.                 endContainer.childNodes[ endOffset ] &&
  413.                 endContainer.childNodes[ endOffset ].nodeName === 'BR' ) {
  414.             endOffset += 1;
  415.             maySkipBR = false;
  416.         }
  417.         if ( endContainer === common ||
  418.                 endOffset !== getLength( endContainer ) ) {
  419.             break;
  420.         }
  421.         parent = endContainer.parentNode;
  422.         endOffset = indexOf.call( parent.childNodes, endContainer ) + 1;
  423.         endContainer = parent;
  424.     }
  425.  
  426.     range.setStart( startContainer, startOffset );
  427.     range.setEnd( endContainer, endOffset );
  428. };
  429.  
  430. // Returns the first block at least partially contained by the range,
  431. // or null if no block is contained by the range.
  432. var getStartBlockOfRange = function ( range, root ) {
  433.     var container = range.startContainer,
  434.         block;
  435.  
  436.     // If inline, get the containing block.
  437.     if ( isInline( container ) ) {
  438.         block = getPreviousBlock( container, root );
  439.     } else if ( container !== root && isBlock( container ) ) {
  440.         block = container;
  441.     } else {
  442.         block = getNodeBefore( container, range.startOffset );
  443.         block = getNextBlock( block, root );
  444.     }
  445.     // Check the block actually intersects the range
  446.     return block && isNodeContainedInRange( range, block, true ) ? block : null;
  447. };
  448.  
  449. // Returns the last block at least partially contained by the range,
  450. // or null if no block is contained by the range.
  451. var getEndBlockOfRange = function ( range, root ) {
  452.     var container = range.endContainer,
  453.         block, child;
  454.  
  455.     // If inline, get the containing block.
  456.     if ( isInline( container ) ) {
  457.         block = getPreviousBlock( container, root );
  458.     } else if ( container !== root && isBlock( container ) ) {
  459.         block = container;
  460.     } else {
  461.         block = getNodeAfter( container, range.endOffset );
  462.         if ( !block || !isOrContains( root, block ) ) {
  463.             block = root;
  464.             while ( child = block.lastChild ) {
  465.                 block = child;
  466.             }
  467.         }
  468.         block = getPreviousBlock( block, root );
  469.     }
  470.     // Check the block actually intersects the range
  471.     return block && isNodeContainedInRange( range, block, true ) ? block : null;
  472. };
  473.  
  474. var contentWalker = new TreeWalker( null,
  475.     SHOW_TEXT|SHOW_ELEMENT,
  476.     function ( node ) {
  477.         return node.nodeType === TEXT_NODE ?
  478.             notWS.test( node.data ) :
  479.             node.nodeName === 'IMG';
  480.     }
  481. );
  482.  
  483. var rangeDoesStartAtBlockBoundary = function ( range, root ) {
  484.     var startContainer = range.startContainer;
  485.     var startOffset = range.startOffset;
  486.     var nodeAfterCursor;
  487.  
  488.     // If in the middle or end of a text node, we're not at the boundary.
  489.     contentWalker.root = null;
  490.     if ( startContainer.nodeType === TEXT_NODE ) {
  491.         if ( startOffset ) {
  492.             return false;
  493.         }
  494.         nodeAfterCursor = startContainer;
  495.     } else {
  496.         nodeAfterCursor = getNodeAfter( startContainer, startOffset );
  497.         if ( nodeAfterCursor && !isOrContains( root, nodeAfterCursor ) ) {
  498.             nodeAfterCursor = null;
  499.         }
  500.         // The cursor was right at the end of the document
  501.         if ( !nodeAfterCursor ) {
  502.             nodeAfterCursor = getNodeBefore( startContainer, startOffset );
  503.             if ( nodeAfterCursor.nodeType === TEXT_NODE &&
  504.                     nodeAfterCursor.length ) {
  505.                 return false;
  506.             }
  507.         }
  508.     }
  509.  
  510.     // Otherwise, look for any previous content in the same block.
  511.     contentWalker.currentNode = nodeAfterCursor;
  512.     contentWalker.root = getStartBlockOfRange( range, root );
  513.  
  514.     return !contentWalker.previousNode();
  515. };
  516.  
  517. var rangeDoesEndAtBlockBoundary = function ( range, root ) {
  518.     var endContainer = range.endContainer,
  519.         endOffset = range.endOffset,
  520.         length;
  521.  
  522.     // If in a text node with content, and not at the end, we're not
  523.     // at the boundary
  524.     contentWalker.root = null;
  525.     if ( endContainer.nodeType === TEXT_NODE ) {
  526.         length = endContainer.data.length;
  527.         if ( length && endOffset < length ) {
  528.             return false;
  529.         }
  530.         contentWalker.currentNode = endContainer;
  531.     } else {
  532.         contentWalker.currentNode = getNodeBefore( endContainer, endOffset );
  533.     }
  534.  
  535.     // Otherwise, look for any further content in the same block.
  536.     contentWalker.root = getEndBlockOfRange( range, root );
  537.  
  538.     return !contentWalker.nextNode();
  539. };
  540.  
  541. var expandRangeToBlockBoundaries = function ( range, root ) {
  542.     var start = getStartBlockOfRange( range, root ),
  543.         end = getEndBlockOfRange( range, root ),
  544.         parent;
  545.  
  546.     if ( start && end ) {
  547.         parent = start.parentNode;
  548.         range.setStart( parent, indexOf.call( parent.childNodes, start ) );
  549.         parent = end.parentNode;
  550.         range.setEnd( parent, indexOf.call( parent.childNodes, end ) + 1 );
  551.     }
  552. };
  553.  
downloadRange.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
CRYENGINE - CRYENGINE is a powerful real-time game development... 2017-06-11
postal - 2017-06-11
reactide - Reactide is the first dedicated IDE for React web ... 2017-06-11
rkt - rkt is a pod-native container engine for Linux. It... 2017-06-11
uWebSockets - Tiny WebSockets https://for... 2017-06-11
realworld - TodoMVC for the RealWorld - Exemplary fullstack Me... 2017-06-11
goreplay - GoReplay is an open-source tool for capturing and ... 2017-06-10
pyenv - Simple Python version management 2017-06-10
redux-saga - An alternative side effect model for Redux apps ... 2017-06-10
angular-starter - 2017-06-10

 Back to top