Make WordPress Core

Changeset 36677


Ignore:
Timestamp:
02/24/2016 06:20:01 AM (7 years ago)
Author:
azaozz
Message:

TinyMCE, inline link dialog:

  • Remove the bottom half of the (old) modal and add autocomplete on the URL field.
  • Disable the inline edit dialog in old IE (7, 8 and 9). Use only the modal there.
  • Fix in IE10 and 11.
  • Fix (most?) remaining edge cases.
  • Fix focusing the inline dialog, the modal and the editor.

See #33301.

Location:
trunk/src/wp-includes
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-editor.php

    r36483 r36677  
    14151415                <p class="howto"><?php _e( 'Enter the destination URL' ); ?></p>
    14161416                <div>
    1417                     <label><span><?php _e( 'URL' ); ?></span><input id="wp-link-url" type="text" /></label>
     1417                    <label><span><?php _e( 'URL' ); ?></span>
     1418                    <input id="wp-link-url" type="text" role="combobox" aria-autocomplete="list" aria-expanded="false" placeholder="<?php _e( 'Paste URL or type to search' ); ?>" /></label>
    14181419                </div>
    14191420                <div class="wp-link-text-field">
    1420                     <label><span><?php _e( 'Link Text' ); ?></span><input id="wp-link-text" type="text" /></label>
     1421                    <label><span><?php _e( 'Link Text' ); ?></span>
     1422                    <input id="wp-link-text" type="text" /></label>
    14211423                </div>
    14221424                <div class="link-target">
    14231425                    <label><span>&nbsp;</span><input type="checkbox" id="wp-link-target" /> <?php _e( 'Open link in a new tab' ); ?></label>
    1424                 </div>
    1425             </div>
    1426             <p class="howto"><a href="#" id="wp-link-search-toggle"><?php _e( 'Or link to existing content' ); ?></a></p>
    1427             <div id="search-panel">
    1428                 <div class="link-search-wrapper">
    1429                     <label>
    1430                         <span class="search-label"><?php _e( 'Search' ); ?></span>
    1431                         <input type="search" id="wp-link-search" class="link-search-field" autocomplete="off" />
    1432                         <span class="spinner"></span>
    1433                     </label>
    1434                 </div>
    1435                 <div id="search-results" class="query-results" tabindex="0">
    1436                     <ul></ul>
    1437                     <div class="river-waiting">
    1438                         <span class="spinner"></span>
    1439                     </div>
    1440                 </div>
    1441                 <div id="most-recent-results" class="query-results" tabindex="0">
    1442                     <div class="query-notice" id="query-notice-message">
    1443                         <em class="query-notice-default"><?php _e( 'No search term specified. Showing recent items.' ); ?></em>
    1444                         <em class="query-notice-hint screen-reader-text"><?php _e( 'Search or use up and down arrow keys to select an item.' ); ?></em>
    1445                     </div>
    1446                     <ul></ul>
    1447                     <div class="river-waiting">
    1448                         <span class="spinner"></span>
    1449                     </div>
    14501426                </div>
    14511427            </div>
  • trunk/src/wp-includes/css/editor.css

    r36528 r36677  
    8585
    8686.mce-textbox,
    87 .mce-checkbox i.mce-i-checkbox,
    88 #wp-link .query-results {
     87.mce-checkbox i.mce-i-checkbox {
    8988    border: 1px solid #ddd;
    9089    -webkit-border-radius: 0;
     
    13351334}
    13361335
    1337 #wp-link-wrap.search-panel-visible {
    1338     height: 500px;
    1339     margin-top: -250px;
    1340 }
    1341 
    13421336#wp-link-wrap .wp-link-text-field {
    13431337    display: none;
     
    14071401#wp-link-wrap.search-panel-visible #link-selector {
    14081402    -webkit-overflow-scrolling: touch;
    1409     padding: 0 16px;
    1410     position: absolute;
    1411     top: 36px;
    1412     left: 0;
    1413     right: 0;
    1414     bottom: 44px;
    14151403}
    14161404
     
    14221410}
    14231411
    1424 #wp-link-search-toggle:after {
    1425     display: inline-block;
    1426     font: normal 20px/1 dashicons;
    1427     vertical-align: top;
    1428     speak: none;
    1429     -webkit-font-smoothing: antialiased;
    1430     -moz-osx-font-smoothing: grayscale;
    1431     content: "\f140";
    1432 }
    1433 
    1434 .search-panel-visible #wp-link-search-toggle:after {
    1435     content: "\f142";
    1436 }
    1437 
    14381412#wp-link input[type="text"] {
    14391413    -webkit-box-sizing: border-box;
     
    14551429}
    14561430
    1457 #wp-link-search-toggle {
    1458     cursor: pointer;
    1459 }
    1460 
    14611431#wp-link label input[type="text"] {
    1462     margin-top: 5px;
     1432    margin: 5px 0 0;
    14631433    width: 70%;
    14641434}
    14651435
    1466 #wp-link #link-options label span,
    1467 #wp-link #search-panel label span.search-label {
     1436#wp-link #link-options label span {
    14681437    display: inline-block;
    14691438    width: 80px;
     
    14751444}
    14761445
    1477 #wp-link .link-search-field {
    1478     float: left;
    1479     width: 250px;
    1480     max-width: 70%;
    1481 }
    1482 
    1483 #wp-link .link-search-wrapper {
    1484     margin: 5px 0 9px;
    1485     display: block;
    1486     overflow: hidden;
    1487 }
    1488 
    1489 #wp-link .link-search-wrapper span {
    1490     float: left;
    1491     margin-top: 4px;
    1492 }
    1493 
    1494 #wp-link .link-search-wrapper .spinner {
    1495     margin-top: 5px;
    1496 }
    1497 
    14981446#wp-link .link-target {
    14991447    padding: 3px 0 0;
     
    15051453#wp-link .link-target label {
    15061454    max-width: 70%;
    1507 }
    1508 
    1509 #wp-link .query-results {
    1510     border: 1px #dfdfdf solid;
    1511     margin: 0;
    1512     background: #fff;
    1513     overflow: auto;
    1514     position: absolute;
    1515     left: 16px;
    1516     right: 16px;
    1517     bottom: 16px;
    1518     top: 172px;
    1519 }
    1520 
    1521 .has-text-field #wp-link .query-results {
    1522     top: 205px;
    15231455}
    15241456
     
    15331465}
    15341466
    1535 #wp-link .query-notice {
    1536     padding: 0;
    1537     border-bottom: 1px solid #dfdfdf;
    1538     background-color: #f7fcfe;
    1539     color: #000;
    1540 }
    1541 
    1542 #wp-link .query-notice .query-notice-default,
    1543 #wp-link .query-notice .query-notice-hint {
    1544     display: block;
    1545     padding: 6px;
    1546     border-left: 4px solid #00a0d2;
    1547 }
    1548 
    1549 #wp-link .unselectable.no-matches-found {
    1550     padding: 0;
    1551     border-bottom: 1px solid #dfdfdf;
    1552     background-color: #fef7f1;
    1553 }
    1554 
    1555 #wp-link .no-matches-found .item-title {
    1556     display: block;
    1557     padding: 6px;
    1558     border-left: 4px solid #d54e21;
    1559 }
    1560 
    1561 #wp-link .query-results em {
    1562     font-style: normal;
    1563 }
    1564 
    15651467#wp-link li:hover {
    15661468    background: #eaf2fa;
     
    16061508    right: 5px;
    16071509    top: 5px;
    1608 }
    1609 
    1610 #wp-link #search-results,
    1611 #wp-link #search-panel {
    1612     display: none;
    1613 }
    1614 
    1615 #wp-link-wrap.search-panel-visible #search-panel {
    1616     display: block;
    1617 }
    1618 
    1619 #wp-link .river-waiting {
    1620     display: none;
    1621     padding: 10px 0;
    16221510}
    16231511
     
    16521540    }
    16531541
    1654     #wp-link-wrap.search-panel-visible .query-results {
    1655         top: 195px;
    1656     }
    1657 
    1658     #wp-link-wrap.search-panel-visible.has-text-field .query-results {
    1659         top: 235px;
    1660     }
    1661 
    16621542    #link-selector {
    16631543        padding: 0 16px 60px;
     
    16881568        transition: none;
    16891569    }
    1690 
    1691     #wp-link-wrap.search-panel-visible {
    1692         height: auto;
    1693         margin-top: 0;
    1694         top: 10px;
    1695         bottom: 10px;
    1696     }
    1697 
    1698     .search-panel-visible #link-selector {
    1699         overflow: auto;
    1700     }
    1701 
    1702     .search-panel-visible #search-panel .query-results {
    1703         position: static;
    1704     }
    17051570}
    17061571
     
    17181583        height: calc(100% - 92px);
    17191584        padding-bottom: 2px;
    1720     }
    1721 
    1722     #search-panel .query-results {
    1723         position: static;
    17241585    }
    17251586}
     
    17451606}
    17461607
    1747 @media screen and ( max-width: 782px ) {
    1748     div.wp-link-preview {
    1749         margin: 8px 0 8px 5px;
    1750         max-width: 70%;
    1751         max-width: -webkit-calc(100% - 86px);
    1752         max-width: calc(100% - 86px);
    1753     }
    1754 }
    1755 
    17561608div.wp-link-input {
    17571609    float: left;
     
    17681620}
    17691621
    1770 @media screen and ( max-width: 400px ) {
     1622.ui-autocomplete.wplink-autocomplete {
     1623    z-index: 100110;
     1624    max-height: 200px;
     1625    overflow-y: auto;
     1626}
     1627
     1628.wplink-autocomplete li {
     1629    clear: both;
     1630    white-space: normal;
     1631}
     1632
     1633@media screen and ( max-width: 782px ) {
     1634    div.wp-link-preview,
    17711635    div.wp-link-input {
    1772         min-width: 0;
    17731636        max-width: 70%;
    1774         max-width: -webkit-calc(100% - 80px);
    1775         max-width: calc(100% - 80px);
     1637        max-width: -webkit-calc(100% - 86px);
     1638        max-width: calc(100% - 86px);
     1639    }
     1640
     1641    div.wp-link-preview {
     1642        margin: 8px 0 8px 5px;
     1643    }
     1644
     1645    div.wp-link-input {
     1646        width: 300px;
    17761647    }
    17771648
     
    17811652        padding: 4px;
    17821653    }
    1783 }
    1784 
    1785 .ui-autocomplete.mce-wp-autocomplete {
    1786     z-index: 100100;
    1787     margin-top: 10px;
    1788     max-height: 200px;
    1789     overflow-y: auto;
    17901654}
    17911655
  • trunk/src/wp-includes/js/tinymce/plugins/wplink/plugin.js

    r36605 r36677  
    5858                '<div id="' + this._id + '" class="wp-link-input">' +
    5959                    '<input type="text" value="" tabindex="-1" placeholder="' + tinymce.translate('Paste URL or type to search') + '" />' +
     60                    '<input type="hidden" value="" />' +
    6061                '</div>'
    6162            );
     
    6364        setURL: function( url ) {
    6465            this.getEl().firstChild.value = url;
     66        },
     67        getURL: function() {
     68            return tinymce.trim( this.getEl().firstChild.value );
     69        },
     70        getLinkText: function() {
     71            return tinymce.trim( this.getEl().firstChild.nextSibling.value );
     72        },
     73        reset: function() {
     74            var urlInput = this.getEl().firstChild;
     75            urlInput.value = '';
     76            urlInput.nextSibling.value = '';
    6577        }
    6678    } );
    6779
    6880    tinymce.PluginManager.add( 'wplink', function( editor ) {
    69         var a;
    7081        var toolbar;
    7182        var editToolbar;
     
    105116                if ( $element.attr( 'href' ) === '_wp_link_placeholder' ) {
    106117                    editor.dom.remove( element, true );
    107                 } else if ( $element.attr( 'data-wp-link-edit' ) ) {
    108                     $element.attr( 'data-wp-link-edit', null );
     118                } else if ( $element.attr( 'data-wplink-edit' ) ) {
     119                    $element.attr( 'data-wplink-edit', null );
    109120                }
    110121            });
     
    113124        function removePlaceholderStrings( content, dataAttr ) {
    114125            if ( dataAttr ) {
    115                 content = content.replace( / data-wp-link-edit="true"/g, '' );
     126                content = content.replace( / data-wplink-edit="true"/g, '' );
    116127            }
    117128
     
    136147                    var inputNode = editToolbar.find( 'toolbar' )[0];
    137148
    138                     inputNode && inputNode.focus( true );
    139                     a = getSelectedLink();
     149                    if ( inputNode && ! tinymce.$( document.body ).hasClass( 'modal-open' ) ) {
     150                        window.setTimeout( function() {
     151                            inputNode.focus( true );
     152                        });
     153                    }
    140154                } );
    141155
    142156                editToolbar.on( 'hide', function() {
    143                     editToolbar.scrolling || editor.execCommand( 'wp_link_cancel' );
     157                    if ( ! editToolbar.scrolling ) {
     158                        editor.execCommand( 'wp_link_cancel' );
     159                    }
    144160                } );
    145161            }
     
    149165            var link = getSelectedLink();
    150166
     167            if ( tinymce.Env.ie && tinymce.Env.ie < 10 ) {
     168                if ( typeof window.wpLink !== 'undefined' ) {
     169                    window.wpLink.open( editor.id );
     170                }
     171
     172                return;
     173            }
     174
    151175            if ( link ) {
    152                 editor.dom.setAttribs( link, { 'data-wp-link-edit': true } );
     176                editor.dom.setAttribs( link, { 'data-wplink-edit': true } );
    153177            } else {
    154178                removePlaceholders();
    155 
    156179                editor.execCommand( 'mceInsertLink', false, { href: '_wp_link_placeholder' } );
    157                 editor.selection.select( editor.$( 'a[href="_wp_link_placeholder"]' )[0] );
    158                 editor.nodeChanged();
     180
     181                if ( tinymce.Env.ie ) {
     182                    editor.windowManager.wplinkBookmark = editor.selection.getBookmark();
     183                }
    159184            }
    160185        } );
     
    165190            }
    166191
    167             var href = tinymce.trim( inputInstance.getEl().firstChild.value );
    168 
    169             if ( href && ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) ) {
    170                 href = 'http://' + href;
    171             }
    172 
    173             if ( ! href ) {
    174                 editor.dom.remove( a, true );
    175                 return;
    176             }
    177 
    178             if ( a ) {
    179                 editor.dom.setAttribs( a, { href: href, 'data-wp-link-edit': null } );
    180             }
    181 
    182             a = false;
    183 
     192            var href, text,
     193                linkNode = getSelectedLink();
     194
     195            if ( linkNode ) {
     196                href = inputInstance.getURL();
     197                text = inputInstance.getLinkText();
     198                editor.focus();
     199
     200                if ( tinymce.isIE ) {
     201                    editor.selection.moveToBookmark( editor.windowManager.wplinkBookmark );
     202                    editor.windowManager.wplinkBookmark = null;
     203                }
     204
     205                if ( ! href ) {
     206                    editor.dom.remove( linkNode, true );
     207                    return;
     208                }
     209
     210                if ( ! /^(?:[a-z]+:|#|\?|\.|\/)/.test( href ) ) {
     211                    href = 'http://' + href;
     212                }
     213
     214                editor.dom.setAttribs( linkNode, { href: href, 'data-wplink-edit': null } );
     215
     216                if ( ! tinymce.trim( linkNode.innerHTML ) ) {
     217                    editor.$( linkNode ).text( text || href );
     218                }
     219            }
     220
     221            inputInstance.reset();
    184222            editor.nodeChanged();
     223        } );
     224
     225        editor.addCommand( 'wp_link_cancel', function() {
     226            inputInstance.reset();
     227            removePlaceholders();
    185228            editor.focus();
    186         } );
    187 
    188         editor.addCommand( 'wp_link_cancel', function() {
    189             removePlaceholders();
    190             a = false;
    191             editor.nodeChanged();
    192             editor.focus();
     229
     230            if ( tinymce.isIE ) {
     231                editor.selection.moveToBookmark( editor.windowManager.wplinkBookmark );
     232                editor.windowManager.wplinkBookmark = null;
     233            }
    193234        } );
    194235
     
    260301            type: 'WPLinkInput',
    261302            onPostRender: function() {
    262                 var input = this.getEl().firstChild;
    263                 var cache;
    264                 var last;
     303                var element = this.getEl(),
     304                    input = element.firstChild,
     305                    $input, cache, last;
    265306
    266307                inputInstance = this;
    267308
    268309                if ( $ && $.ui && $.ui.autocomplete ) {
    269                     $( input )
    270                     .on( 'keydown', function() {
    271                         $( input ).removeAttr( 'aria-activedescendant' );
     310                    $input = $( input );
     311
     312                    $input.on( 'keydown', function() {
     313                        $input.removeAttr( 'aria-activedescendant' );
    272314                    } )
    273315                    .autocomplete( {
     
    295337                        },
    296338                        focus: function( event, ui ) {
    297                             $( input ).attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID );
     339                            $input.attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID );
    298340                        },
    299341                        select: function( event, ui ) {
    300                             $( input ).val( ui.item.permalink );
     342                            $input.val( ui.item.permalink );
     343                            $( element.firstChild.nextSibling ).val( ui.item.title );
    301344                            return false;
    302345                        },
    303346                        open: function() {
    304                             $( input ).attr( 'aria-expanded', 'true' );
     347                            $input.attr( 'aria-expanded', 'true' );
    305348                            editToolbar.blockHide = true;
    306349                        },
    307350                        close: function() {
    308                             $( input ).attr( 'aria-expanded', 'false' );
     351                            $input.attr( 'aria-expanded', 'false' );
    309352                            editToolbar.blockHide = false;
    310353                        },
    311354                        minLength: 2,
    312355                        position: {
    313                             my: 'left top+5'
     356                            my: 'left top+2'
    314357                        }
    315358                    } ).autocomplete( 'instance' )._renderItem = function( ul, item ) {
     
    319362                    };
    320363
    321                     $( input )
    322                     .attr( {
     364                    $input.attr( {
    323365                        'role': 'combobox',
    324366                        'aria-autocomplete': 'list',
    325367                        'aria-expanded': 'false',
    326                         'aria-owns': $( input ).autocomplete( 'widget' ).attr( 'id' )
     368                        'aria-owns': $input.autocomplete( 'widget' ).attr( 'id' )
    327369                    }  )
    328370                    .on( 'focus', function() {
    329                         $( input ).autocomplete( 'search' );
     371                        $input.autocomplete( 'search' );
    330372                    } )
    331373                    .autocomplete( 'widget' )
    332                         .addClass( 'mce-wp-autocomplete' )
     374                        .addClass( 'wplink-autocomplete' )
    333375                        .attr( 'role', 'listbox' );
    334376                }
    335377
    336378                tinymce.$( input ).on( 'keydown', function( event ) {
    337                     event.keyCode === 13 && editor.execCommand( 'wp_link_apply' );
     379                    if ( event.keyCode === 13 ) {
     380                        editor.execCommand( 'wp_link_apply' );
     381                    }
    338382                } );
    339383            }
     
    341385
    342386        editor.on( 'wptoolbar', function( event ) {
    343             var anchor = editor.dom.getParent( event.element, 'a' ),
    344                 $anchor, href, edit;
    345 
    346             if ( anchor ) {
    347                 $anchor = editor.$( anchor );
    348                 href = $anchor.attr( 'href' );
    349                 edit = $anchor.attr( 'data-wp-link-edit' );
     387            var linkNode = editor.dom.getParent( event.element, 'a' ),
     388                $linkNode, href, edit;
     389
     390            if ( tinymce.$( document.body ).hasClass( 'modal-open' ) ) {
     391                return;
     392            }
     393
     394            if ( linkNode ) {
     395                $linkNode = editor.$( linkNode );
     396                href = $linkNode.attr( 'href' );
     397                edit = $linkNode.attr( 'data-wplink-edit' );
    350398
    351399                if ( href === '_wp_link_placeholder' || edit ) {
    352                     inputInstance.setURL( edit ? href : '' );
    353                     event.element = anchor;
     400                    if ( edit && ! inputInstance.getURL() ) {
     401                        inputInstance.setURL( href );
     402                    }
     403
     404                    event.element = linkNode;
    354405                    event.toolbar = editToolbar;
    355                 } else if ( href && ! $anchor.find( 'img' ).length ) {
     406                } else if ( href && ! $linkNode.find( 'img' ).length ) {
    356407                    previewInstance.setURL( href );
    357                     event.element = anchor;
     408                    event.element = linkNode;
    358409                    event.toolbar = toolbar;
    359410                }
     
    379430            onclick: function() {
    380431                if ( typeof window.wpLink !== 'undefined' ) {
    381                     if ( inputInstance.getEl().firstChild.value ) {
    382                         editor.execCommand( 'wp_link_apply' );
    383                     }
    384 
    385                     window.wpLink.open( editor.id );
     432                    var url = inputInstance.getURL() || null,
     433                        text = inputInstance.getLinkText() || null;
     434
     435                    editor.focus();
     436                    window.wpLink.open( editor.id, url, text );
     437                    inputInstance.reset();
    386438                }
    387439            }
  • trunk/src/wp-includes/js/wplink.js

    r36602 r36677  
    1 /* global ajaxurl, tinymce, wpLinkL10n, setUserSetting, wpActiveEditor */
     1/* global tinymce, wpLinkL10n, wpActiveEditor */
    22var wpLink;
    33
    44( function( $ ) {
    5     var editor, searchTimer, River, Query, correctedURL,
     5    var editor, correctedURL,
    66        inputs = {},
    7         rivers = {},
    87        isTouch = ( 'ontouchend' in document );
    98
     
    1312
    1413    wpLink = {
    15         timeToTriggerRiver: 150,
    16         minRiverAJAXDuration: 200,
    17         riverBottomThreshold: 5,
    18         keySensitivity: 100,
    19         lastSearch: '',
    2014        textarea: '',
    2115
     
    3024            inputs.text = $( '#wp-link-text' );
    3125            inputs.url = $( '#wp-link-url' );
    32             inputs.nonce = $( '#_ajax_linking_nonce' );
    3326            inputs.openInNewTab = $( '#wp-link-target' );
    34             inputs.search = $( '#wp-link-search' );
    35 
    36             // Build Rivers
    37             rivers.search = new River( $( '#search-results' ) );
    38             rivers.recent = new River( $( '#most-recent-results' ) );
    39             rivers.elements = inputs.dialog.find( '.query-results' );
    40 
    41             // Get search notice text
    42             inputs.queryNotice = $( '#query-notice-message' );
    43             inputs.queryNoticeTextDefault = inputs.queryNotice.find( '.query-notice-default' );
    44             inputs.queryNoticeTextHint = inputs.queryNotice.find( '.query-notice-hint' );
    45 
    46             // Bind event handlers
    47             inputs.dialog.keydown( wpLink.keydown );
    48             inputs.dialog.keyup( wpLink.keyup );
     27
     28            if ( $.ui && $.ui.autocomplete ) {
     29                wpLink.setAutocomplete();
     30            }
     31
    4932            inputs.submit.click( function( event ) {
    5033                event.preventDefault();
    5134                wpLink.update();
    5235            });
     36
    5337            inputs.close.add( inputs.backdrop ).add( '#wp-link-cancel a' ).click( function( event ) {
    5438                event.preventDefault();
     
    5640            });
    5741
    58             $( '#wp-link-search-toggle' ).on( 'click', wpLink.toggleInternalLinking );
    59 
    60             rivers.elements.on( 'river-select', wpLink.updateFields );
    61 
    62             // Display 'hint' message when search field or 'query-results' box are focused
    63             inputs.search.on( 'focus.wplink', function() {
    64                 inputs.queryNoticeTextDefault.hide();
    65                 inputs.queryNoticeTextHint.removeClass( 'screen-reader-text' ).show();
    66             } ).on( 'blur.wplink', function() {
    67                 inputs.queryNoticeTextDefault.show();
    68                 inputs.queryNoticeTextHint.addClass( 'screen-reader-text' ).hide();
    69             } );
    70 
    71             inputs.search.on( 'keyup input', function() {
    72                 var self = this;
    73 
    74                 window.clearTimeout( searchTimer );
    75                 searchTimer = window.setTimeout( function() {
    76                     wpLink.searchInternalLinks.call( self );
    77                 }, 500 );
    78             });
    79 
    8042            inputs.url.on( 'paste', function() {
    8143                setTimeout( wpLink.correctURL, 0 );
    8244            } );
    83 
    84             inputs.url.on( 'blur', wpLink.correctURL );
     45        },
     46
     47        setAutocomplete: function() {
     48            var $input = inputs.url,
     49                cache, last;
     50
     51            $input.on( 'keydown', function() {
     52                $input.removeAttr( 'aria-activedescendant' );
     53            } ).autocomplete( {
     54                source: function( request, response ) {
     55                    if ( last === request.term ) {
     56                        response( cache );
     57                        return;
     58                    }
     59
     60                    if ( /^https?:/.test( request.term ) || request.term.indexOf( '.' ) !== -1 ) {
     61                        return response();
     62                    }
     63
     64                    $.post( window.ajaxurl, {
     65                        action: 'wp-link-ajax',
     66                        page: 1,
     67                        search: request.term,
     68                        _ajax_linking_nonce: $( '#_ajax_linking_nonce' ).val()
     69                    }, function( data ) {
     70                        cache = data;
     71                        response( data );
     72                    }, 'json' );
     73
     74                    last = request.term;
     75                },
     76                focus: function( event, ui ) {
     77                    $input.attr( 'aria-activedescendant', 'mce-wp-autocomplete-' + ui.item.ID );
     78                },
     79                select: function( event, ui ) {
     80                    $input.val( ui.item.permalink );
     81
     82                    if ( inputs.wrap.hasClass( 'has-text-field' ) && tinymce.trim( inputs.text.val() ) === '' ) {
     83                        inputs.text.val( ui.item.title );
     84                    }
     85
     86                    return false;
     87                },
     88                open: function() {
     89                    $input.attr( 'aria-expanded', 'true' );
     90                },
     91                close: function() {
     92                    $input.attr( 'aria-expanded', 'false' );
     93                },
     94                minLength: 2,
     95                position: {
     96                    my: 'left top+2'
     97                }
     98            } ).autocomplete( 'instance' )._renderItem = function( ul, item ) {
     99                return $( '<li role="option" id="mce-wp-autocomplete-' + item.ID + '">' )
     100                .append( '<span class="item-title">' + item.title + '</span>&nbsp;<span class="item-date alignright">' + item.info + '</span>' )
     101                .appendTo( ul );
     102            };
     103
     104            $input.attr( {
     105                'aria-owns': $input.autocomplete( 'widget' ).attr( 'id' )
     106            }  )
     107            .on( 'focus', function() {
     108                $input.autocomplete( 'search' );
     109            } )
     110            .autocomplete( 'widget' )
     111                .addClass( 'wplink-autocomplete' )
     112                .attr( 'role', 'listbox' );
     113
     114
    85115        },
    86116
     
    95125        },
    96126
    97         open: function( editorId ) {
     127        open: function( editorId, url, text ) {
    98128            var ed,
    99129                $body = $( document.body );
     
    126156                }
    127157
    128                 if ( editor && tinymce.isIE ) {
    129                     editor.windowManager.bookmark = editor.selection.getBookmark();
     158                if ( editor && tinymce.isIE && ! editor.windowManager.wplinkBookmark ) {
     159                    editor.windowManager.wplinkBookmark = editor.selection.getBookmark();
    130160                }
    131161            }
     
    139169            inputs.backdrop.show();
    140170
    141             wpLink.refresh();
     171            wpLink.refresh( url, text );
    142172
    143173            $( document ).trigger( 'wplink-open', inputs.wrap );
     
    148178        },
    149179
    150         refresh: function() {
    151             var text = '';
    152 
    153             // Refresh rivers (clear links, check visibility)
    154             rivers.search.refresh();
    155             rivers.recent.refresh();
     180        refresh: function( url, text ) {
     181            var linkText = '';
    156182
    157183            if ( wpLink.isMCE() ) {
    158                 wpLink.mceRefresh();
     184                wpLink.mceRefresh( url, text );
    159185            } else {
    160186                // For the Text editor the "Link text" field is always shown
     
    165191                if ( document.selection ) {
    166192                    // Old IE
    167                     text = document.selection.createRange().text || '';
     193                    linkText = document.selection.createRange().text || text || '';
    168194                } else if ( typeof this.textarea.selectionStart !== 'undefined' &&
    169195                    ( this.textarea.selectionStart !== this.textarea.selectionEnd ) ) {
    170196                    // W3C
    171                     text = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd ) || '';
     197                    text = this.textarea.value.substring( this.textarea.selectionStart, this.textarea.selectionEnd ) || text || '';
    172198                }
    173199
     
    186212            }
    187213
    188             // Load the most recent results if this is the first time opening the panel.
    189             if ( ! rivers.recent.ul.children().length ) {
    190                 rivers.recent.ajax();
    191             }
    192 
    193214            correctedURL = inputs.url.val().replace( /^http:\/\//, '' );
    194215        },
    195216
    196217        hasSelectedText: function( linkNode ) {
    197             var html = editor.selection.getContent();
     218            var node, nodes, i, html = editor.selection.getContent();
    198219
    199220            // Partial html and not a fully selected anchor element
     
    203224
    204225            if ( linkNode ) {
    205                 var nodes = linkNode.childNodes, i;
     226                nodes = linkNode.childNodes;
    206227
    207228                if ( nodes.length === 0 ) {
     
    210231
    211232                for ( i = nodes.length - 1; i >= 0; i-- ) {
    212                     if ( nodes[i].nodeType != 3 ) {
     233                    node = nodes[i];
     234
     235                    if ( node.nodeType != 3 && ! tinymce.dom.BookmarkManager.isBookmarkNode( node ) ) {
    213236                        return false;
    214237                    }
     
    219242        },
    220243
    221         mceRefresh: function() {
    222             var text, url,
    223                 selectedNode = editor.selection.getNode(),
     244        mceRefresh: function( url, text ) {
     245            var selectedNode = editor.selection.getNode(),
    224246                linkNode = editor.dom.getParent( selectedNode, 'a[href]' ),
    225247                onlyText = this.hasSelectedText( linkNode );
    226248
    227249            if ( linkNode ) {
    228                 text = linkNode.innerText || linkNode.textContent;
    229                 url = editor.dom.getAttrib( linkNode, 'href' );
     250                text = tinymce.trim( linkNode.innerText || linkNode.textContent ) || text;
     251                url = url || editor.dom.getAttrib( linkNode, 'href' );
    230252
    231253                if ( url === '_wp_link_placeholder' ) {
     
    237259                inputs.submit.val( wpLinkL10n.update );
    238260            } else {
    239                 text = editor.selection.getContent({ format: 'text' });
     261                text = editor.selection.getContent({ format: 'text' }) || text;
    240262                this.setDefaultValues();
    241263            }
     
    252274        close: function() {
    253275            var linkNode;
    254            
     276
    255277            $( document.body ).removeClass( 'modal-open' );
    256278
     
    370392
    371393            if ( tinymce.isIE ) {
    372                 editor.selection.moveToBookmark( editor.windowManager.bookmark );
     394                editor.selection.moveToBookmark( editor.windowManager.wplinkBookmark );
     395                editor.windowManager.wplinkBookmark = null;
    373396            }
    374397
     
    406429        },
    407430
    408         updateFields: function( e, li ) {
    409             inputs.url.val( li.children( '.item-permalink' ).val() );
    410         },
    411 
    412431        setDefaultValues: function() {
    413432            var selection,
     
    436455            // Update save prompt.
    437456            inputs.submit.val( wpLinkL10n.save );
    438         },
    439 
    440         searchInternalLinks: function() {
    441             var t = $( this ), waiting,
    442                 search = t.val();
    443 
    444             if ( search.length > 2 ) {
    445                 rivers.recent.hide();
    446                 rivers.search.show();
    447 
    448                 // Don't search if the keypress didn't change the title.
    449                 if ( wpLink.lastSearch == search )
    450                     return;
    451 
    452                 wpLink.lastSearch = search;
    453                 waiting = t.parent().find( '.spinner' ).addClass( 'is-active' );
    454 
    455                 rivers.search.change( search );
    456                 rivers.search.ajax( function() {
    457                     waiting.removeClass( 'is-active' );
    458                 });
    459             } else {
    460                 rivers.search.hide();
    461                 rivers.recent.show();
    462             }
    463         },
    464 
    465         next: function() {
    466             rivers.search.next();
    467             rivers.recent.next();
    468         },
    469 
    470         prev: function() {
    471             rivers.search.prev();
    472             rivers.recent.prev();
    473         },
    474 
    475         keydown: function( event ) {
    476             var fn, id;
    477 
    478             // Escape key.
    479             if ( 27 === event.keyCode ) {
    480                 wpLink.close();
    481                 event.stopImmediatePropagation();
    482             // Tab key.
    483             } else if ( 9 === event.keyCode ) {
    484                 id = event.target.id;
    485 
    486                 // wp-link-submit must always be the last focusable element in the dialog.
    487                 // following focusable elements will be skipped on keyboard navigation.
    488                 if ( id === 'wp-link-submit' && ! event.shiftKey ) {
    489                     inputs.close.focus();
    490                     event.preventDefault();
    491                 } else if ( id === 'wp-link-close' && event.shiftKey ) {
    492                     inputs.submit.focus();
    493                     event.preventDefault();
    494                 }
    495             }
    496 
    497             // Up Arrow and Down Arrow keys.
    498             if ( 38 !== event.keyCode && 40 !== event.keyCode ) {
    499                 return;
    500             }
    501 
    502             if ( document.activeElement &&
    503                 ( document.activeElement.id === 'link-title-field' || document.activeElement.id === 'url-field' ) ) {
    504                 return;
    505             }
    506 
    507             // Up Arrow key.
    508             fn = 38 === event.keyCode ? 'prev' : 'next';
    509             clearInterval( wpLink.keyInterval );
    510             wpLink[ fn ]();
    511             wpLink.keyInterval = setInterval( wpLink[ fn ], wpLink.keySensitivity );
    512             event.preventDefault();
    513         },
    514 
    515         keyup: function( event ) {
    516             // Up Arrow and Down Arrow keys.
    517             if ( 38 === event.keyCode || 40 === event.keyCode ) {
    518                 clearInterval( wpLink.keyInterval );
    519                 event.preventDefault();
    520             }
    521         },
    522 
    523         delayedCallback: function( func, delay ) {
    524             var timeoutTriggered, funcTriggered, funcArgs, funcContext;
    525 
    526             if ( ! delay )
    527                 return func;
    528 
    529             setTimeout( function() {
    530                 if ( funcTriggered )
    531                     return func.apply( funcContext, funcArgs );
    532                 // Otherwise, wait.
    533                 timeoutTriggered = true;
    534             }, delay );
    535 
    536             return function() {
    537                 if ( timeoutTriggered )
    538                     return func.apply( this, arguments );
    539                 // Otherwise, wait.
    540                 funcArgs = arguments;
    541                 funcContext = this;
    542                 funcTriggered = true;
    543             };
    544         },
    545 
    546         toggleInternalLinking: function( event ) {
    547             var visible = inputs.wrap.hasClass( 'search-panel-visible' );
    548 
    549             inputs.wrap.toggleClass( 'search-panel-visible', ! visible );
    550             setUserSetting( 'wplink', visible ? '0' : '1' );
    551             inputs[ ! visible ? 'search' : 'url' ].focus();
    552             event.preventDefault();
    553457        }
    554458    };
    555459
    556     River = function( element, search ) {
    557         var self = this;
    558         this.element = element;
    559         this.ul = element.children( 'ul' );
    560         this.contentHeight = element.children( '#link-selector-height' );
    561         this.waiting = element.find('.river-waiting');
    562 
    563         this.change( search );
    564         this.refresh();
    565 
    566         $( '#wp-link .query-results, #wp-link #link-selector' ).scroll( function() {
    567             self.maybeLoad();
    568         });
    569         element.on( 'click', 'li', function( event ) {
    570             self.select( $( this ), event );
    571         });
    572     };
    573 
    574     $.extend( River.prototype, {
    575         refresh: function() {
    576             this.deselect();
    577             this.visible = this.element.is( ':visible' );
    578         },
    579         show: function() {
    580             if ( ! this.visible ) {
    581                 this.deselect();
    582                 this.element.show();
    583                 this.visible = true;
    584             }
    585         },
    586         hide: function() {
    587             this.element.hide();
    588             this.visible = false;
    589         },
    590         // Selects a list item and triggers the river-select event.
    591         select: function( li, event ) {
    592             var liHeight, elHeight, liTop, elTop;
    593 
    594             if ( li.hasClass( 'unselectable' ) || li == this.selected )
    595                 return;
    596 
    597             this.deselect();
    598             this.selected = li.addClass( 'selected' );
    599             // Make sure the element is visible
    600             liHeight = li.outerHeight();
    601             elHeight = this.element.height();
    602             liTop = li.position().top;
    603             elTop = this.element.scrollTop();
    604 
    605             if ( liTop < 0 ) // Make first visible element
    606                 this.element.scrollTop( elTop + liTop );
    607             else if ( liTop + liHeight > elHeight ) // Make last visible element
    608                 this.element.scrollTop( elTop + liTop - elHeight + liHeight );
    609 
    610             // Trigger the river-select event
    611             this.element.trigger( 'river-select', [ li, event, this ] );
    612         },
    613         deselect: function() {
    614             if ( this.selected )
    615                 this.selected.removeClass( 'selected' );
    616             this.selected = false;
    617         },
    618         prev: function() {
    619             if ( ! this.visible )
    620                 return;
    621 
    622             var to;
    623             if ( this.selected ) {
    624                 to = this.selected.prev( 'li' );
    625                 if ( to.length )
    626                     this.select( to );
    627             }
    628         },
    629         next: function() {
    630             if ( ! this.visible )
    631                 return;
    632 
    633             var to = this.selected ? this.selected.next( 'li' ) : $( 'li:not(.unselectable):first', this.element );
    634             if ( to.length )
    635                 this.select( to );
    636         },
    637         ajax: function( callback ) {
    638             var self = this,
    639                 delay = this.query.page == 1 ? 0 : wpLink.minRiverAJAXDuration,
    640                 response = wpLink.delayedCallback( function( results, params ) {
    641                     self.process( results, params );
    642                     if ( callback )
    643                         callback( results, params );
    644                 }, delay );
    645 
    646             this.query.ajax( response );
    647         },
    648         change: function( search ) {
    649             if ( this.query && this._search == search )
    650                 return;
    651 
    652             this._search = search;
    653             this.query = new Query( search );
    654             this.element.scrollTop( 0 );
    655         },
    656         process: function( results, params ) {
    657             var list = '', alt = true, classes = '',
    658                 firstPage = params.page == 1;
    659 
    660             if ( ! results ) {
    661                 if ( firstPage ) {
    662                     list += '<li class="unselectable no-matches-found"><span class="item-title"><em>' +
    663                         wpLinkL10n.noMatchesFound + '</em></span></li>';
    664                 }
    665             } else {
    666                 $.each( results, function() {
    667                     classes = alt ? 'alternate' : '';
    668                     classes += this.title ? '' : ' no-title';
    669                     list += classes ? '<li class="' + classes + '">' : '<li>';
    670                     list += '<input type="hidden" class="item-permalink" value="' + this.permalink + '" />';
    671                     list += '<span class="item-title">';
    672                     list += this.title ? this.title : wpLinkL10n.noTitle;
    673                     list += '</span><span class="item-info">' + this.info + '</span></li>';
    674                     alt = ! alt;
    675                 });
    676             }
    677 
    678             this.ul[ firstPage ? 'html' : 'append' ]( list );
    679         },
    680         maybeLoad: function() {
    681             var self = this,
    682                 el = this.element,
    683                 bottom = el.scrollTop() + el.height();
    684 
    685             if ( ! this.query.ready() || bottom < this.contentHeight.height() - wpLink.riverBottomThreshold )
    686                 return;
    687 
    688             setTimeout(function() {
    689                 var newTop = el.scrollTop(),
    690                     newBottom = newTop + el.height();
    691 
    692                 if ( ! self.query.ready() || newBottom < self.contentHeight.height() - wpLink.riverBottomThreshold )
    693                     return;
    694 
    695                 self.waiting.addClass( 'is-active' );
    696                 el.scrollTop( newTop + self.waiting.outerHeight() );
    697 
    698                 self.ajax( function() {
    699                     self.waiting.removeClass( 'is-active' );
    700                 });
    701             }, wpLink.timeToTriggerRiver );
    702         }
    703     });
    704 
    705     Query = function( search ) {
    706         this.page = 1;
    707         this.allLoaded = false;
    708         this.querying = false;
    709         this.search = search;
    710     };
    711 
    712     $.extend( Query.prototype, {
    713         ready: function() {
    714             return ! ( this.querying || this.allLoaded );
    715         },
    716         ajax: function( callback ) {
    717             var self = this,
    718                 query = {
    719                     action : 'wp-link-ajax',
    720                     page : this.page,
    721                     '_ajax_linking_nonce' : inputs.nonce.val()
    722                 };
    723 
    724             if ( this.search )
    725                 query.search = this.search;
    726 
    727             this.querying = true;
    728 
    729             $.post( ajaxurl, query, function( r ) {
    730                 self.page++;
    731                 self.querying = false;
    732                 self.allLoaded = ! r;
    733                 callback( r, query );
    734             }, 'json' );
    735         }
    736     });
    737 
    738460    $( document ).ready( wpLink.init );
    739461})( jQuery );
Note: See TracChangeset for help on using the changeset viewer.