Index: trunk/extensions/UsabilityInitiative/js/plugins/jquery.wikiEditor.js |
— | — | @@ -85,6 +85,7 @@ |
86 | 86 | 'isSupported': function( module ) { |
87 | 87 | // Fallback to the wikiEditor browser map if no special map is provided in the module |
88 | 88 | var mod = module && 'browsers' in module ? module : $.wikiEditor; |
| 89 | + return mod.supported = true; |
89 | 90 | // Check for and make use of cached value and early opportunities to bail |
90 | 91 | if ( typeof mod.supported !== 'undefined' ) { |
91 | 92 | // Cache hit |
— | — | @@ -418,8 +419,12 @@ |
419 | 420 | 'paste': function( event ) { |
420 | 421 | // Save the cursor position to restore it after all this voodoo |
421 | 422 | var cursorPos = context.fn.getCaretPosition(); |
422 | | - var oldLength = context.fn.getContents().length - ( cursorPos[1] - cursorPos[0] ); |
423 | | - context.$content.find( ':not(.wikiEditor)' ).addClass( 'wikiEditor' ); |
| 423 | + if ( !context.$content.text() ) { |
| 424 | + context.$content.empty(); |
| 425 | + } |
| 426 | + var oldLength = context.fn.getContents().length; |
| 427 | + |
| 428 | + context.$content.find( '*' ).addClass( 'wikiEditor' ); |
424 | 429 | if ( $.layout.name !== 'webkit' ) { |
425 | 430 | context.$content.addClass( 'pasting' ); |
426 | 431 | } |
— | — | @@ -439,58 +444,82 @@ |
440 | 445 | var outerParent = $(this).parent(); |
441 | 446 | outerParent.replaceWith( outerParent.childNodes ); |
442 | 447 | } ); |
| 448 | + |
443 | 449 | // Unwrap the span found in webkit copies (Apple Richtext) |
444 | | - context.$content.find( 'span.Apple-style-span' ).each( function() { |
445 | | - $(this).replaceWith( this.childNodes ); |
446 | | - } ); |
| 450 | + if ( ! $.browser.msie ) { |
| 451 | + context.$content.find( 'span.Apple-style-span' ).each( function() { |
| 452 | + $(this).replaceWith( this.childNodes ); |
| 453 | + } ); |
| 454 | + } |
447 | 455 | |
448 | | - // If the pasted content is plain text then wrap it in a <p> and adjust the <br> accordingly |
449 | | - var pasteContent = context.fn.getOffset( cursorPos[0] ).node; |
450 | | - var removeNextBR = false; |
451 | | - while ( pasteContent != null && !$( pasteContent ).hasClass( 'wikiEditor' ) ) { |
452 | | - var currentNode = pasteContent; |
453 | | - pasteContent = pasteContent.nextSibling; |
454 | | - if ( currentNode.nodeName == '#text' && currentNode.nodeValue == currentNode.wholeText ) { |
455 | | - var pWrapper = $( '<p />' ).addClass( 'wikiEditor' ); |
456 | | - $( currentNode ).wrap( pWrapper ); |
457 | | - $( currentNode ).addClass( 'wikiEditor' ); |
458 | | - removeNextBR = true; |
459 | | - } else if ( currentNode.nodeName == 'BR' && removeNextBR ) { |
460 | | - $( currentNode ).remove(); |
461 | | - removeNextBR = false; |
462 | | - } else { |
463 | | - removeNextBR = false; |
464 | | - } |
465 | | - } |
466 | 456 | var $selection = context.$content.find( ':not(.wikiEditor)' ); |
| 457 | + var $previousElement; |
467 | 458 | while ( $selection.length && $selection.length > 0 ) { |
468 | 459 | var $currentElement = $selection.eq( 0 ); |
| 460 | + |
| 461 | + //go up till we find the first pasted element |
469 | 462 | while ( !$currentElement.parent().is( 'body' ) && !$currentElement.parent().is( '.wikiEditor' ) ) { |
470 | 463 | $currentElement = $currentElement.parent(); |
471 | 464 | } |
| 465 | + //go to the previous element till we find the first pasted element |
| 466 | + while ( $currentElement[0] != null && |
| 467 | + $currentElement[0].previousSibling != null && |
| 468 | + !$( $currentElement[0].previousSibling ).hasClass( 'wikiEditor' ) ) { |
| 469 | + $currentElement = $( $currentElement[0].previousSibling ); |
| 470 | + } |
472 | 471 | |
| 472 | + //each pasted element is always wrapped in a <p> |
473 | 473 | var $newElement; |
474 | | - if ( $currentElement.is( 'p' ) || $currentElement.is( 'div' ) || $currentElement.is( 'pre' ) ) { |
475 | | - //Convert all <div>, <p> and <pre> that was pasted into a <p> element |
476 | | - $newElement = $( '<p />' ); |
| 474 | + var textNode = false; |
| 475 | + if ( $currentElement[0].nodeName == '#text' ) { |
| 476 | + $newElement = $( '<p></p>' ); |
| 477 | + textNode = true; |
| 478 | + } else if ( $currentElement.is( 'p' ) || $currentElement.is( 'pre' ) || $currentElement.is( 'br' ) ) { |
| 479 | + $newElement = $( '<p></p>' ); |
477 | 480 | } else { |
478 | | - // everything else becomes a <span> |
479 | | - $newElement = $( '<span />' ).addClass( 'wikiEditor' ); |
| 481 | + $newElement = $( '<span></span>' ); |
480 | 482 | } |
| 483 | + var newElementHTML = ''; |
| 484 | + var currentHTML = ''; |
481 | 485 | |
482 | | - // If the pasted content was html, just convert it into text and <br> |
483 | | - var pieces = $.trim( $currentElement.text() ).split( '\n' ); |
484 | | - var newElementHTML = ''; |
| 486 | + |
| 487 | + if ( $currentElement[0].nodeName == '#text' ) { |
| 488 | + //if it is a text node then just append it |
| 489 | + currentHTML = $currentElement[0].nodeValue; |
| 490 | + } else { |
| 491 | + currentHTML = $currentElement.html(); |
| 492 | + //replace all forms of <p> tags with a \n. All other tags get removed. |
| 493 | + currentHTML = currentHTML.replace(/(<[\s]*p[^>]*>)|(<[\s]*\/p[^>]*>)|(<[\s]*p[^\/>]*\/>)/gi, '\n'); |
| 494 | + currentHTML = currentHTML.replace(/(<[^>]*>)|(<[^\>]*\>)/gi, ''); |
| 495 | + |
| 496 | + } |
| 497 | + |
| 498 | + //wrap each piece in a <p> with a <br> in between. |
| 499 | + var pieces = currentHTML.split( '\n' ); |
485 | 500 | for ( var i = 0; i < pieces.length; i++ ) { |
486 | 501 | if ( pieces[i] ) { |
487 | | - newElementHTML += $.trim( pieces[i] ); |
488 | | - } else { |
489 | | - newElementHTML += '<span><br class="wikiEditor" /></span>'; |
| 502 | + if ( textNode || ! $newElement.is( 'p' ) ) { |
| 503 | + newElementHTML += '<p class="wikiEditor">' + pieces[i] + '</p>'; |
| 504 | + } else { |
| 505 | + newElementHTML += pieces[i]; |
| 506 | + } |
| 507 | + } else if ( textNode || ! $newElement.is( 'p' ) ) { |
| 508 | + newElementHTML += '<br class="wikiEditor" >'; |
490 | 509 | } |
| 510 | + |
| 511 | + if ( !textNode ) { |
| 512 | + newElementHTML += '<br class="wikiEditor" >'; |
| 513 | + } |
491 | 514 | } |
492 | | - $newElement.html( newElementHTML ) |
493 | | - .addClass( 'wikiEditor' ) |
494 | | - .insertAfter( $currentElement ); |
| 515 | + |
| 516 | + $newElement.html( newElementHTML ).addClass( 'wikiEditor' ); |
| 517 | + |
| 518 | + //remove extra <br>s |
| 519 | + if ( $newElement.is( 'p' ) && $currentElement[0].nextSibling != null && $( $currentElement[0].nextSibling ).is( 'br' ) ) { |
| 520 | + $( $currentElement[0].nextSibling ).remove(); |
| 521 | + } |
| 522 | + //swap out the original content with with newly sanitized one |
| 523 | + $newElement.insertAfter( $currentElement ); |
495 | 524 | $currentElement.remove(); |
496 | 525 | |
497 | 526 | $selection = context.$content.find( ':not(.wikiEditor)' ); |
— | — | @@ -501,10 +530,14 @@ |
502 | 531 | context.$content.removeClass( 'pasting' ); |
503 | 532 | } |
504 | 533 | |
| 534 | + |
505 | 535 | // Restore cursor position |
506 | 536 | context.fn.purgeOffsets(); |
507 | 537 | var newLength = context.fn.getContents().length; |
508 | 538 | var restoreTo = cursorPos[0] + newLength - oldLength; |
| 539 | + if ( restoreTo > newLength ) { |
| 540 | + restoreTo = newLength; |
| 541 | + } |
509 | 542 | context.fn.setSelection( { start: restoreTo, end: restoreTo } ); |
510 | 543 | }, 0 ); |
511 | 544 | return true; |
— | — | @@ -1626,10 +1659,10 @@ |
1627 | 1660 | end = e ? e.offset : null; |
1628 | 1661 | // Don't try to set the selection past the end of a node, causes errors |
1629 | 1662 | // Just put the selection at the end of the node in this case |
1630 | | - if ( sc.nodeName == '#text' && start > sc.nodeValue.length ) { |
| 1663 | + if ( sc != null && sc.nodeName == '#text' && start > sc.nodeValue.length ) { |
1631 | 1664 | start = sc.nodeValue.length - 1; |
1632 | 1665 | } |
1633 | | - if ( ec.nodeName == '#text' && end > ec.nodeValue.length ) { |
| 1666 | + if ( ec != null && ec.nodeName == '#text' && end > ec.nodeValue.length ) { |
1634 | 1667 | end = ec.nodeValue.length - 1; |
1635 | 1668 | } |
1636 | 1669 | } |