' )
- .attr( 'id', module.id )
- .html( module.html )
- .data( 'context', context )
+
+ // This stuff is just hanging here, perhaps we could come up with a better home for this stuff
+ modules: {},
+
+ quickDialog: function ( body, settings ) {
+ $( '
' )
+ .text( body )
.appendTo( $( 'body' ) )
- .each( module.init )
- .dialog( configuration );
- // Set tabindexes on buttons added by .dialog()
- $.wikiEditor.modules.dialogs.fn.setTabindexes( dialogDiv.closest( '.ui-dialog' )
- .find( 'button' ).not( '[tabindex]' ) );
- if ( !( 'resizeme' in module ) || module.resizeme ) {
- dialogDiv
- .bind( 'dialogopen', $.wikiEditor.modules.dialogs.fn.resize )
- .find( '.ui-tabs' ).bind( 'tabsshow', function() {
- $(this).closest( '.ui-dialog-content' ).each(
- $.wikiEditor.modules.dialogs.fn.resize );
- });
- }
- dialogDiv.bind( 'dialogclose', function() {
- context.fn.restoreSelection();
- } );
-
- // Let the outside world know we set up this dialog
- context.$textarea.trigger( 'wikiEditor-dialogs-loaded-' + name );
- },
- /**
- * Resize a dialog so its contents fit
- *
- * Usage: dialog.each( resize ); or dialog.bind( 'blah', resize );
- * NOTE: This function assumes $.ui.dialog has already been loaded
- */
- resize: function() {
- var wrapper = $(this).closest( '.ui-dialog' );
- var oldWidth = wrapper.width();
- // Make sure elements don't wrapped so we get an accurate idea of whether they really fit. Also temporarily show
- // hidden elements. Work around jQuery bug where
inside a dialog is both
- // :visible and :hidden
- var oldHidden = $(this).find( '*' ).not( ':visible' );
- // Save the style attributes of the hidden elements to restore them later. Calling hide() after show() messes up
- // for elements hidden with a class
- oldHidden.each( function() {
- $(this).data( 'oldstyle', $(this).attr( 'style' ) );
- });
- oldHidden.show();
- var oldWS = $(this).css( 'white-space' );
- $(this).css( 'white-space', 'nowrap' );
- if ( wrapper.width() <= $(this).get(0).scrollWidth ) {
- var thisWidth = $(this).data( 'thisWidth' ) ? $(this).data( 'thisWidth' ) : 0;
- thisWidth = Math.max( $(this).get(0).width, thisWidth );
- $(this).width( thisWidth );
- $(this).data( 'thisWidth', thisWidth );
- var wrapperWidth = $(this).data( 'wrapperWidth' ) ? $(this).data( 'wrapperWidth' ) : 0;
- wrapperWidth = Math.max( wrapper.get(0).scrollWidth, wrapperWidth );
- wrapper.width( wrapperWidth );
- $(this).data( 'wrapperWidth', wrapperWidth );
- $(this).dialog( { 'width': wrapper.width() } );
- wrapper.css( 'left', parseInt( wrapper.css( 'left' ) ) - ( wrapper.width() - oldWidth ) / 2 );
- }
- $(this).css( 'white-space', oldWS );
- oldHidden.each( function() {
- $(this).attr( 'style', $(this).data( 'oldstyle' ) );
- });
- },
- /**
- * Set the right tabindexes on elements in a dialog
- * @param $elements Elements to set tabindexes on. If they already have tabindexes, this function can behave a bit weird
- */
- setTabindexes: function( $elements ) {
- // Get the highest tab index
- var tabIndex = $( document ).lastTabIndex() + 1;
- $elements.each( function() {
- $(this).attr( 'tabindex', tabIndex++ );
- } );
+ .dialog( $.extend( {
+ bgiframe: true,
+ modal: true
+ }, settings ) )
+ .dialog( 'open' );
}
-},
-// This stuff is just hanging here, perhaps we could come up with a better home for this stuff
-modules: {},
-quickDialog: function( body, settings ) {
- $( '
' )
- .text( body )
- .appendTo( $( 'body' ) )
- .dialog( $.extend( {
- bgiframe: true,
- modal: true
- }, settings ) )
- .dialog( 'open' );
-}
-}; } ) ( jQuery );
+};
+
+}( jQuery, mediaWiki ) );
diff --git a/modules/jquery.wikiEditor.highlight.js b/modules/jquery.wikiEditor.highlight.js
index 48140206..9d964fa7 100644
--- a/modules/jquery.wikiEditor.highlight.js
+++ b/modules/jquery.wikiEditor.highlight.js
@@ -1,357 +1,373 @@
/* Highlight module for wikiEditor */
-( function( $ ) { $.wikiEditor.modules.highlight = {
+( function ( $ ) {
-/**
- * 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 = {
+
+ /**
+ * 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, event.data.scope );
+ $.wikiEditor.modules.highlight.fn.mark( context, 'ready' );
}
},
- '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
+ * Internally used functions
*/
- 'create': function( context, config ) {
- context.modules.highlight.markersStr = '';
- },
- /**
- * Scans text division for tokens
- *
- * @param division
- */
- 'scan': function( context, division ) {
- // Remove all existing tokens
- var tokenArray = context.modules.highlight.tokenArray = [];
- // Scan text for new tokens
- var 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 ( var module in context.modules ) {
- if ( module in $.wikiEditor.modules && 'exp' in $.wikiEditor.modules[module] ) {
- for ( var 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, left, right, match;
- 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;
+ 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 ) {
- // Reset markers
- var markers = [];
+ // 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' );
+ },
- // Recycle markers that will be skipped in this run
- if ( context.modules.highlight.markers && division != '' ) {
- for ( var 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;
+ /**
+ * 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;
- // Get all markers
- context.fn.trigger( 'mark' );
- markers.sort( function( a, b ) { return a.start - b.start || a.end - b.end; } );
+ // Reset markers
+ var markers = [];
- // 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 ( var 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 ( var 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
- var subtracted = s.offset;
- var oldLength = s.length;
-
- var j, o;
- // 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;
+ // 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] );
}
}
}
- 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;
+ 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 + ',';
}
- 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
- var subtracted = e.offset + 1;
- var oldLength = e.length;
- var j, o;
- // 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;
+ 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;
+ }
}
}
- // 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;
+ 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
with
- 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
, insert a
- startNode.parentNode.insertBefore(
- startNode.ownerDocument.createElement( 'br' ),
- afterStart
- );
- }
- // A
with just a
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
, insert a
- startNode.parentNode.appendChild(
- startNode.ownerDocument.createElement( 'br' )
- );
- }
- // A
with just a
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 );
+ // 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
with
+ 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
, insert a
+ startNode.parentNode.insertBefore(
+ startNode.ownerDocument.createElement( 'br' ),
+ afterStart
+ );
+ }
+ // A
with just a
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
, insert a
+ startNode.parentNode.appendChild(
+ startNode.ownerDocument.createElement( 'br' )
+ );
+ }
+ // A
with just a
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 );
}
- // Remove oldParent, which is now empty
- oldParent.parentNode.removeChild( oldParent );
+ lastP = t.inP;
}
- 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
}
- // 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;
+ // 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;
}
- // 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] );
+ $( 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 {
- // Update the marker object
- $( anchor ).data( 'marker', markers[i] );
- if ( typeof markers[i].onSkip == 'function' ) {
- markers[i].onSkip( anchor );
- }
+ // Assume anchor == 'wrap'
+ $(this).replaceWith( this.childNodes );
}
- visited[v++] = anchor;
- }
+ context.fn.purgeOffsets();
+ });
+
}
- // 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
- var 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 );
+}( jQuery ) );
diff --git a/modules/jquery.wikiEditor.js b/modules/jquery.wikiEditor.js
index c2fb4094..6e87a45f 100644
--- a/modules/jquery.wikiEditor.js
+++ b/modules/jquery.wikiEditor.js
@@ -2,13 +2,13 @@
* This plugin provides a way to build a wiki-text editing user interface around a textarea.
*
* @example To intialize without any modules:
- * $( 'div#edittoolbar' ).wikiEditor();
+ * $( 'div#edittoolbar' ).wikiEditor();
*
* @example To initialize with one or more modules, or to add modules after it's already been initialized:
- * $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'toolbar', { ... config ... } );
+ * $( 'textarea#wpTextbox1' ).wikiEditor( 'addModule', 'toolbar', { ... config ... } );
*
*/
-( function( $ ) {
+( function ( $ ) {
/**
* Global static object for wikiEditor that provides generally useful functionality to all modules and contexts.
@@ -19,63 +19,68 @@ $.wikiEditor = {
* module name. The existance of a module in this object only indicates the module is available. To check if a
* module is in use by a specific context check the context.modules object.
*/
- 'modules': {},
+ modules: {},
+
/**
* A context can be extended, such as adding iframe support, on a per-wikiEditor instance basis.
*/
- 'extensions': {},
+ extensions: {},
+
/**
* In some cases like with the iframe's HTML file, it's convienent to have a lookup table of all instances of the
* WikiEditor. Each context contains an instance field which contains a key that corrosponds to a reference to the
* textarea which the WikiEditor was build around. This way, by passing a simple integer you can provide a way back
* to a specific context.
*/
- 'instances': [],
+ instances: [],
+
/**
* For each browser name, an array of conditions that must be met are supplied in [operaton, value]-form where
* operation is a string containing a JavaScript compatible binary operator and value is either a number to be
* compared with $.browser.versionNumber or a string to be compared with $.browser.version. If a browser is not
* specifically mentioned, we just assume things will work.
*/
- 'browsers': {
+ browsers: {
// Left-to-right languages
- 'ltr': {
+ ltr: {
// The toolbar layout is broken in IE6
- 'msie': [['>=', 7]],
+ msie: [['>=', 7]],
// Layout issues in FF < 2
- 'firefox': [['>=', 2]],
+ firefox: [['>=', 2]],
// Text selection bugs galore - this may be a different situation with the new iframe-based solution
- 'opera': [['>=', 9.6]],
+ opera: [['>=', 9.6]],
// jQuery minimums
- 'safari': [['>=', 3]],
- 'chrome': [['>=', 3]],
- 'netscape': [['>=', 9]],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
+ safari: [['>=', 3]],
+ chrome: [['>=', 3]],
+ netscape: [['>=', 9]],
+ blackberry: false,
+ ipod: false,
+ iphone: false
},
// Right-to-left languages
- 'rtl': {
+ rtl: {
// The toolbar layout is broken in IE 7 in RTL mode, and IE6 in any mode
- 'msie': [['>=', 8]],
+ msie: [['>=', 8]],
// Layout issues in FF < 2
- 'firefox': [['>=', 2]],
+ firefox: [['>=', 2]],
// Text selection bugs galore - this may be a different situation with the new iframe-based solution
- 'opera': [['>=', 9.6]],
+ opera: [['>=', 9.6]],
// jQuery minimums
- 'safari': [['>=', 3]],
- 'chrome': [['>=', 3]],
- 'netscape': [['>=', 9]],
- 'blackberry': false,
- 'ipod': false,
- 'iphone': false
+ safari: [['>=', 3]],
+ chrome: [['>=', 3]],
+ netscape: [['>=', 9]],
+ blackberry: false,
+ ipod: false,
+ iphone: false
}
},
+
/**
* Path to images - this is a bit messy, and it would need to change if this code (and images) gets moved into the
* core - or anywhere for that matter...
*/
- 'imgPath' : mw.config.get( 'wgExtensionAssetsPath' ) + '/WikiEditor/modules/images/',
+ imgPath : mw.config.get( 'wgExtensionAssetsPath' ) + '/WikiEditor/modules/images/',
+
/**
* Checks the current browser against the browsers object to determine if the browser has been black-listed or not.
* Because these rules are often very complex, the object contains configurable operators and can check against
@@ -88,7 +93,7 @@ $.wikiEditor = {
* "open-web" way to go.
* @param module Module object, defaults to $.wikiEditor
*/
- 'isSupported': function( module ) {
+ isSupported: function ( module ) {
// Fallback to the wikiEditor browser map if no special map is provided in the module
var mod = module && 'browsers' in module ? module : $.wikiEditor;
// Check for and make use of cached value and early opportunities to bail
@@ -99,21 +104,23 @@ $.wikiEditor = {
// Run a browser support test and then cache and return the result
return mod.supported = $.client.test( mod.browsers );
},
+
/**
* Checks if a module has a specific requirement
* @param module Module object
* @param requirement String identifying requirement
*/
- 'isRequired': function( module, requirement ) {
- if ( typeof module['req'] !== 'undefined' ) {
- for ( var req in module['req'] ) {
- if ( module['req'][req] == requirement ) {
+ isRequired: function ( module, requirement ) {
+ if ( typeof module.req !== 'undefined' ) {
+ for ( var req in module.req ) {
+ if ( module.req[req] == requirement ) {
return true;
}
}
}
return false;
},
+
/**
* Provides a way to extract messages from objects. Wraps the mediaWiki.msg() function, which
* may eventually become a wrapper for some kind of core MW functionality.
@@ -124,7 +131,7 @@ $.wikiEditor = {
* would return the raw text 'that', while passing property as 'foo' would return the internationalized message
* with the key 'bar'.
*/
- 'autoMsg': function( object, property ) {
+ autoMsg: function ( object, property ) {
// Accept array of possible properties, of which the first one found will be used
if ( typeof property == 'object' ) {
for ( var i in property ) {
@@ -147,6 +154,7 @@ $.wikiEditor = {
return '';
}
},
+
/**
* Provides a way to extract a property of an object in a certain language, falling back on the property keyed as
* 'default' or 'default-rtl'. If such key doesn't exist, the object itself is considered the actual value, which
@@ -156,10 +164,11 @@ $.wikiEditor = {
* @param object Object to extract property from
* @param lang Language code, defaults to wgUserLanguage
*/
- 'autoLang': function( object, lang ) {
+ autoLang: function ( object, lang ) {
var defaultKey = $( 'body' ).hasClass( 'rtl' ) ? 'default-rtl' : 'default';
return object[lang || mw.config.get( 'wgUserLanguage' )] || object[defaultKey] || object['default'] || object;
},
+
/**
* Provides a way to extract the path of an icon in a certain language, automatically appending a version number for
* caching purposes and prepending an image path when icon paths are relative.
@@ -168,7 +177,7 @@ $.wikiEditor = {
* @param path Default icon path, defaults to $.wikiEditor.imgPath
* @param lang Language code, defaults to wgUserLanguage
*/
- 'autoIcon': function( icon, path, lang ) {
+ autoIcon: function ( icon, path, lang ) {
var src = $.wikiEditor.autoLang( icon, lang );
path = path || $.wikiEditor.imgPath;
// Prepend path if src is not absolute
@@ -177,6 +186,7 @@ $.wikiEditor = {
}
return src + '?' + mw.loader.version( 'jquery.wikiEditor' );
},
+
/**
* Get the sprite offset for a language if available, icon for a language if available, or the default offset or icon,
* in that order of preference.
@@ -185,7 +195,7 @@ $.wikiEditor = {
* @param path Icon path, see autoIcon()
* @param lang Language code, defaults to wgUserLanguage
*/
- 'autoIconOrOffset': function( icon, offset, path, lang ) {
+ autoIconOrOffset: function ( icon, offset, path, lang ) {
lang = lang || mw.config.get( 'wgUserLanguage' );
if ( typeof offset == 'object' && lang in offset ) {
return offset[lang];
@@ -200,7 +210,7 @@ $.wikiEditor = {
/**
* jQuery plugin that provides a way to initialize a wikiEditor instance on a textarea.
*/
-$.fn.wikiEditor = function() {
+$.fn.wikiEditor = function () {
// Skip any further work when running in browsers that are unsupported
if ( !$.wikiEditor.isSupported() ) {
@@ -263,21 +273,22 @@ if ( !context || typeof context == 'undefined' ) {
* @param data Either a string of the name of a module to add without any additional configuration parameters,
* or an object with members keyed with module names and valued with configuration objects.
*/
- 'addModule': function( context, data ) {
- var modules = {};
+ 'addModule': function ( context, data ) {
+ var module, call,
+ modules = {};
if ( typeof data == 'string' ) {
modules[data] = {};
} else if ( typeof data == 'object' ) {
modules = data;
}
- for ( var module in modules ) {
+ for ( module in modules ) {
// Check for the existance of an available / supported module with a matching name and a create function
if ( typeof module == 'string' && typeof $.wikiEditor.modules[module] !== 'undefined' &&
$.wikiEditor.isSupported( $.wikiEditor.modules[module] ) )
{
// Extend the context's core API with this module's own API calls
if ( 'api' in $.wikiEditor.modules[module] ) {
- for ( var call in $.wikiEditor.modules[module].api ) {
+ for ( call in $.wikiEditor.modules[module].api ) {
// Modules may not overwrite existing API functions - first come, first serve
if ( !( call in context.api ) ) {
context.api[call] = $.wikiEditor.modules[module].api[call];
@@ -313,7 +324,7 @@ if ( !context || typeof context == 'undefined' ) {
/**
* Executes core event filters as well as event handlers provided by modules.
*/
- 'trigger': function( name, event ) {
+ trigger: function ( name, event ) {
// Event is an optional argument, but from here on out, at least the type field should be dependable
if ( typeof event == 'undefined' ) {
event = { 'type': 'custom' };
@@ -339,9 +350,9 @@ if ( !context || typeof context == 'undefined' ) {
name in $.wikiEditor.modules[module].evt
) {
var ret = $.wikiEditor.modules[module].evt[name]( context, event );
- if (ret != null) {
+ if (ret !== null) {
//if 1 returns false, the end result is false
- if( returnFromModules == null ) {
+ if( returnFromModules === null ) {
returnFromModules = ret;
} else {
returnFromModules = returnFromModules && ret;
@@ -349,45 +360,47 @@ if ( !context || typeof context == 'undefined' ) {
}
}
}
- if ( returnFromModules != null ) {
+ if ( returnFromModules !== null ) {
return returnFromModules;
} else {
return true;
}
},
+
/**
* Adds a button to the UI
*/
- 'addButton': function( options ) {
+ addButton: function ( options ) {
// Ensure that buttons and tabs are visible
context.$controls.show();
context.$buttons.show();
- return $( '' )
+ return $( '