diff options
Diffstat (limited to 'extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js')
-rw-r--r-- | extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js | 373 |
1 files changed, 0 insertions, 373 deletions
diff --git a/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js b/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js deleted file mode 100644 index 9d964fa7..00000000 --- a/extensions/WikiEditor/modules/jquery.wikiEditor.highlight.js +++ /dev/null @@ -1,373 +0,0 @@ -/* Highlight module for wikiEditor */ - -( function ( $ ) { - -$.wikiEditor.modules.highlight = { - - /** - * Core Requirements - */ - req: [ 'iframe' ], - - /** - * Configuration - */ - cfg: { - styleVersion: 3 - }, - - /** - * Internally used event handlers - */ - evt: { - delayedChange: function ( context, event ) { - if ( event.data.scope == 'realchange' ) { - $.wikiEditor.modules.highlight.fn.scan( context ); - $.wikiEditor.modules.highlight.fn.mark( context, event.data.scope ); - } - }, - ready: function ( context, event ) { - $.wikiEditor.modules.highlight.fn.scan( context ); - $.wikiEditor.modules.highlight.fn.mark( context, 'ready' ); - } - }, - - /** - * Internally used functions - */ - fn: { - /** - * Creates a highlight module within a wikiEditor - * - * @param config Configuration object to create module from - */ - create: function ( context, config ) { - context.modules.highlight.markersStr = ''; - }, - /** - * Scans text division for tokens - * - * @param division - */ - scan: function ( context, division ) { - var tokenArray, text, module, exp, - left, right, match; - /*jshint eqnull: true */ - - // Remove all existing tokens - tokenArray = context.modules.highlight.tokenArray = []; - // Scan text for new tokens - text = context.fn.getContents(); - // Perform a scan for each module which provides any expressions to scan for - // FIXME: This traverses the entire string once for every regex. Investigate - // whether |-concatenating regexes then traversing once is faster. - for ( module in context.modules ) { - if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) { - for ( exp in $.wikiEditor.modules[module].exp ) { - // Prepare configuration - var regex = $.wikiEditor.modules[module].exp[exp].regex; - var label = $.wikiEditor.modules[module].exp[exp].label; - var markAfter = $.wikiEditor.modules[module].exp[exp].markAfter || false; - // Search for tokens - var offset = 0; - while ( ( match = text.substr( offset ).match( regex ) ) != null ) { - right = ( left = offset + match.index ) + match[0].length; - tokenArray[tokenArray.length] = { - offset: markAfter ? right : left, - label: label, - tokenStart: left, - match: match - }; - // Move to the right of this match - offset = right; - } - } - } - } - // Sort by start - tokenArray.sort( function ( a, b ) { - return a.tokenStart - b.tokenStart; - } ); - // Let the world know, a scan just happened! - context.fn.trigger( 'scan' ); - }, - - /** - * Marks up text with HTML - * - * @param division - * @param tokens - */ - // FIXME: What do division and tokens do? - // TODO: Document the scan() and mark() APIs somewhere - mark: function ( context, division, tokens ) { - var i, subtracted, oldLength, j, o; - - // Reset markers - var markers = []; - - // Recycle markers that will be skipped in this run - if ( context.modules.highlight.markers && division !== '' ) { - for ( i = 0; i < context.modules.highlight.markers.length; i++ ) { - if ( context.modules.highlight.markers[i].skipDivision == division ) { - markers.push( context.modules.highlight.markers[i] ); - } - } - } - context.modules.highlight.markers = markers; - - // Get all markers - context.fn.trigger( 'mark' ); - markers.sort( function ( a, b ) { - return a.start - b.start || a.end - b.end; - } ); - - // Serialize the markers array to a string and compare it with the one stored in the previous run - if they're - // equal, there's no markers to change - var markersStr = ''; - for ( i = 0; i < markers.length; i++ ) { - markersStr += markers[i].start + ',' + markers[i].end + ',' + markers[i].type + ','; - } - if ( context.modules.highlight.markersStr == markersStr ) { - // No change, bail out - return; - } - context.modules.highlight.markersStr = markersStr; - - // Traverse the iframe DOM, inserting markers where they're needed - store visited markers here so we know which - // markers should be removed - var visited = [], v = 0; - for ( i = 0; i < markers.length; i++ ) { - if ( typeof markers[i].skipDivision !== 'undefined' && ( division == markers[i].skipDivision ) ) { - continue; - } - - // We want to isolate each marker, so we may need to split textNodes if a marker starts or ends halfway one. - var start = markers[i].start; - var s = context.fn.getOffset( start ); - if ( !s ) { - // This shouldn't happen - continue; - } - var startNode = s.node; - - // Don't wrap leading BRs, produces undesirable results - // FIXME: It's also possible that the offset is a bit high because getOffset() has incremented .length to - // fake the newline caused by startNode being in a P. In this case, prevent the textnode splitting below - // from making startNode an empty textnode, IE barfs on that - while ( startNode.nodeName === 'BR' || s.offset === startNode.nodeValue.length ) { - start++; - s = context.fn.getOffset( start ); - startNode = s.node; - } - - // The next marker starts somewhere in this textNode or at this BR - if ( s.offset > 0 && s.node.nodeName == '#text' ) { - // Split off the prefix - this leaves the prefix in the current node and puts the rest in a new node - // which is our start node - var newStartNode = startNode.splitText( s.offset < s.node.nodeValue.length ? - s.offset : s.node.nodeValue.length - 1 - ); - var oldStartNode = startNode; - startNode = newStartNode; - // Update offset objects. We don't need purgeOffsets(), simply manipulating the existing offset objects - // will suffice - // FIXME: This manipulates context.offsets directly, which is ugly, but the performance improvement vs. - // purgeOffsets() is worth it - this code doesn't set lastTextNode to newStartNode for offset objects - // with lastTextNode == oldStartNode, but that doesn't really matter - subtracted = s.offset; - oldLength = s.length; - - // Update offset objects referring to oldStartNode - for ( j = start - subtracted; j < start; j++ ) { - if ( j in context.offsets ) { - o = context.offsets[j]; - o.node = oldStartNode; - o.length = subtracted; - } - } - // Update offset objects referring to newStartNode - for ( j = start; j < start - subtracted + oldLength; j++ ) { - if ( j in context.offsets ) { - o = context.offsets[j]; - o.node = newStartNode; - o.offset -= subtracted; - o.length -= subtracted; - o.lastTextNode = oldStartNode; - } - } - } - var end = markers[i].end; - // To avoid ending up at the first char of the next node, we grab the offset for end - 1 and add one to the - // offset - var e = context.fn.getOffset( end - 1 ); - if ( !e ) { - // This shouldn't happen - continue; - } - var endNode = e.node; - if ( e.offset + 1 < e.length - 1 && endNode.nodeName == '#text' ) { - // Split off the suffix. This puts the suffix in a new node and leaves the rest in endNode - var oldEndNode = endNode; - var newEndNode = endNode.splitText( e.offset + 1 ); - // Update offset objects - subtracted = e.offset + 1; - oldLength = e.length; - - // Update offset objects referring to oldEndNode - for ( j = end - subtracted; j < end; j++ ) { - if ( j in context.offsets ) { - o = context.offsets[j]; - o.node = oldEndNode; - o.length = subtracted; - } - } - // We have to insert this one, as it might not exist: we didn't call getOffset( end ) - context.offsets[end] = { - 'node': newEndNode, - 'offset': 0, - 'length': oldLength - subtracted, - 'lastTextNode': oldEndNode - }; - // Update offset objects referring to newEndNode - for ( j = end + 1; j < end - subtracted + oldLength; j++ ) { - if ( j in context.offsets ) { - o = context.offsets[j]; - o.node = newEndNode; - o.offset -= subtracted; - o.length -= subtracted; - o.lastTextNode = oldEndNode; - } - } - } - // Don't wrap trailing BRs, doing that causes weird issues - if ( endNode.nodeName == 'BR' ) { - endNode = e.lastTextNode; - } - // If startNode and endNode have different parents, we need to pull endNode and all textnodes in between - // into startNode's parent and replace </p><p> with <br> - if ( startNode.parentNode !== endNode.parentNode ) { - var startP = $( startNode ).closest( 'p' ).get( 0 ); - var t = new context.fn.rawTraverser( startNode, startP, context.$content.get( 0 ), false ); - var afterStart = startNode.nextSibling; - var lastP = startP; - var nextT = t.next(); - while ( nextT && t.node !== endNode ) { - t = nextT; - nextT = t.next(); - // If t.node has a different parent, merge t.node.parentNode with startNode.parentNode - if ( t.node.parentNode !== startNode.parentNode ) { - var oldParent = t.node.parentNode; - if ( afterStart ) { - if ( lastP !== t.inP ) { - // We're entering a new <p>, insert a <br> - startNode.parentNode.insertBefore( - startNode.ownerDocument.createElement( 'br' ), - afterStart - ); - } - // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it - if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) { - // Move all children of oldParent into startNode's parent - while ( oldParent.firstChild ) { - startNode.parentNode.insertBefore( oldParent.firstChild, afterStart ); - } - } - } else { - if ( lastP !== t.inP ) { - // We're entering a new <p>, insert a <br> - startNode.parentNode.appendChild( - startNode.ownerDocument.createElement( 'br' ) - ); - } - // A <p> with just a <br> in it is an empty line, so let's not bother with unwrapping it - if ( !( oldParent.childNodes.length == 1 && oldParent.firstChild.nodeName == 'BR' ) ) { - // Move all children of oldParent into startNode's parent - while ( oldParent.firstChild ) { - startNode.parentNode.appendChild( oldParent.firstChild ); - } - } - } - // Remove oldParent, which is now empty - oldParent.parentNode.removeChild( oldParent ); - } - lastP = t.inP; - } - // Moving nodes around like this invalidates offset objects - // TODO: Update offset objects ourselves for performance. Requires rewriting this code block to be - // offset-based rather than traverser-based - } - // Now wrap everything between startNode and endNode (may be equal). - var ca1 = startNode, ca2 = endNode; - if ( ca1 && ca2 && ca1.parentNode ) { - var anchor = markers[i].getAnchor( ca1, ca2 ); - if ( !anchor ) { - var commonAncestor = ca1.parentNode; - if ( markers[i].anchor == 'wrap') { - // We have to store things like .parentNode and .nextSibling because appendChild() changes these - var newNode = ca1.ownerDocument.createElement( 'span' ); - var nextNode = ca2.nextSibling; - // Append all nodes between ca1 and ca2 (inclusive) to newNode - var n = ca1; - while ( n !== nextNode ) { - var ns = n.nextSibling; - newNode.appendChild( n ); - n = ns; - } - // Insert newNode in the right place - if ( nextNode ) { - commonAncestor.insertBefore( newNode, nextNode ); - } else { - commonAncestor.appendChild( newNode ); - } - anchor = newNode; - } else if ( markers[i].anchor == 'tag' ) { - anchor = commonAncestor; - } - $( anchor ).data( 'marker', markers[i] ).addClass( 'wikiEditor-highlight' ); - // Allow the module adding this marker to manipulate it - markers[i].afterWrap( anchor, markers[i] ); - - } else { - // Update the marker object - $( anchor ).data( 'marker', markers[i] ); - if ( typeof markers[i].onSkip == 'function' ) { - markers[i].onSkip( anchor ); - } - } - visited[v++] = anchor; - } - } - // Remove markers that were previously inserted but weren't passed to this function - visited[] contains the - // visited elements in order and find() and each() preserve order - j = 0; - context.$content.find( '.wikiEditor-highlight' ).each( function () { - if ( visited[j] == this ) { - // This marker is legit, leave it in - j++; - return true; - } - // Remove this marker - var marker = $(this).data( 'marker' ); - if ( marker && typeof marker.skipDivision !== 'undefined' && ( division === marker.skipDivision ) ) { - // Don't remove these either - return true; - } - if ( marker && typeof marker.beforeUnwrap === 'function' ) - marker.beforeUnwrap( this ); - if ( ( marker && marker.anchor === 'tag' ) || $(this).is( 'p' ) ) { - // Remove all classes - $(this).removeAttr( 'class' ); - } else { - // Assume anchor == 'wrap' - $(this).replaceWith( this.childNodes ); - } - context.fn.purgeOffsets(); - }); - - } - } -}; - -}( jQuery ) ); - |